// Source: (http://www.codeproject.com/KB/scripting/jvk.aspx) // Parts of the following code are taken from the DocumentSelection // library (http://debugger.ru/projects/browserextensions/documentselection) // by Ilya Lebedev. DocumentSelection is distributed under LGPL license // (http://www.gnu.org/licenses/lgpl.html). var opened = false, vkb = null; function keyb_change() { //document.getElementById("switch").innerHTML = (opened ? "Show keyboard" : "Hide keyboard"); opened = !opened; if(opened && !vkb) init(); else vkb.Show(opened); } // 'source' is the field which is currently focused: var source = null, insertionS = 0, insertionE = 0; var userstr = navigator.userAgent.toLowerCase(); var safari = (userstr.indexOf('applewebkit') != -1); var gecko = (userstr.indexOf('gecko') != -1) && !safari; var standr = gecko || window.opera || safari; function search_for_text_field(num) { var tg = document.getElementsByTagName("INPUT"); if(tg && tg[num]) return tg[num]; else { var tg2 = document.getElementsByTagName("TEXTAREA"); if(tg2 && tg2[tg.length - num]) return tg2[tg.length - num]; } return null; } // This function retrieves the source element // for the given event object: function get_event_source(e) { var event = e || window.event; return event.srcElement || event.target; } // This function binds 'handler' function to the // 'eventType' event of the 'elem' element: function setup_event(elem, eventType, handler) { return (elem.attachEvent) ? elem.attachEvent("on" + eventType, handler) : ((elem.addEventListener) ? elem.addEventListener(eventType, handler, false) : false); } // By focusing the INPUT field we set the 'source' // to the newly focused field: function focus_keyboard(e) { source = get_event_source(e); } // This function slightly differs from one with the same name // in '4-test-fly' sample. Now it accepts not the id, but the // number (index in the INPUT elements array) of the INPUT field. function register_input_field(num) { var tg = document.getElementsByTagName("INPUT"); if(tg && tg[num]) setup_event(tg[num], "focus", focus_keyboard); } // The same for TEXTAREA fields: function register_textarea_field(num) { var tg = document.getElementsByTagName("TEXTAREA"); if(tg && tg[num]) setup_event(tg[num], "focus", focus_keyboard); } // This function enumerates and "registers" all INPUT fields // on the page: function register_text_fields() { var tg = document.getElementsByTagName("INPUT"); if(tg) { for(var i = 0; i < tg.length; i++) register_input_field(i); } tg = document.getElementsByTagName("TEXTAREA"); if(tg) { for(var i = 0; i < tg.length; i++) register_textarea_field(i); } } function init() { // Note: all parameters, starting with 3rd, in the following // expression are equal to the default parameters for the // VKeyboard object. The only exception is 18th parameter // (flash switch), which is false by default. vkb = new VKeyboard("keyboard", // container's id keyb_callback, // reference to the callback function false, // create the arrow keys or not? (this and the following params are optional) false, // create up and down arrow keys? false, // reserved false, // create the numpad or not? "", // font name ("" == system default) "14px", // font size in px "#000", // font color "#F00", // font color for the dead keys "#FFF", // keyboard base background color "#FFF", // keys' background color "#DDD", // background color of switched/selected item "#777", // border color "#CCC", // border/font color of "inactive" key (key with no value/disabled) "#FFF", // background color of "inactive" key (key with no value/disabled) "#F77", // border color of the language selector's cell true, // show key flash on click? (false by default) "#CC3300", // font color during flash "#FF9966", // key background color during flash "#CC3300", // key border color during flash false, // embed VKeyboard into the page? true, // use 1-pixel gap between the keys? 0); // index(0-based) of the initial layout // The very 1st (index == 0) field is "focused" by default: source = search_for_text_field(0); // Any INPUTs? Register them all! if(source) register_text_fields(); source.focus(); } // Advanced callback function: // function keyb_callback(ch) { var val = source.value; switch(ch) { case "BackSpace": if(val.length) { var span = null; if(document.selection) span = document.selection.createRange().duplicate(); if(span && span.text.length > 0) { span.text = ""; getCaretPositions(source); } else deleteAtCaret(source); } break; case "<": if(insertionS > 0) setRange(source, insertionS - 1, insertionE - 1); break; case ">": if(insertionE < val.length) setRange(source, insertionS + 1, insertionE + 1); break; case "/\\": if(!standr) break; var prev = val.lastIndexOf("\n", insertionS) + 1; var pprev = val.lastIndexOf("\n", prev - 2); var next = val.indexOf("\n", insertionS); if(next == -1) next = val.length; var nnext = next - insertionS; if(prev > next) { prev = val.lastIndexOf("\n", insertionS - 1) + 1; pprev = val.lastIndexOf("\n", prev - 2); } // number of chars in current line to the left of the caret: var left = insertionS - prev; // length of the prev. line: var plen = prev - pprev - 1; // number of chars in the prev. line to the right of the caret: var right = (plen <= left) ? 1 : (plen - left); var change = left + right; setRange(source, insertionS - change, insertionE - change); break; case "\\/": if(!standr) break; var prev = val.lastIndexOf("\n", insertionS) + 1; var next = val.indexOf("\n", insertionS); var pnext = val.indexOf("\n", next + 1); if( next == -1) next = val.length; if(pnext == -1) pnext = val.length; if(pnext < next) pnext = next; if(prev > next) prev = val.lastIndexOf("\n", insertionS - 1) + 1; // number of chars in current line to the left of the caret: var left = insertionS - prev; // length of the next line: var nlen = pnext - next; // number of chars in the next line to the left of the caret: var right = (nlen <= left) ? 0 : (nlen - left - 1); var change = (next - insertionS) + nlen - right; setRange(source, insertionS + change, insertionE + change); break; default: insertAtCaret(source, (ch == "Enter" ? (window.opera ? '\r\n' : '\n') : ch)); } } // This function retrieves the position (in chars, relative to // the start of the text) of the edit cursor (caret), or, if // text is selected in the TEXTAREA, the start and end positions // of the selection. // function getCaretPositions(ctrl) { var CaretPosS = -1, CaretPosE = 0; // Mozilla way: if(ctrl.selectionStart || (ctrl.selectionStart == '0')) { CaretPosS = ctrl.selectionStart; CaretPosE = ctrl.selectionEnd; insertionS = CaretPosS == -1 ? CaretPosE : CaretPosS; insertionE = CaretPosE; } // IE way: else if(document.selection && ctrl.createTextRange) { var start = end = 0; try { start = Math.abs(document.selection.createRange().moveStart("character", -10000000)); // start if (start > 0) { try { var endReal = Math.abs(ctrl.createTextRange().moveEnd("character", -10000000)); var r = document.body.createTextRange(); r.moveToElementText(ctrl); var sTest = Math.abs(r.moveStart("character", -10000000)); var eTest = Math.abs(r.moveEnd("character", -10000000)); if ((ctrl.tagName.toLowerCase() != 'input') && (eTest - endReal == sTest)) start -= sTest; } catch(err) {} } } catch (e) {} try { end = Math.abs(document.selection.createRange().moveEnd("character", -10000000)); // end if(end > 0) { try { var endReal = Math.abs(ctrl.createTextRange().moveEnd("character", -10000000)); var r = document.body.createTextRange(); r.moveToElementText(ctrl); var sTest = Math.abs(r.moveStart("character", -10000000)); var eTest = Math.abs(r.moveEnd("character", -10000000)); if ((ctrl.tagName.toLowerCase() != 'input') && (eTest - endReal == sTest)) end -= sTest; } catch(err) {} } } catch (e) {} insertionS = start; insertionE = end } } function setRange(ctrl, start, end) { if(ctrl.setSelectionRange) // Standard way (Mozilla, Opera, Safari ...) { ctrl.setSelectionRange(start, end); } else // MS IE { var range; try { range = ctrl.createTextRange(); } catch(e) { try { range = document.body.createTextRange(); range.moveToElementText(ctrl); } catch(e) { range = null; } } if(!range) return; range.collapse(true); range.moveStart("character", start); range.moveEnd("character", end - start); range.select(); } insertionS = start; insertionE = end; } function deleteSelection(ctrl) { if(insertionS == insertionE) return; var tmp = (document.selection && !window.opera) ? ctrl.value.replace(/\r/g,"") : ctrl.value; ctrl.value = tmp.substring(0, insertionS) + tmp.substring(insertionE, tmp.length); setRange(ctrl, insertionS, insertionS); } function deleteAtCaret(ctrl) { // if(insertionE < insertionS) insertionE = insertionS; if(insertionS != insertionE) { deleteSelection(ctrl); return; } if(insertionS == insertionE) insertionS = insertionS - 1; var tmp = (document.selection && !window.opera) ? ctrl.value.replace(/\r/g,"") : ctrl.value; ctrl.value = tmp.substring(0, insertionS) + tmp.substring(insertionE, tmp.length); setRange(ctrl, insertionS, insertionS); } // This function inserts text at the caret position: // function insertAtCaret(ctrl, val) { if(insertionS != insertionE) deleteSelection(ctrl); if(gecko && document.createEvent && !window.opera) { var e = document.createEvent("KeyboardEvent"); if(e.initKeyEvent && ctrl.dispatchEvent) { e.initKeyEvent("keypress", // in DOMString typeArg, false, // in boolean canBubbleArg, true, // in boolean cancelableArg, null, // in nsIDOMAbstractView viewArg, specifies UIEvent.view. This value may be null; false, // in boolean ctrlKeyArg, false, // in boolean altKeyArg, false, // in boolean shiftKeyArg, false, // in boolean metaKeyArg, null, // key code; val.charCodeAt(0));// char code. ctrl.dispatchEvent(e); } } else { var tmp = (document.selection && !window.opera) ? ctrl.value.replace(/\r/g,"") : ctrl.value; ctrl.value = tmp.substring(0, insertionS) + val + tmp.substring(insertionS, tmp.length); } setRange(ctrl, insertionS + val.length, insertionS + val.length); }