File "vk.inc.php"
Full Path: /home/analogde/www/WINDOWS/setup/vk.inc.php
File size: 22.56 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Converts numeric code to the UTF-8 symbol
*
* @param $num character number
* @return string resulting char
*/
function code2utf($num){
if ($num < 128) {
return chr($num);
}
if ($num < 2048) {
return chr(($num >> 6) + 192) . chr(($num & 63) + 128);
}
if ($num < 65536) {
return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
}
if ($num < 2097152) {
return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
}
return '';
}
/**
* $Id: vk.inc.php 737 2011-05-03 21:50:55Z wingedfox $
*
* Keyboard layout parset implementation
*
* This software is protected by patent No.2009611147 issued on 20.02.2009 by Russian Federal Service for Intellectual Property Patents and Trademarks.
*
* @author Ilya Lebedev
* @copyright 2006-2009 Ilya Lebedev <ilya@lebedev.net>
* @version $Rev: 737 $
* @lastchange $Author: wingedfox $ $Date: 2011-05-04 01:50:55 +0400 (Wed, 04 May 2011) $
*/
class VirtualKeyboardLayout {
var $SERIALIZE_USE_DOMAIN = "domain";
var $SERIALIZE_USE_CODE = "code";
var $controlCodes = array(0x0600,0x0601,0x0602,0x0603,0x06DD,0x070F,0x17B4,0x17B5,0x200B
,0x200C,0x200D,0x200E,0x200F,0x202A,0x202B,0x202C,0x202D,0x202E,0x2060
,0x2061,0x2062,0x2063,0x206A,0x206B,0x206C,0x206D,0x206E,0x206F);
/**
* Folder name to be used as prefix for building layout requirements
*/
private $addonFolder = "";
/**
* List of the chars to be not skipped in the layout
*/
var $problemChars = array('021e','021f' // from Lakhota Standard, windows does not uppercase them properly
);
/**
* Keys mapping as used in the US keyboard layout
* it might follow in the different order to the mapping described in the KLC file
*/
var $keymap = array (
'29' /* 'OEM_3' */ => array()
,'02' /* 1 */ => array()
,'03' /* 2 */ => array()
,'04' /* 3 */ => array()
,'05' /* 4 */ => array()
,'06' /* 5 */ => array()
,'07' /* 6 */ => array()
,'08' /* 7 */ => array()
,'09' /* 8 */ => array()
,'0a' /* 9 */ => array()
,'0b' /* 0 */ => array()
,'0c' /* 'OEM_MINUS' */ => array()
,'0d' /* 'OEM_PLUS' */ => array()
,'2b' /* 'OEM_5' */ => array()
,'10' /* 'Q' */ => array()
,'11' /* 'W' */ => array()
,'12' /* 'E' */ => array()
,'13' /* 'R' */ => array()
,'14' /* 'T' */ => array()
,'15' /* 'Y' */ => array()
,'16' /* 'U' */ => array()
,'17' /* 'I' */ => array()
,'18' /* 'O' */ => array()
,'19' /* 'P' */ => array()
,'1a' /* 'OEM_4' */ => array()
,'1b' /* 'OEM_6' */ => array()
,'1e' /* 'A' */ => array()
,'1f' /* 'S' */ => array()
,'20' /* 'D' */ => array()
,'21' /* 'F' */ => array()
,'22' /* 'G' */ => array()
,'23' /* 'H' */ => array()
,'24' /* 'J' */ => array()
,'25' /* 'K' */ => array()
,'26' /* 'L' */ => array()
,'27' /* 'OEM_1' */ => array()
,'28' /* 'OEM_7' */ => array()
,'2c' /* 'Z' */ => array()
,'2d' /* 'X' */ => array()
,'2e' /* 'C' */ => array()
,'2f' /* 'V' */ => array()
,'30' /* 'B' */ => array()
,'31' /* 'N' */ => array()
,'32' /* 'M' */ => array()
,'33' /* 'OEM_COMMA' */ => array()
,'34' /* 'OEM_PERIOD' */ => array()
,'35' /* 'OEM_2' */ => array()
);
/**
* Maps column number to the mnemonic
*/
var $colmap = array(
0 => 'normal'
,1 => 'shift'
,2 => 'ctrl'
,3 => 'shift_ctrl'
,6 => 'alt'
,7 => 'shift_alt'
,8 => 'caps'
,9 => 'shift_caps'
);
/**
* Layout name
*/
var $name;
/**
* Country code (might be not a _code_ but any string)
*/
var $code;
/**
* Language domain, as specified in the ISO-639*
*/
var $domain;
/**
* Just a copyright string
*/
var $copyright;
/**
* Developer company
*/
var $company;
/**
* Enabled columns, regarding to the KLC enablement info
*/
var $columns = array();
/**
* List of the ligatures in the format
* '<key_name>' => array ('<column>' => array('<symbol1>','<symbol4>')));
* where <key_name> is the same as key in the {@link #keymap}
* <column> column number
* <symbolN> up to 4 consecutive symbols in the ligature
*/
var $ligature = array();
/**
* Deadkeys
*/
var $deadkey = array();
var $root = "";
var $addon = "/addons/";
var $callback = "callbacks/";
var $fname = "";
function VirtualKeyboardLayout($fname) {
$this->convertControlCodes();
$this->problemChars = array_map(create_function('$a','return code2utf(hexdec($a));'), $this->problemChars);
$this->root = dirname($fname);
$this->fname = $fname;
mb_internal_encoding("UTF-8");
$this->layoutText = mb_convert_encoding(file_get_contents($fname), "UTF-8", "UCS-2");
$this->parseHeader();
}
/**
* Converts @{link #controlCodes} array to fast-replacement hash
*/
function convertControlCodes() {
$tmp = array();
foreach ($this->controlCodes as $v) {
$k = code2utf($v);
$tmp[$k] = "\u".str_pad(dechex($v),4,0,STR_PAD_LEFT);
}
$this->controlCodes = $tmp;
}
/**
* Converts char code to UTF codepoing, if required
*
* @param $chr char or 4-byte hex
* @return string UTF codepoint
*/
function __chr2utf ($chr) {
if (preg_match("/^[\\da-f]{4,6}$/i", $chr)) {
return code2utf(hexdec($chr));
} else {
return $chr;
}
}
/**
* Serializes the layout row
*
* @param $data row data
* @param $token row id
* @return serialized row
*/
function __serializeRow ($data, $token) {
$uni_keys = array_keys($this->controlCodes);
$uni_vals = array_values($this->controlCodes);
if ($this->colmap[0] == $token) {
$charset = addcslashes(join('',$data),"\\'");
$charset = str_replace($uni_keys, $uni_vals, $charset);
return $token.":'".$charset."'";
} else if (!empty($data) && in_array($token, $this->colmap)) {
$str = $token.":{";
foreach ($data as $k => $v) {
$charset = addcslashes(join('',$v),"\\'");
$charset = str_replace($uni_keys, $uni_vals, $charset);
$data[$k] = $k.":'".$charset."'";
}
$str .= join(",",$data)."}";
return $str;
}
return null;
}
/**
*
*
*
*/
function __serializeDeadkeys() {
if (!empty($this->deadkey)) {
$dk = array();
foreach ($this->deadkey as $k => $v) {
$charr = array();
foreach ($v as $s => $t) {
$charr[] = $s;
$charr[] = $t;
}
$dk[] = "'".addslashes($k)."':'".addcslashes(join('',$charr),"\\'"/*.$this->controlCodes*/)."'";
}
return "dk:{".join(",",$dk)."}";
}
}
/**
* Retrieves the DEADKEY xxxx block from the layout description, parses it and appends to {@link #deadkey}
*
* @param $sym symbol to look deadkeys for
*/
function parseDeadkey ($sym) {
$str = &$this->layoutText;
$char = $this->__chr2utf($sym);
if (mb_strlen($sym) == 1) {
$sym = str_pad(dechex(ord($sym)),4,0,STR_PAD_LEFT);
}
if (!key_exists($char, $this->deadkey)) {
// DEADKEY xxx block
if (preg_match("/^DEADKEY\s+".$sym."(.+?)(?:^KEYNAME|^DEADKEY)/ms", $str, $m)) {
$keys = preg_split("/\s*[\r\n]+\s*/m",$m[1]);
$deadkeys = array();
for ($i=0; $i<sizeof($keys); $i++) {
$key = trim($keys[$i]);
if (!empty($key)) {
$key = preg_split("/\t+/",$key);
$deadkeys[$this->__chr2utf($key[0])] = $this->__chr2utf($key[1]);
}
}
if (sizeof($deadkeys) > 0) {
$this->deadkey[$char] = $deadkeys;
}
}
}
}
/**
* Retrieves the LIGATURE block and parses it if exists
*
* @param $sym symbol to look deadkeys for
*/
function parseLigature () {
$str = &$this->layoutText;
if (empty($this->ligature)) {
// LIGATURE block
if (preg_match("/^LIGATURE(.+?)(?:^KEYNAME|^DEADKEY)/ms", $str, $m)) {
preg_match_all("#^([A-Z\\d_]+)\\t\\t(\\d+)\\t+((?:[^\\t]+\\t)+)#m", $m[1], $m);
if (!empty($m[1])) {
$keys = $m[1];
$modes = $m[2];
foreach ($keys as $key => $val) {
$mode = $modes[$key];
//this seems a bit strange, but ligatures for altGr and shift+altGr
//are counted by a real column number, not a 'virtual' one, set in the column header
if (key_exists($mode, $this->columns)) {
$this->ligature[$val][$this->columns[$mode]] = array_map(create_function('$a','return code2utf(hexdec($a));'), array_filter(explode("\t",$m[3][$key])));
}
}
}
}
}
}
/**
* Preprocesses layout according to the KLC 1.4 file format
* Retrieves only name, code, domain, copyright and company fields
*
* @return void
*/
function parseHeader() {
$str = &$this->layoutText;
preg_match("/^.*?KBD\\t[^\\t]+\\t\"([^\"]+)/m", $str, $m);
$this->name = $m[1];
// probably we have to skip standard comment from MSKLC like "- Custom"
// $this->name = array_shift(preg_split("/\\s-\\s/",$m[1]));
preg_match("/^LOCALENAME\\t\"(\\w+)-(\\w+)/m", $str, $m);
if (empty($m)) {
$this->code = "US";
$this->domain = "EN";
} else {
$this->code = $m[2];
$this->domain = mb_strtoupper($m[1]);
}
preg_match("/^COMPANY\\t\"([^\"]*)/m", $str, $m);
$this->company = $m[1];
preg_match("/^COPYRIGHT\\t\"([^\"]+)/m", $str, $m);
$this->copyright = $m[1];
if (preg_match("/ilya lebedev/i",$this->copyright)) $this->copyright = "";
}
/**
* Makes the complete KLC parse, except done in the {@link #preParse}
*
* @return void
*/
function parse() {
$str = &$this->layoutText;
// available columns
preg_match("#^//(SC[^\\r\\n]+)#m", $str, $m);
$this->columns = array_slice(preg_split("/\\t/", $m[1]), 4);
$this->parseLigature();
// LAYOUT block
// String format: ScanCode \t KeyId \t CapsFlag \t NormalKey \t ShiftKey \t (and so on)
// [SGCaps]: -1 \t -1 \t 0 \t SGCapsKey \t ShiftSGCapsKey
preg_match("/^LAYOUT.+?--[\\r\\n]+(.+?)(?:^DEADKEY|^LIGATURE|^KEYNAME)/sm",$str,$m);
$strings = preg_split("#[\\r\\n]+#",$m[1]);
$strings = array_map(create_function('$a','return preg_split("#\\t+#",$a);'),array_filter($strings));
// Column values
// 0 - normal key state
// 1 - Shift
// 2 - Ctrl
// 3 - Shift+Ctrl
// 4 - AltGr (Shift+Ctrl)
// 5 - Shift+AltGr (Shift+Ctrl+Alt)
// 6 - SGCaps
// 7 - Shift+SGCaps
for ($i=0; $i<sizeof($strings); $i++) {
$sgcaps = 'SGCap' == $strings[$i][2];
$shiftlock = 1 == $strings[$i][2];
$keyCode = $strings[$i][0];
$keyName = $strings[$i][1];
$string = $strings[$i];
if (key_exists($keyCode, $this->keymap)) {
$lKey = &$this->keymap[$keyCode];
foreach ($this->columns as $z => $v) {
$sym = trim($string[$z+3]);
$col = $this->colmap[$v];
$res;
if ('-1' == $sym) {
continue;
} else if ('%%' == $sym) {
$res = $this->ligature[$keyName][$v];
} else if (mb_substr($sym, -1) == '@') {
$sym = str_replace('@','',$sym);
$res = "\x03".$this->__chr2utf($sym);
$this->parseDeadkey($sym);
} else {
$res = $this->__chr2utf($sym);
}
$lKey[$col] = $res;
if ($shiftlock) {
if (0 == $z) {
$lKey[$this->colmap[9]] = $res;
} else if (1 == $z) {
$lKey[$this->colmap[8]] = $res;
}
}
}
if ($sgcaps) {
$string = $strings[$i+1];
if (key_exists(3, $string) && false === mb_strpos($string[3], "//")) {
$col = $this->colmap[8];
$lKey[$col] = $this->__chr2utf($string[3]);
}
if (key_exists(4, $string) && false === mb_strpos($string[4], "//")) {
$col = $this->colmap[9];
$lKey[$col] = $this->__chr2utf($string[4]);
}
}
}
if ($sgcaps) {
$i++;
}
}
}
/**
* Parses layout and returns structured data describing it
*
* @return array
*/
function & getParsedLayout () {
mb_internal_encoding("UTF-8");
$this->parse();
$VK = array('name' => $this->name
,'code' => $this->code
,'domain' => $this->domain
,'copy' => $this->copyright
,'company' => $this->company
,'keys' => $this->keymap
,'dk' => $this->deadkey
,'cbk' => $this->getCallback()
,'addon' => $this->getAddon()
);
return $VK;
}
/**
* @return string with addon file path
*/
function getAddon() {
$add = realpath($this->root.$this->addon.$this->code.'.js');
if (!file_exists($add)) {
$add = '';
}
return $add;
}
/**
* @return string with callback file path
*/
function getCallback() {
$add = realpath($this->root.$this->addon.$this->callback.preg_replace("/.+[\\/\\\\]+(.+)\\.klc$/i","\\1.js",$this->fname));
if (file_exists($add)) {
return $add;
}
return '';
}
function getFilename() {
return basename($this->fname, LAYOUT_EXT);
}
/**
* @return layout name
*/
function getName () {
return $this->name;
}
/**
* @return layout code
*/
function getCode () {
return $this->code;
}
/**
* @return layout domain
*/
function getDomain () {
return $this->domain;
}
/**
* @return layout copyright
*/
function getCopyright () {
return $this->copyright;
}
/**
* @param $v string addon folder name
*/
function setAddonFolder ($v) {
$this->addonFolder = $v;
}
/**
* Serializes layout in the following way
* 1) ligatures are encoded as substrings surrounded with 0x01
* 2) 'normal' key state is 47-item length array with the "empty" keys shown as 0x02
* 3) 'shift', 'alt', 'shift_alt', 'caps', 'shift_caps' are the objects with 'diffs' against
* the 'normal' keys
* 4) deadkeys are stored as the hash of string with all available pairs of the source and target symbols
*
* @param $type one of: SERIALIZE_USE_CODE or SERIALIZE_USE_DOMAIN
*/
function serialize ($type) {
$domain = mb_strtoupper($this->domain);
switch ($type) {
case "lng" :
$code = $domain==$this->code?$this->code
:$domain.'-'.$this->code;
break;
case "domain" :
$code = $domain;
break;
default:
return "";
}
$this->parse();
$keys = & $this->keymap;
$anc = array();
$asc = array();
$aac = array();
$asac = array();
$acc = array();
$ascc = array();
$i_anc = $i_asc = $i_aac = $i_asac = $i_acc = $i_ascc = 0;
foreach ($keys as $k => $v) {
$nc = @$v[$this->colmap[0]];
$sc = @$v[$this->colmap[1]];
$ac = @$v[$this->colmap[6]];
$sac = @$v[$this->colmap[7]];
$cc = @$v[$this->colmap[8]];
$scc = @$v[$this->colmap[9]];
if (is_array($nc)) {
// ligature
$nc = chr(0x01).join($nc).chr(0x01);
} else if (!is_string($nc)) {
// key not exists
$nc = chr(0x02);
}
if (is_array($sc)) {
// ligature
$sc = chr(0x01).join($sc).chr(0x01);
}
if (is_array($ac)) {
// ligature
$ac = chr(0x01).join($ac).chr(0x01);
}
if (is_array($sac)) {
// ligature
$sac = chr(0x01).join($sac).chr(0x01);
}
if (is_array($cc)) {
// ligature
$cc = chr(0x01).join($cc).chr(0x01);
}
if (is_array($scc)) {
// ligature
$scc = chr(0x01).join($scc).chr(0x01);
}
// fill the things
$anc[$i_anc++] = $nc;
//shift
if (is_string($sc) && (preg_match("/^\\x03./",$sc) || in_array($sc, $this->problemChars) || mb_strtoupper($sc) != mb_strtoupper($nc))) {
$asc[$i_asc][] = $sc;
} else {
// key not exists
$i_asc = $i_anc;
}
// alt
if (is_string($ac) && (preg_match("/^\\x03./",$ac) || in_array($ac, $this->problemChars) || mb_strtoupper($ac) != mb_strtoupper($nc))) {
$aac[$i_aac][] = $ac;
} else {
// key not exists
$i_aac = $i_anc;
}
// shift+alt
if (is_string($sac) && (preg_match("/^\\x03./",$sac) || (in_array($sac, $this->problemChars) || mb_strtoupper($sac) != mb_strtoupper($ac)))) {
$asac[$i_asac][] = $sac;
} else {
// key not exists
$i_asac = $i_anc;
}
// caps
// could not have deadkeys
if (is_string($cc) && (in_array($cc, $this->problemChars) || mb_strtoupper($cc) != mb_strtoupper($nc))) {
$acc[$i_acc][] = $cc;
} else {
// key not exists
$i_acc = $i_anc;
}
// shift+caps
// could not have deadkeys
if (is_string($scc) && (in_array($scc, $this->problemChars) || mb_strtoupper($scc) != mb_strtoupper($cc))) {
$ascc[$i_ascc][] = $scc;
} else {
// key not exists
$i_ascc = $i_anc;
}
}
$serArr = array(
"code:'".addslashes($code)."'"
,"name:'".addslashes($this->name)."'"
,$this->__serializeRow($anc, $this->colmap[0])
,$this->__serializeRow($asc, $this->colmap[1])
,$this->__serializeRow($aac, $this->colmap[6])
,$this->__serializeRow($asac, $this->colmap[7])
,$this->__serializeRow($acc, $this->colmap[8])
,$this->__serializeRow($ascc, $this->colmap[9])
,$this->__serializeDeadkeys()
);
$add = $this->getCallback();
if (!empty($add)) {
$serArr[] = "'cbk':".trim(file_get_contents($add),"\r\n; ");
}
return "{".join("\n,",array_filter($serArr))."}";
}
/**
* Serializes layout header in the following way
* 1) ligatures are encoded as substrings surrounded with 0x01
* 2) 'normal' key state is 47-item length array with the "empty" keys shown as 0x02
*
* @param $type one of: SERIALIZE_USE_CODE or SERIALIZE_USE_DOMAIN
*/
function serializeHeader($type) {
$domain = mb_strtoupper($this->domain);
switch ($type) {
case "lng" :
$code = $domain==$this->code?$this->code
:$domain.'-'.$this->code;
break;
case "domain" :
$code = $domain;
break;
default:
return "";
}
$this->parse();
$keys = & $this->keymap;
$anc = array();
$i_anc = $i_asc = $i_aac = $i_asac = $i_acc = $i_ascc = 0;
foreach ($keys as $k => $v) {
$nc = @$v[$this->colmap[0]];
if (is_array($nc)) {
// ligature
$nc = chr(0x01).join($nc).chr(0x01);
} else if (!is_string($nc)) {
// key not exists
$nc = chr(0x02);
}
// fill the things
$anc[$i_anc++] = $nc;
}
$serArr = array(
"code:'".addslashes($code)."'"
,"name:'".addslashes($this->name)."'"
,$this->__serializeRow($anc, $this->colmap[0])
,"requires:['".
(($this->getAddon()=="")?""
:($this->addonFolder.basename($this->getAddon())."','"))
.($this->getFilename()).".js']"
);
return "{".join("\n,",array_filter($serArr))."}";
}
}