Bộ gõ tiếng việt cho WebOS

Thảo luận trong 'THỦ THUẬT WEBOS' bắt đầu bởi nhatientri, 5/5/12.

  1. nhatientri Thành viên


    Các bác có thể gõ kiểu telex hoặc VNI tuỳ ý :D

    Patch đưới đây chỉ dùng được cho hp veer chạy WebOS 2.1
    các bác có thể chạy patch hoặc copy mojo.js vào /usr/palm/frameworks/mojo2/
    nháy 2 lần phím sym nó hiện chữ V lên là gõ được

    Đối với các máy khác, và các version khác của webos thì các bạn làm như sau:

    1. Dùng WebOS Quick Install copy mojo.js từ /usr/palm/frameworks/mojo2/
    2. Sửa mojo.js
    2.1 mở mojo.js bằng Notepad++ (dùng cái này cho dễ nhìn)
    2.2 copy đoạn này vào dòng đầu tiên:
    Code:
    window.kblayout="en";
    var isvUniOn= false;
    var timePress = 0;
    var vUni;
    2.3 Bấm ctrl+F để tìm dòng
    Code:
    document.write('<script type="text/javascript" onload="mojoLoaderLoaded();" src="/usr/palm/frameworks/mojoloader.js"><\/script>');
    Điền đoạn mã này vào bên trên dòng đó
    Code:
    //IME function
            document.onkeydown = function(event) {
            if (event.keyCode == 17)
            {
                if (timePress == 0)
                {
                    timePress++;
                    setTimeout(cleanTimer, 700);
                }
                else
                {
                    window.prevchar="";
                    window.prevcharen="";
                    if(window.kblayout=="en")
                    {
                        var t=document.getElementsByTagName("BODY")[0];
                        var div=document.createElement("div");
                        div.style.position="fixed";
                        div.id="kblayoutdiv";
                        div.style.display="block";
                        div.style.top="80%";
                        div.style.left="90%";
                        div.style.width="24px";
                        div.style.height="20px";
                        div.style.border="2px solid gray";
                        div.style.background="yellow";
                        div.style.zIndex="255";
                        div.style.opacity=0.6;
                        div.style.webkitBorderRadius="3px";
                        div.innerHTML="<div style=\"margin-top:-5px;text-align:center;color:#333333;\">V</div>";
                        t.appendChild(div);
                        window.kblayout="Vi";
                        disabled = false;
                        if(!isvUniOn){
                            vUni = new vietUni();
                            vUni.initTyper(event.target);
                            isvUniOn=true;
                        }
           
                    }
                    else
                    {
                        var t=document.getElementById("kblayoutdiv");
                        t.parentNode.removeChild(t);
                        window.kblayout="en";
                        disabled = true;
                        if(isvUniOn) vUni.setMethod(0);
                        isvUniOn=false;
                    }
                }
            }
     
        }
     
        function cleanTimer() {
            timePress = 0;
        }
        //start ime
    function vietUni(mode) {
      if (document.all || document.getElementById) {
        this.method = (mode == undefined || mode == null) ? 4 : mode;
        return this;
      }
      console.log("Xin loi, trinh duyet web cua ban khong cho phep dung VietTyping.");
      return false;
    }
     
    /**
    * Sets Vietnamese typing method
    */
    vietUni.prototype.setMethod = function(mode) {
      this.method = mode;
      if (this.typer) this.typer.keyMode = this.initKeys();
    };
     
    vietUni.prototype.initKeys = function() {
      switch (this.method) {
        case 1:
          if (!this.telexKeys) this.telexKeys = new vietKeysTelex();
          return this.telexKeys;
     
        case 2:
          if (!this.vniKeys) this.vniKeys = new vietKeysVni();
          return this.vniKeys;
     
        case 3:
          if (!this.viqrKeys) this.viqrKeys = new vietKeysViqr();
          return this.viqrKeys;
     
        case 4:
          if (!this.allKeys) this.allKeys = new vietKeysAll();
          return this.allKeys;
     
        default:
          if (!this.vkOff) this.vkOff = new vietKeysOff();
          return this.vkOff;
      }
    };
     
    /**
    * Initializes Vietnamese typer for an editable element (textbox, textarea, iframe.document)
    */
    vietUni.prototype.initTyper = function(el) {
      // validates parameters
      if (!el) return;
     
      if (!this.typer) {
        this.typer = new vietTyper();
        this.typer.keyMode = this.initKeys();
      }
     
      var self = this;
      if (el.attachEvent) {
        el.attachEvent("onkeypress", function(evt) {
          return vietUni.vietTyping(evt, self, el);
        });
      }
      else if (el.addEventListener) {
        el.addEventListener("keypress", function(evt) {
          return vietUni.vietTyping(evt, self, el);
        }, true);
      }
      else if (el.onkeypress) {
        var oldFunc = el.onkeypress;
        if (typeof oldFunc !== "function") {
          el.onkeypress = function(evt) {
            return vietUni.vietTyping(evt, self, el);
          };
        }
        else {
          el.onkeypress = function(evt) {
            return (!oldFunc(evt)) ? false : vietUni.vietTyping(evt, self, el);
          };
        }
      }
    };
     
    /**
    * Handles key-press event of the editable element
    */
    vietUni.vietTyping = function(evt, vUni, el) {
      // validates parameters
      if (!vUni || vUni.typer.keyMode.off) return true;
     
      // event fixing
      if (!evt) evt = event;
     
      // retrieves pressed key code
      var c = document.all ? evt.keyCode : (evt.which || evt.charCode);
      // out of typeable keys range
      if (c < 49 && c != 16 && c != 20) return true;
     
      // retrieves the current word
      var len, s = vUni.getCurrentWord(el);
      if (s == null || (len = s.length) < 1 || s.match(/\s+$/)) return true;
     
      // replaces with Vietnamese word
      vUni.typer.value = s;
      if (c > 32 && vUni.typer.typing(c)) {
        // found an increment of the word length
        if (((s = vUni.typer.value).length == len + 1) && c == s.charCodeAt(len)) {
          // fixs length of the replacement word
          vUni.typer.value = s.substr(0, len);
          len = 0;
        }
        vUni.replaceWord(el, vUni.typer.value);
     
        // cancels the current key
        if (len > 0) {
          if (typeof evt.cancelBubble !== "undefined") {
            evt.cancelBubble = true;
          }
          if (evt.stopPropagation) {
            evt.preventDefault();
            evt.stopPropagation();
          }
          return false;
        }
      }
     
      return (!evt.cancelBubble);
    };
     
    vietUni.prototype.getCurrentWord = function(el) {
      // for IE / Opera textbox
      if (document.selection && !el.createRange) {
        var caret = (!el.selection) ? el.document.selection.createRange() : el.selection.createRange();
        // is selected ?
        if (caret.text) return null;
     
        var caret2;
        try {
          caret2 = caret.duplicate();
          caret2.moveStart("word", -1);
        } catch(e) {
          var backward = -10;
          do {
            caret2 = caret.duplicate();
            caret2.moveStart("character", backward++);
          }
          while (!caret2.text && backward < 0);
        }
     
        el.curWord = caret2.duplicate();
     
        return caret2.text;
      }
      // for Firefox textbox
      else if (el.setSelectionRange) {
        var p1 = el.selectionStart, p2 = el.selectionEnd;
        // is selected ?
        if (p1 != p2) return null;
     
        p1 = Math.max(0, p2 - 10);
        el.pos1 = p1;
        el.pos2 = p2;
     
        return el.value.substr(p1, p2 - p1);
      }
      // for Firefox / Opera iframe.document
      else if (window.getSelection) {
        var sel = el.defaultView.getSelection();
        var rng = sel.getRangeAt(sel.rangeCount - 1).cloneRange();
     
        // is selected ?
        if (rng.toString()) return null;
     
        // get neareat word
        var p2 = rng.startOffset, nod = rng.endContainer;
        rng.setEnd(nod, p2);
        rng.setStart(nod, Math.max(0, p2 - 10));
     
        // store current word
        el.rng1 = rng;
        el.nod1 = nod;
        el.pos1 = rng.startOffset;
        el.pos2 = p2;
     
        var txt = rng.toString();
        // restore caret position
        rng.setStart(nod, p2);
     
        return txt;
      }
      else if (typeof el.value !== "undefined") {
        return el.value;
      }
     
      return null;
    };
     
    vietUni.prototype.replaceWord = function(el, newWord) {
      // for IE / Opera textbox
      if (document.selection && !el.createRange && el.curWord) {
        el.curWord.text = newWord;
        el.curWord.collapse(false);
      }
      // for Firefox textbox
      else if (el.setSelectionRange) {
        var p1 = el.pos1, p2 = el.pos2, txt = el.value;
        el.value = txt.substr(0, p1) + newWord + txt.substr(p2);
        //
        el.setSelectionRange(p1 + newWord.length, p1 + newWord.length);
      }
      // for Firefox / Opera iframe.document
      else if (window.getSelection && el.nod1 && el.nod1.insertData) {
        el.rng1.setStart(el.nod1, el.pos1);
        //alert(newWord + ':' + el.pos1 + ':' + el.pos2);
        el.nod1.insertData(el.pos1, newWord);
        el.nod1.deleteData(el.pos1 + newWord.length, el.pos2 - el.pos1);
     
        // move cursor to the end
        el.rng1.setEnd(el.nod1, el.pos2);
        el.rng1.setStart(el.nod1, el.pos2);
      }
      else if (typeof el.value !== "undefined") {
        el.value = newWord;
      }
    };
     
    /*---------- VietTyper class ----------*/
    function vietTyper() {
      this.value = "";
      this.charMap = new vietUnicodeMap();
      this.ctrlChar = '-';
      this.changed = 0;
      return this;
    }
     
    vietTyper.prototype.typing = function(ctrl) {
      //if (this.keyMode.off) return 0;
      this.changed = 0;
      this.ctrlChar = String.fromCharCode(ctrl);
      this.keyMode.getAction(this);
     
      this.correct();
      return this.changed;
    };
     
    vietTyper.prototype.compose = function(typ) {
      if (!this.value) return;
     
      var info = this.findCharToChange(typ);
      if (!info || !info[0]) return;
     
      var telex;
      if (info[0] == '\\') {
        telex = [1, this.ctrlChar, 1];
      }
      else if (typ > 6) {
        telex = this.charMap.getAEOWD(info[0], typ, info[3]);
      }
      else {
        telex = this.charMap.getDau(info[0], typ);
      }
      if (!(this.changed = telex[0])) return;
     
      // replace a character at info[1]
      this.value = this.value.substr(0, info[1]) + telex[1] + this.value.substr(info[1] + info[2]);
     
      // spell error
      if (!telex[2]) this.value += this.ctrlChar;
    };
     
    vietTyper.prototype.correct = function() {
      //if (!document.all) return 0;
      var val = this.value;
      if ('nNcC'.indexOf(this.ctrlChar) >= 0) val += this.ctrlChar;
     
      var er = /[^\x01-\x7f](hn|hc|gn)$/i.exec(val);
      if (er) {
        this.value = val.substr(0, val.length - 2) + er[1].charAt(1) + er[1].charAt(0);
        this.changed = 1;
      }
      else if (!this.changed) {
        return 0;
      }
     
      er = /\w([^\x01-\x7f])(\w*)([^\x01-\x7f])\S*$/.exec(this.value);
      if (!er) return 0;
     
      var i = this.charMap.isVowel(er[1]);
      var ri = (i - 1) % 24 + 1, ci = (i - ri) / 24;
      var i2 = this.charMap.isVowel(er[3]);
      if (!ci || !i2) return 0;
     
      var ri2 = (i2 - 1) % 24 + 1;
      var nc = this.charMap.charAt(ri) + er[2] + this.charMap.charAt(ci * 24 + ri2);
      //
      this.value = this.value.replace(new RegExp(er[1] + er[2] + er[3], 'g'), nc);
    };
     
    vietTyper.prototype.findCharToChange = function(typ) {
      var lastChars = this.charMap.lastCharsOf(this.value, 5);
      //
      var i = 0, c = lastChars[0][0], chr = 0;
      if (c == '\\') return [c, this.value.length - 1, 1];
     
      if (typ == 15) {
        while (!(chr = this.charMap.isVD(c))) {
          if ((c < 'A') || (i >= 4) || !(c = lastChars[++i][0])) return null;
        }
      } else {
        while ("cghmnptCGHMNPT".indexOf(c) >= 0) {
          if ((c < 'A') || (i >= 2) || !(c = lastChars[++i][0])) return null;
        }
      }
      c = lastChars[0][0].toLowerCase();
     
      var pc = lastChars[1][0].toLowerCase();
      var ppc = lastChars[2][0].toLowerCase();
      //
      if (i == 0 && typ != 15) {
        if ((chr = this.charMap.isVowel(lastChars[1][0])) && ("uyoia".indexOf(c) >= 0)
          && !this.charMap.isUO(pc, c) && !((pc == 'o' && c == 'a') || (pc == 'u' && c == 'y'))
          && !((ppc == 'q' && pc == 'u') || (ppc == 'g' && pc == 'i'))) i++;
        //
        if (c == 'a' && (typ == 9 || typ == 7)) i = 0;
      }
      c = lastChars[i][0];
     
      if ((i == 0 || chr == 0) && typ != 15) chr = this.charMap.isVowel(c);
      if (!chr) return null;
     
      var clen = lastChars[i][1], isuo = 0;
      //
      if ((i > 0) && (typ == 7 || typ == 8 || typ == 11)) {
        isuo = this.charMap.isUO(lastChars[i + 1][0], c);
        if (isuo) {
          chr = isuo;
          clen += lastChars[++i][1];
          isuo = 1;
        }
      }
     
      var pos = this.value.length;
      for (var j = 0; j <= i; j++) pos -= lastChars[j][1];
     
      return [chr, pos, clen, isuo];
    };
    /*---------- End of VietTyper class ----------*/
     
    /*---------- VietCharMap class ----------*/
    function vietCharMap() {
      // properties
      this.vietChars = null;
      this.length = 149;
      this.chrCache = new Array(20);
      this.indCache = new Array(20);
      this.cptr = 0;
      // methods
      this.caching = function(chr, ind) {
        this.chrCache[this.cptr] = chr;
        this.indCache[this.cptr++] = ind;
        this.cptr %= 20;
      };
      // constants
      this.vmap = [[7, 7, 7, 8, 8, 8, 9, 10, 11, 15],
                  [0, 3, 6, 0, 6, 9, 0, 3, 6, 0],
                  [1, 4, 7, 2, 8, 10, 1, 4, 7, 1]];
      return this;
    }
     
    vietCharMap.prototype.charAt = function(ind) {
      var chrCode = this.vietChars[ind];
     
      return chrCode ? String.fromCharCode(chrCode) : null;
    };
     
    vietCharMap.prototype.isVowel = function(chr) {
      var i = 0;
      while ((i < 20) && (chr != this.chrCache[i])) i++;
     
      if (i < 20) return this.indCache[i];
     
      i = this.length - 5;
      while ((chr != this.charAt(i)) && i) i--;
     
      this.caching(chr, i);
     
      return i;
    };
     
    vietCharMap.prototype.isVD = function(chr) {
      var ind = this.length - 5;
     
      while ((chr != this.charAt(ind)) && (ind < this.length)) ind++;
     
      return (ind < this.length) ? ind: 0;
    };
     
    vietCharMap.prototype.isUO = function(c1, c2) {
      if (!c1 || !c2) return 0;
     
      var ind1 = this.isVowel(c1);
      var ci = (ind1 - 1) % 12;
      if ((ci != 9) && (ci != 10)) return 0;
     
      var ind2 = this.isVowel(c2);
      ci = (ind2 - 1) % 12;
      if ((ci != 6) && (ci != 7) && (ci != 8)) return 0;
     
      return [ind1, ind2];
    };
     
    vietCharMap.prototype.getDau = function(ind, typ) {
      var accented = (ind < 25) ? 0: 1;
      var indI = (ind - 1) % 24 + 1;
      var charSet = (typ == 6) ? 0: typ;
     
      if ((typ == 6) && !accented) return [0];
     
      var newInd = charSet * 24 + indI;
      if (newInd == ind) newInd = indI;
     
      var chr = this.charAt(newInd);
      if (!chr) chr = this.lowerCaseOf(0, newInd);
     
      return [1, chr, newInd > 24 || typ == 6];
    };
     
    vietCharMap.prototype.getAEOWD = function(ind, typ, isuo) {
      var c = 0, i1 = isuo ? ind[0] : ind;
      var vc1 = (typ == 15) ? (i1 - 1) % 2 : (i1 - 1) % 12;
     
      if (isuo) {
        var base = ind[1] - (ind[1] - 1) % 12;
     
        if (typ == 7 || typ == 11) {
          c = this.charAt(i1 - vc1 + 9) + this.charAt(base + 7);
        }
        else if (typ == 8) {
          c = this.charAt(i1 - vc1 + 10) + this.charAt(base + 8);
        }
     
        return [c != 0, c, 1];
      }
     
      var i = -1, shif = 0, del = 0;
     
      while (shif == 0 && ++i < this.vmap[0].length) {
        if (this.vmap[0][i] == typ) {
          if (this.vmap[1][i] == vc1) {
            shif = this.vmap[2][i] - vc1;
          }
          else if (this.vmap[2][i] == vc1) {
            shif = this.vmap[1][i] - vc1;
          }
        }
      }
     
      if (shif == 0) {
        if (typ == 7 && (vc1 == 2 || vc1 == 8)) shif = -1;
        else if ((typ == 9 && vc1 == 2) || (typ == 11 && vc1 == 8)) shif = -1;
        else if (typ == 8 && (vc1 == 1 || vc1 == 7)) shif = 1;
     
        del = 1;
      }
      else {
        del = (shif > 0);
      }
     
      i1 += shif;
      var chr = this.charAt(i1);
     
      if (i1 < 145) this.caching(chr, i1);
      if (!chr) chr = this.lowerCaseOf(0, i1);
     
      return [shif != 0, chr, del];
    };
     
    vietCharMap.prototype.lastCharsOf = function(str, len) {
      if (!len) return [str.charAt(str.length - 1), 1];
     
      var vchars = new Array(len);
      for (var i = 0; i < len; i++) {
        vchars[i] = [str.charAt(str.length - i - 1), 1];
      }
      return vchars;
    };
    /*---------- End of VietCharMap class ----------*/
     
    /*---------- VietUnicodeMap class ----------*/
    function vietUnicodeMap() {
      var vcmap = new vietCharMap();
      vcmap.vietChars = new Array("UNICODE", 97, 226, 259, 101, 234, 105, 111, 244,
        417, 117, 432, 121, 65, 194, 258, 69, 202, 73, 79, 212, 416, 85, 431, 89,
        225, 7845, 7855, 233, 7871, 237, 243, 7889, 7899, 250, 7913, 253, 193,
        7844, 7854, 201, 7870, 205, 211, 7888, 7898, 218, 7912, 221, 224, 7847,
        7857, 232, 7873, 236, 242, 7891, 7901, 249, 7915, 7923, 192, 7846, 7856,
        200, 7872, 204, 210, 7890, 7900, 217, 7914, 7922, 7841, 7853, 7863, 7865,
        7879, 7883, 7885, 7897, 7907, 7909, 7921, 7925, 7840, 7852, 7862, 7864,
        7878, 7882, 7884, 7896, 7906, 7908, 7920, 7924, 7843, 7849, 7859, 7867,
        7875, 7881, 7887, 7893, 7903, 7911, 7917, 7927, 7842, 7848, 7858, 7866,
        7874, 7880, 7886, 7892, 7902, 7910, 7916, 7926, 227, 7851, 7861, 7869,
        7877, 297, 245, 7895, 7905, 361, 7919, 7929, 195, 7850, 7860, 7868, 7876,
        296, 213, 7894, 7904, 360, 7918, 7928, 100, 273, 68, 272);
      return vcmap;
    }
    /*---------- End of VietUnicodeMap class ----------*/
     
    /*---------- VietKeys classes ----------*/
    function vietKeys() {
      this.getAction = function(typer) {
        var i = this.keys.indexOf(typer.ctrlChar.toLowerCase());
        if (i >= 0) typer.compose(this.actions[i]);
      };
      return this;
    }
    function vietKeysOff() {
      this.off = true;
      this.getAction = function(typer) {};
      return this;
    }
     
    function vietKeysTelex() {
      var k = new vietKeys();
      k.keys = "sfjrxzaeowd";
      k.actions = [1, 2, 3, 4, 5, 6, 9, 10, 11, 8, 15];
      return k;
    }
    function vietKeysVni() {
      var k = new vietKeys();
      k.keys = "0123456789";
      k.actions = [6, 1, 2, 4, 5, 3, 7, 8, 8, 15];
      return k;
    }
    function vietKeysViqr() {
      var k = new vietKeys();
      k.keys = "\xB4/'\u2019`.?~-^(*+d";
      k.actions = [1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 15];
      return k;
    }
    function vietKeysAll() {
      var k = new vietKeys();
      k.keys = "sfjrxzaeowd0123456789\xB4/'`.?~-^(*+d";
      k.actions = [1, 2, 3, 4, 5, 6, 9, 10, 11, 8, 15, 6, 1, 2, 4, 5, 3, 7, 8, 8,
                  15, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 15];
      return k;
    }
        //end ime
    2.4 lưu lại.
    3. Dùng WebOS Quick Install copy mojo.js vừa sửa vào /usr/palm/frameworks/mojo2/ trong máy của bạn

    Các bạn nhớ backup lại file gốc nhéo_O

    File đính kèm:

    cucunktvn, cukhang, aloneheart8 người khác thích nội dung này.
  2. agent7t7 Thành viên

    bác ởi 2.2 thì làm theo cách ở dưới ạ
  3. nhatientri Thành viên

    đúng rồi bác cứ làm theo bước dưới,
    khi nào được nhớ thông báo cho anh em biết nhé
  4. agent7t7 Thành viên

    em dốt mấy vụ nè lắm k dám làm chuột bạch đâu TT. hay bác làm nốt rồi up lên luôn cho anh em đỡ phải mò.
  5. loveyouhp Thành viên

    http://www.tinhte.vn/threads/1022116/
    cái này dùng dễ hơn bạn ạ! :)
  6. agent7t7 Thành viên

    hiện cái touchpad của mình cũng đang dùng cách gõ này. nói thật là mình thấy rất bất tiện khi gõ. chưa kể mỗi lần restart là phải cài lại. thế nên mình thấy bộ gõ của bạn nhatientri hoàn thiện hơn tiện lợi hơn rất nhiều nên dùng cái nè cho veer cho lành :D
  7. nhatientri Thành viên

    bác gửi file mojo cho em,em tạo patch cho
  8. agent7t7 Thành viên

    không dùng file mojo của máy bác dc ạ
  9. nhatientri Thành viên

    Em chỉ có mỗi của máy HP Veer thôi, bác dùng máy khác thì gửi file cho em :D
  10. agent7t7 Thành viên

    em cũng dùng veer mà bác. Ý em là muốn nhờ bác làm luôn cho 2.2.4 đó mà : D
  11. nhatientri Thành viên

    Em không có file mojo của 2.2.4 chỉ có của 2.1 thôi, có gì bác đính kèm file 2.2.4 cho em
  12. nhatientri Thành viên

    Quên mất em chưa thử trên cái đó :D, browser danh bạ tin nhắn em test hết trên emulator rồi
  13. drthang2007 Thành viên

    đã test trên veer, bộ gõ này hoạt động tốt trên messenger, browser, facebook, nhưng vẫn chưa hoạt động trên memos và mail, có lẽ bác chủ thớt chỉ sửa file patch trong mojo2, chưa sửa trong mojo nên memos và mail nó không hoạt động, em đã thử chỉnh sửa trong mojo nhưng khi cài path thì báo lối, bác chủ thớt thử làm patch can thiệp vào mojo xem sao
  14. drthang2007 Thành viên

    bác thử chỉnh file patch trong cai mojo xem sao ?
  15. nhatientri Thành viên

    Cái này em chỉ patch cái frameword mojo2, có lẽ mấy cái khác nó nằm trong mojo 1 :D
    edit: Vừa debug xong, lỗi trong mojo 2
  16. agent7t7 Thành viên

    bác chủ thớt fix xong chưa. Post lại cho ae cùng test di.
  17. nhimret Thành viên

    cám ơn chủ thớt, dùng rất tốt :)

    dưới đây là file dùng cho bản 2.2.4 pre 3, hướng dẫn trong file nén luôn nhé

    File đính kèm:

    chầu rìa, tobe_alone81, musical1 người khác thích nội dung này.
  18. drthang2007 Thành viên

    bác kiểm tra lại xem, hình như bị lỗi thì phải, em copy vào thì thấy máy khởi động rồi không lên được
  19. nhimret Thành viên

    ah 2.2.4 của pre 3 :(
  20. drthang2007 Thành viên

    vẫn không gõ được trong memos và mail đâu bác ạ

Chia sẻ trang này