/* ******************************************************************** ********************************************************************** * HTML Virtual Keyboard Interface Script - v1.22 * Copyright (c) 2009 - GreyWyvern * * - Licenced for free distribution under the BSDL * http://www.opensource.org/licenses/bsd-license.php * * Add a script-driven keyboard interface to text fields, password * fields and textareas. * * See http://www.greywyvern.com/code/javascript/keyboard for examples * and usage instructions. * * Version 1.22 - March 30, 2009 * - Added support for max-length on inputs (Olivier D??sormeaux) * - Downgrade comma-below forms of S and T to cedilla for IE < 8 * - Change exposed attachment function name to VKI_attach * - Exposed keyboard close function as VKI_close * - No longer expose any functions in the userscript version * * See full changelog at: * http://www.greywyvern.com/code/javascript/keyboard.changelog.txt * * Keyboard Credits * - Swedish keyboard layout by H?kan Sandberg * - Romanian keyboard layout by Aurel * - Farsi (Persian) keyboard layout by Kaveh Bakhtiyari (www.bakhtiyari.com) * - Burmese keyboard layout by Cetanapa * - Slovenian keyboard layout by Miran Zeljko * - Hungarian keyboard layout by Antal Sall 'Hiromacu' * - Arabic keyboard layout by Srinivas Reddy * - Italian and Spanish (Spain) keyboard layouts by dictionarist.com * - Lithuanian and Russian keyboard layouts by Ramunas * - German keyboard layout by QuHno * - French keyboard layout by Hidden Evil * - Polish Programmers layout by moose * - Turkish keyboard layouts by offcu * - Dutch and US Int'l keyboard layouts by jerone * - Portuguese keyboard layout by clisboa * */ var VKI_attach, VKI_close; function VKI_buildKeyboardInputs() { var self = this; this.VKI_version = "1.22"; this.VKI_target = this.VKI_visible = false; this.VKI_shift = this.VKI_capslock = this.VKI_alternate = this.VKI_dead = false; this.VKI_deadkeysOn = false; this.VKI_kt = "china"; // Default keyboard layout this.VKI_clearPasswords = false; // Clear password fields on focus this.VKI_showVersion = false; this.VKI_clickless = false; this.VKI_clicklessDelay = 500; this.VKI_keyCenter = 3; this.VKI_isIE = /*@cc_on!@*/false; this.VKI_isIE6 = /*@if(@_jscript_version == 5.6)!@end@*/false; this.VKI_isIElt8 = /*@if(@_jscript_version < 5.8)!@end@*/false; this.VKI_isMoz = (navigator.product == "Gecko"); this.VKI_isWebKit = RegExp("KHTML").test(navigator.userAgent); /* ***** Create keyboards ************************************** */ this.VKI_layout = {}; this.VKI_layoutDDK = {}; // - Lay out each keyboard in rows of sub-arrays. Each sub-array // represents one key. // // - Each sub-array consists of four slots described as follows: // example: ["a", "A", "\u00e1", "\u00c1"] // // a) Normal character // A) Character + Shift or Caps // \u00e1) Character + Alt or AltGr // \u00c1) Character + Shift or Caps + Alt or AltGr // // You may include sub-arrays which are fewer than four slots. // In these cases, the missing slots will be blanked when the // corresponding modifier key (Shift or AltGr) is pressed. // // - If the second slot of a sub-array matches one of the following // strings: // "Tab", "Caps", "Shift", "Enter", "后退", "Alt" OR "AltGr" // then the function of the key will be the following, // respectively: // - Insert a tab // - Toggle Caps Lock (technically a Shift Lock) // - Next entered character will be the shifted character // - Insert a newline (textarea), or close the keyboard // - Delete the previous character // - Next entered character will be the alternate character // // The first slot of this sub-array will be the text to display // on the corresponding key. This allows for easy localisation // of key names. // // - Layout dead keys (diacritic + letter) should be added as // arrays of two item arrays with hash keys equal to the // diacritic. See the "this.VKI_deadkey" object below the layout // definitions. In each two item child array, the second item // is what the diacritic would change the first item to. // // - To disable dead keys for a layout, simply assign true to the // this.VKI_layoutDDK (DDK = disable dead keys) object of the // same name as the layout. See the Numpad layout below for an // example. // // - Note that any characters beyond the normal ASCII set should be // entered in escaped Unicode format. (eg \u00a3 = Pound symbol) // You can find Unicode values for characters here: // http://unicode.org/charts/ // // - To remove a keyboard, just delete it, or comment it out of the // source code this.VKI_layout["china"] = [ // US Standard Keyboard [["`", "~"], ["1", "!"], ["2", "@"], ["3", "#"], ["4", "$"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["后退", "后退"]], [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["[", "{"], ["]", "}"], ["\\", "|"]], [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], [";", ":"], ["'", '"'], ["Enter", "Enter"]], [["Shift", "Shift"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]] ]; /* ***** Define Dead Keys ************************************** */ this.VKI_deadkey = {}; // - Lay out each dead key set in one row of sub-arrays. The rows // below are wrapped so uppercase letters are below their // lowercase equivalents. // // - The first letter in each sub-array is the letter pressed after // the diacritic. The second letter is the letter this key-combo // will generate. // // - Note that if you have created a new keyboard layout and want // it included in the distributed script, PLEASE TELL ME if you // have added additional dead keys to the ones below. /* **************************************************************** * Attach the keyboard to an element * */ this.VKI_attachKeyboard = VKI_attach = function(elem) { if (elem.VKI_attached) return false; var keybut = document.createElement('img'); keybut.src = "../images/keyboard.png"; keybut.alt = "软键盘"; keybut.className = "keyboardInputInitiator"; keybut.title = "软键盘"; keybut.elem = elem; keybut.onclick = function() { self.VKI_show(this.elem); }; elem.VKI_attached = true; elem.parentNode.insertBefore(keybut, elem.nextSibling); if (this.VKI_isIE) { elem.onclick = elem.onselect = elem.onkeyup = function(e) { if ((e || event).type != "keyup" || !this.readOnly) this.range = document.selection.createRange(); }; } }; /* ***** Find tagged input & textarea elements ***************** */ var inputElems = [ document.getElementsByTagName('input'), document.getElementsByTagName('textarea') ]; for (var x = 0, elem; elem = inputElems[x++];) for (var y = 0, ex; ex = elem[y++];) if ((ex.nodeName == "TEXTAREA" || ex.type == "text" || ex.type == "password") && ex.className.indexOf ("keyboardInput") > -1) this.VKI_attachKeyboard(ex); /* ***** Build the keyboard interface ************************** */ this.VKI_keyboard = document.createElement('table'); this.VKI_keyboard.id = "keyboardInputMaster"; this.VKI_keyboard.dir = "ltr"; this.VKI_keyboard.cellSpacing = this.VKI_keyboard.border = "0"; var thead = document.createElement('thead'); var tr = document.createElement('tr'); var th = document.createElement('th'); /*var kblist = document.createElement('select'); for (ktype in this.VKI_layout) { if (typeof this.VKI_layout[ktype] == "object") { var opt = document.createElement('option'); opt.value = ktype; opt.appendChild(document.createTextNode(ktype)); kblist.appendChild(opt); } } if (kblist.options.length) { kblist.value = this.VKI_kt; kblist.onchange = function() { self.VKI_kt = this.value; self.VKI_buildKeys(); self.VKI_position(); }; th.appendChild(kblist); } var label = document.createElement('label'); var checkbox = document.createElement('input'); checkbox.type = "hidden"; checkbox.title = "Dead keys: " + ((this.VKI_deadkeysOn) ? "On" : "Off"); checkbox.defaultChecked = this.VKI_deadkeysOn; checkbox.onclick = function() { self.VKI_deadkeysOn = this.checked; this.title = "Dead keys: " + ((this.checked) ? "On" : "Off"); self.VKI_modify(""); return true; }; label.appendChild(this.VKI_deadkeysElem = checkbox); checkbox.checked = this.VKI_deadkeysOn; th.appendChild(label);*/ tr.appendChild(th); var td = document.createElement('td'); var clearer = document.createElement('span'); clearer.classname = "table_title"; clearer.id = "keyboardInputClear"; clearer.appendChild(document.createTextNode("清空")); clearer.title = "清空输入框"; clearer.onmousedown = function() { this.className = "pressed"; }; clearer.onmouseup = function() { this.className = ""; }; clearer.onclick = function() { self.VKI_target.value = ""; self.VKI_target.focus(); return false; }; td.appendChild(clearer); var closer = document.createElement('span'); closer.id = "keyboardInputClose"; closer.appendChild(document.createTextNode('关闭')); closer.title = "关闭"; closer.onmousedown = function() { this.className = "pressed"; }; closer.onmouseup = function() { this.className = ""; }; closer.onclick = function() { self.VKI_close(); }; td.appendChild(closer); tr.appendChild(td); thead.appendChild(tr); this.VKI_keyboard.appendChild(thead); var tbody = document.createElement('tbody'); var tr = document.createElement('tr'); var td = document.createElement('td'); td.colSpan = "2"; var div = document.createElement('div'); div.id = "keyboardInputLayout"; td.appendChild(div); if (this.VKI_showVersion) { var div = document.createElement('div'); var ver = document.createElement('var'); ver.appendChild(document.createTextNode("v" + this.VKI_version)); div.appendChild(ver); td.appendChild(div); } tr.appendChild(td); tbody.appendChild(tr); this.VKI_keyboard.appendChild(tbody); if (this.VKI_isIE6) { this.VKI_iframe = document.createElement('iframe'); this.VKI_iframe.style.position = "absolute"; this.VKI_iframe.style.border = "0px none"; this.VKI_iframe.style.filter = "mask()"; this.VKI_iframe.style.zIndex = "999999"; } /* **************************************************************** * Build or rebuild the keyboard keys * */ this.VKI_buildKeys = function() { this.VKI_shift = this.VKI_capslock = this.VKI_alternate = this.VKI_dead = false; this.VKI_deadkeysOn = false; var container = this.VKI_keyboard.tBodies[0].getElementsByTagName('div')[0]; while (container.firstChild) container.removeChild(container.firstChild); for (var x = 0, hasDeadKey = false, lyt; lyt = this.VKI_layout[this.VKI_kt][x++];) { var table = document.createElement('table'); table.cellSpacing = table.border = "0"; if (lyt.length <= this.VKI_keyCenter) table.className = "keyboardInputCenter"; var tbody = document.createElement('tbody'); var tr = document.createElement('tr'); for (var y = 0, lkey; lkey = lyt[y++];) { var td = document.createElement('td'); td.appendChild(document.createTextNode(lkey[0])); var className = []; if (this.VKI_deadkeysOn) for (key in this.VKI_deadkey) if (key === lkey[0]) { className.push("alive"); break; } if (lyt.length > this.VKI_keyCenter && y == lyt.length) className.push("last"); if (lkey[0] == " ") className.push("space"); td.className = className.join(" "); td.VKI_clickless = 0; if (!td.click) { td.click = function() { var evt = this.ownerDocument.createEvent('MouseEvents'); evt.initMouseEvent('click', true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null); this.dispatchEvent(evt); }; } td.onmouseover = function() { if (self.VKI_clickless) { var _self = this; clearTimeout(this.VKI_clickless); this.VKI_clickless = setTimeout(function() { _self.click(); }, self.VKI_clicklessDelay); } if (this.firstChild.nodeValue != "\xa0") this.className += " hover"; }; td.onmouseout = function() { if (self.VKI_clickless) clearTimeout(this.VKI_clickless); this.className = this.className.replace(/ ?(hover|pressed)/g, ""); }; td.onmousedown = function() { if (self.VKI_clickless) clearTimeout(this.VKI_clickless); if (this.firstChild.nodeValue != "\xa0") this.className += " pressed"; }; td.onmouseup = function() { if (self.VKI_clickless) clearTimeout(this.VKI_clickless); this.className = this.className.replace(/ ?pressed/g, ""); }; td.ondblclick = function() { return false; }; switch (lkey[1]) { case "Caps": case "Shift": case "Alt": case "AltGr": td.onclick = (function(type) { return function() { self.VKI_modify(type); return false; }; })(lkey[1]); break; case "Tab": td.onclick = function() { self.VKI_insert("\t"); return false; }; break; case "后退": td.onclick = function() { self.VKI_target.focus(); if (self.VKI_target.setSelectionRange) { if (self.VKI_target.readOnly && self.VKI_isWebKit) { var rng = [self.VKI_target.selStart || 0, self.VKI_target.selEnd || 0]; } else var rng = [self.VKI_target.selectionStart, self.VKI_target.selectionEnd]; if (rng[0] < rng[1]) rng[0]++; self.VKI_target.value = self.VKI_target.value.substr(0, rng[0] - 1) + self.VKI_target.value.substr (rng[1]); self.VKI_target.setSelectionRange(rng[0] - 1, rng[0] - 1); if (self.VKI_target.readOnly && self.VKI_isWebKit) { var range = window.getSelection().getRangeAt(0); self.VKI_target.selStart = range.startOffset; self.VKI_target.selEnd = range.endOffset; } } else if (self.VKI_target.createTextRange) { try { self.VKI_target.range.select(); } catch(e) { self.VKI_target.range = document.selection.createRange(); } if (!self.VKI_target.range.text.length) self.VKI_target.range.moveStart('character', -1); self.VKI_target.range.text = ""; } else self.VKI_target.value = self.VKI_target.value.substr(0, self.VKI_target.value.length - 1); if (self.VKI_shift) self.VKI_modify("Shift"); if (self.VKI_alternate) self.VKI_modify("AltGr"); self.VKI_target.focus(); return true; }; break; case "Enter": td.onclick = function() { if (self.VKI_target.nodeName != "TEXTAREA") { self.VKI_close(); this.className = this.className.replace(/ ?(hover|pressed)/g, ""); } else self.VKI_insert("\n"); return true; }; break; default: td.onclick = function() { if (self.VKI_deadkeysOn && self.VKI_dead) { if (self.VKI_dead != this.firstChild.nodeValue) { for (key in self.VKI_deadkey) { if (key == self.VKI_dead) { if (this.firstChild.nodeValue != " ") { for (var z = 0, rezzed = false, dk; dk = self.VKI_deadkey[key][z++];) { if (dk[0] == this.firstChild.nodeValue) { self.VKI_insert(dk[1]); rezzed = true; break; } } } else { self.VKI_insert(self.VKI_dead); rezzed = true; } break; } } } else rezzed = true; } self.VKI_dead = false; if (!rezzed && this.firstChild.nodeValue != "\xa0") { if (self.VKI_deadkeysOn) { for (key in self.VKI_deadkey) { if (key == this.firstChild.nodeValue) { self.VKI_dead = key; this.className += " dead"; if (self.VKI_shift) self.VKI_modify("Shift"); if (self.VKI_alternate) self.VKI_modify("AltGr"); break; } } if (!self.VKI_dead) self.VKI_insert(this.firstChild.nodeValue); } else self.VKI_insert(this.firstChild.nodeValue); } self.VKI_modify(""); return false; }; } tr.appendChild(td); tbody.appendChild(tr); table.appendChild(tbody); for (var z = 0; z < 4; z++) if (this.VKI_deadkey[lkey[z] = lkey[z] || "\xa0"]) hasDeadKey = true; } container.appendChild(table); } //this.VKI_deadkeysElem.style.display = (!this.VKI_layoutDDK[this.VKI_kt] && hasDeadKey) ? "inline" : "none"; }; this.VKI_buildKeys(); VKI_disableSelection(this.VKI_keyboard); /* **************************************************************** * Controls modifier keys * */ this.VKI_modify = function(type) { switch (type) { case "Alt": case "AltGr": this.VKI_alternate = !this.VKI_alternate; break; case "Caps": this.VKI_capslock = !this.VKI_capslock; break; case "Shift": this.VKI_shift = !this.VKI_shift; break; } var vchar = 0; if (!this.VKI_shift != !this.VKI_capslock) vchar += 1; var tables = this.VKI_keyboard.getElementsByTagName('table'); for (var x = 0; x < tables.length; x++) { var tds = tables[x].getElementsByTagName('td'); for (var y = 0; y < tds.length; y++) { var className = []; var lkey = this.VKI_layout[this.VKI_kt][x][y]; if (tds[y].className.indexOf('hover') > -1) className.push("hover"); switch (lkey[1]) { case "Alt": case "AltGr": if (this.VKI_alternate) className.push("dead"); break; case "Shift": if (this.VKI_shift) className.push("dead"); break; case "Caps": if (this.VKI_capslock) className.push("dead"); break; case "Tab": case "Enter": case "后退": break; default: if (type) tds[y].firstChild.nodeValue = lkey[vchar + ((this.VKI_alternate && lkey.length == 4) ? 2 : 0)]; if (this.VKI_deadkeysOn) { var char = tds[y].firstChild.nodeValue; if (this.VKI_dead) { if (char == this.VKI_dead) className.push("dead"); for (var z = 0; z < this.VKI_deadkey[this.VKI_dead].length; z++) { if (char == this.VKI_deadkey[this.VKI_dead][z][0]) { className.push("target"); break; } } } for (key in this.VKI_deadkey) if (key === char) { className.push("alive"); break; } } } if (y == tds.length - 1 && tds.length > this.VKI_keyCenter) className.push("last"); if (lkey[0] == " ") className.push("space"); tds[y].className = className.join(" "); } } }; /* **************************************************************** * Insert text at the cursor * */ this.VKI_insert = function(text) { this.VKI_target.focus(); if (typeof this.VKI_target.maxLength == "undefined" || this.VKI_target.maxLength < 0 || this.VKI_target.value.length < this.VKI_target.maxLength) { if (this.VKI_target.setSelectionRange) { if (this.VKI_target.readOnly && this.VKI_isWebKit) { var rng = [this.VKI_target.selStart || 0, this.VKI_target.selEnd || 0]; } else var rng = [this.VKI_target.selectionStart, this.VKI_target.selectionEnd]; this.VKI_target.value = this.VKI_target.value.substr(0, rng[0]) + text + this.VKI_target.value.substr(rng[1]); if (text == "\n" && window.opera) rng[0]++; this.VKI_target.setSelectionRange(rng[0] + text.length, rng[0] + text.length); if (this.VKI_target.readOnly && this.VKI_isWebKit) { var range = window.getSelection().getRangeAt(0); this.VKI_target.selStart = range.startOffset; this.VKI_target.selEnd = range.endOffset; } } else if (this.VKI_target.createTextRange) { try { this.VKI_target.range.select(); } catch(e) { this.VKI_target.range = document.selection.createRange(); } this.VKI_target.range.text = text; this.VKI_target.range.collapse(true); this.VKI_target.range.select(); } else this.VKI_target.value += text; if (this.VKI_shift) this.VKI_modify("Shift"); if (this.VKI_alternate) this.VKI_modify("AltGr"); this.VKI_target.focus(); } else if (this.VKI_target.createTextRange) this.VKI_target.range.select(); }; /* **************************************************************** * Show the keyboard interface * */ this.VKI_show = function(elem) { if (this.VKI_target = elem) { if (this.VKI_visible != elem) { if (this.VKI_isIE) { if (!this.VKI_target.range) { this.VKI_target.range = this.VKI_target.createTextRange(); this.VKI_target.range.moveStart('character', this.VKI_target.value.length); } this.VKI_target.range.select(); } try { this.VKI_keyboard.parentNode.removeChild(this.VKI_keyboard); } catch (e) {} if (this.VKI_clearPasswords && this.VKI_target.type == "password") this.VKI_target.value = ""; var elem = this.VKI_target; this.VKI_target.keyboardPosition = "absolute"; do { if (VKI_getStyle(elem, "position") == "fixed") { this.VKI_target.keyboardPosition = "fixed"; break; } } while (elem = elem.offsetParent); if (this.VKI_isIE6) document.body.appendChild(this.VKI_iframe); document.body.appendChild(this.VKI_keyboard); this.VKI_keyboard.style.top = this.VKI_keyboard.style.right = this.VKI_keyboard.style.bottom = this.VKI_keyboard.style.left = "auto"; this.VKI_keyboard.style.position = this.VKI_target.keyboardPosition; this.VKI_visible = this.VKI_target; this.VKI_position(); this.VKI_target.focus(); } else this.VKI_close(); } }; /* **************************************************************** * Position the keyboard * */ this.VKI_position = function() { if (self.VKI_visible) { var inputElemPos = VKI_findPos(self.VKI_target); self.VKI_keyboard.style.top = inputElemPos[1] - ((self.VKI_target.keyboardPosition == "fixed" && !self.VKI_isIE && ! self.VKI_isMoz) ? VKI_scrollDist()[1] : 0) + self.VKI_target.offsetHeight + 3 + "px"; self.VKI_keyboard.style.left = Math.min(VKI_innerDimensions()[0] - self.VKI_keyboard.offsetWidth - 15, inputElemPos [0]) + "px"; if (self.VKI_isIE6) { self.VKI_iframe.style.width = self.VKI_keyboard.offsetWidth + "px"; self.VKI_iframe.style.height = self.VKI_keyboard.offsetHeight + "px"; self.VKI_iframe.style.top = self.VKI_keyboard.style.top; self.VKI_iframe.style.left = self.VKI_keyboard.style.left; } } }; if (window.addEventListener) { window.addEventListener('resize', this.VKI_position, false); } else if (window.attachEvent) window.attachEvent('onresize', this.VKI_position); /* **************************************************************** * Close the keyboard interface * */ this.VKI_close = VKI_close = function() { if (this.VKI_visible) { try { this.VKI_keyboard.parentNode.removeChild(this.VKI_keyboard); if (this.VKI_isIE6) this.VKI_iframe.parentNode.removeChild(this.VKI_iframe); } catch (e) {} this.VKI_target.focus(); this.VKI_target = this.VKI_visible = false; } }; }; function VKI_findPos(obj) { var curleft = curtop = 0; do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); return [curleft, curtop]; } function VKI_innerDimensions() { if (self.innerHeight) { return [self.innerWidth, self.innerHeight]; } else if (document.documentElement && document.documentElement.clientHeight) { return [document.documentElement.clientWidth, document.documentElement.clientHeight]; } else if (document.body) return [document.body.clientWidth, document.body.clientHeight]; return [0, 0]; } function VKI_scrollDist() { var html = document.getElementsByTagName('html')[0]; if (html.scrollTop && document.documentElement.scrollTop) { return [html.scrollLeft, html.scrollTop]; } else if (html.scrollTop || document.documentElement.scrollTop) return [html.scrollLeft + document.documentElement.scrollLeft, html.scrollTop + document.documentElement.scrollTop]; return [0, 0]; } function VKI_getStyle(obj, styleProp) { if (obj.currentStyle) { var y = obj.currentStyle[styleProp]; } else if (window.getComputedStyle) var y = window.getComputedStyle(obj, null)[styleProp]; return y; } function VKI_disableSelection(elem) { elem.onselectstart = function() { return false; }; elem.unselectable = "on"; elem.style.MozUserSelect = "none"; elem.style.cursor = "default"; if (window.opera) elem.onmousedown = function() { return false; }; } /* ***** Attach this script to the onload event ****************** */ if (window.addEventListener) { window.addEventListener('load', VKI_buildKeyboardInputs, false); } else if (window.attachEvent) window.attachEvent('onload', VKI_buildKeyboardInputs);