1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Initial Developer of the Original Code is Fireinput Inc.
 15  *
 16  * Portions created by the Initial Developer are Copyright (C) 2007
 17  * the Initial Developer. All Rights Reserved.
 18  *
 19  * Contributor(s):
 20  *     Olly Ja <ollyja@gmail.com>
 21  *
 22  * Alternatively, the contents of this file may be used under the terms of
 23  * either the GNU General Public License Version 2 or later (the "GPL"), or
 24  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 25  * in which case the provisions of the GPL or the LGPL are applicable instead
 26  * of those above. If you wish to allow use of your version of this file only
 27  * under the terms of either the GPL or the LGPL, and not to allow others to
 28  * use your version of this file under the terms of the MPL, indicate your
 29  * decision by deleting the provisions above and replace them with the notice
 30  * and other provisions required by the GPL or the LGPL. If you do not delete
 31  * the provisions above, a recipient may use your version of this file under
 32  * the terms of any one of the MPL, the GPL or the LGPL.
 33  *
 34  * ***** END LICENSE BLOCK ***** 
 35  */
 36 
 37 var Wubi = function()  {}; 
 38 
 39 Wubi.prototype =  extend(new FireinputIME(), 
 40 {
 41     // 0 to disable debug or non zero to enable debug 
 42     debug: 0, 
 43 
 44     // the name of IME 
 45     name: IME_WUBI,
 46 
 47     // array to keep all matched words 
 48     charArray: null,
 49 
 50     // current position of charArray 
 51     charIndex: 0, 
 52 
 53     // invalid input key 
 54     validInputKey: null,
 55 
 56     // the hash table for wubi key=> word
 57     keyWubiHash: null, 
 58 
 59     // the hash table for wubi word=>key for learning 
 60     wordWubiHash: null, 
 61 
 62     // the hash table for user frequency 
 63     userCodeHash: null, 
 64 
 65     // use code hash table event 
 66     userTableChanged: false, 
 67 
 68     // full/half letter converter 
 69     letterConverter: null, 
 70 
 71     // pinyin Schema 
 72     wubiSchema: null, 
 73 
 74     // encoding mode. Either simplified or big5. Simplified default. 
 75     encodingMode: ENCODING_ZH, 
 76 
 77     // engine enabled 
 78     engineDisabled: false, 
 79 
 80     // can auto insertion 
 81     autoInsertion: false, 
 82 
 83     // the entrance function to load all related tables 
 84     loadTable: function()
 85     {
 86        letterConverter = new FullLetterConverter(); 
 87 
 88        // setTimeout to not block firefox start
 89        var self = this; 
 90        setTimeout(function() { return self.loadWubiTable(); }, 500); 
 91 
 92        // init encoding table 
 93        FireinputEncoding.init(); 
 94     },
 95 
 96     getCodeLine: function(str)
 97     {
 98        var strArray = str.split(':');
 99        if(strArray.length < 2)
100           return;
101 
102        // initKey:key=>word 
103        this.keyWubiHash.setItem(strArray[0],strArray[1]);
104     },
105 
106     loadWubiTable: function()
107     {
108        var ios = IOService.getService(Components.interfaces.nsIIOService);
109        var fileHandler = ios.getProtocolHandler("file")
110                          .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
111 
112        var path = this.getDataPath(); 
113        var datafile = ""; 
114        if(this.wubiSchema == WUBI_86)
115           datafile = fileHandler.getFileFromURLSpec(path + this.getWubi86File()); 
116        if(this.wubiSchema == WUBI_98)
117           datafile = fileHandler.getFileFromURLSpec(path + this.getWubi98File()); 
118        
119        if(!datafile.exists())
120        {
121            this.engineDisabled = true; 
122            return; 
123        }
124 
125        this.keyWubiHash = new FireinputHash();
126 
127        var options = {
128           caller: this, 
129           oncomplete: this.loadUserTable, 
130           onavailable: this.getCodeLine
131        }; 
132 
133        FireinputStream.loadDataAsync(datafile, options);
134     },
135 
136 
137     getUserCodeLine: function(str)
138     {
139        var strArray = str.split(':');
140        if(strArray.length < 4)
141           return;
142 
143        // user data format: word: freq key initKey 
144 
145        this.userCodeHash.setItem(strArray[0], {freq: strArray[1], key: strArray[2], initKey: strArray[3]});
146 
147     },
148 
149     loadUserTable: function()
150     {
151        var ios = IOService.getService(Components.interfaces.nsIIOService);
152        var fileHandler = ios.getProtocolHandler("file")
153                          .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
154 
155        var path = FireinputUtils.getAppRootPath();
156        var datafile = fileHandler.getFileFromURLSpec(path + this.getUserDataFile());
157        if(!datafile.exists())
158           return; 
159        this.userCodeHash = new FireinputHash();
160 
161        var options = {
162           caller: this, 
163           onavailable: this.getUserCodeLine
164        }; 
165        FireinputStream.loadDataAsync(datafile, options); 
166     },
167 
168     isEnabled: function()
169     {
170        if(this.engineDisabled)
171           return false; 
172        var ios = IOService.getService(Components.interfaces.nsIIOService);
173        var fileHandler = ios.getProtocolHandler("file")
174                          .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
175 
176        var path = this.getDataPath();
177        var datafile = fileHandler.getFileFromURLSpec(path + this.getWubi86File());
178        if(!datafile.exists())
179        {
180           datafile = fileHandler.getFileFromURLSpec(path + this.getWubi98File());
181           if(datafile.exists())
182              return true; 
183           return false; 
184        }
185 
186        return true; 
187     }, 
188 
189     isSchemaEnabled: function()
190     {
191        if(this.engineDisabled)
192           return false;
193        var ios = IOService.getService(Components.interfaces.nsIIOService);
194        var fileHandler = ios.getProtocolHandler("file")
195                          .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
196 
197        var path = this.getDataPath();
198        if(this.wubiSchema == WUBI_86)
199           datafile = fileHandler.getFileFromURLSpec(path + this.getWubi86File()); 
200        if(this.wubiSchema == WUBI_98)
201           datafile = fileHandler.getFileFromURLSpec(path + this.getWubi98File()); 
202        if(!datafile.exists())
203        {
204           return false;
205        }
206 
207        return true;
208     },
209 
210     canComposeNew: function()
211     {
212        return false; 
213     },
214 
215     setSchema: function(schema)
216     {
217        //FireinputLog.debug(this, "Set schema: " + schema);
218        this.wubiSchema = schema; 
219     }, 
220 
221     getAllowedInputKey: function()
222     {
223        return "abcdefghijklmnopqrstuvwxy"; 
224     },
225 
226     setEncoding: function(encoding)
227     {
228        this.encodingMode = encoding; 
229     }, 
230 
231     convertLetter: function(code)
232     {
233        // Full: number + alpha character
234        // Punct: any printable character which is not a space or an alphanumeric character
235        if((!this.isHalfLetterMode() && 
236           ((code > 47 && code < 58) || 
237            (code > 64 && code < 91))) || 
238           (!this.isHalfPunctMode() && 
239            !((code > 47 && code < 58) ||
240             (code > 64 && code < 91))))
241           return letterConverter.toFullLetter(String.fromCharCode(code)); 
242 
243        return String.fromCharCode(code); 
244     }, 
245 
246     find: function(inputChar)
247     {
248        var s = inputChar; 
249        var retArray = null;
250 
251        this.autoInsertion = false; 
252        // here we will do searching on inputChar by length -1 every time if retArray is null 
253        while(s.length > 0 && (retArray=this.searchAll(s)) == null)
254        {
255           s = s.substr(0, s.length - 1); 
256           // remove last single quot if it presents.  
257           if(s.substr(s.length-1, 1) == "'")
258           { 
259             s = s.substr(0, s.length - 1); 
260           }
261        }
262 
263        this.validInputKey = s;
264        return {charArray:retArray, validInputKey: this.validInputKey};
265     },
266 
267     searchAll: function(inputChar)
268     {
269        this.charArray = null; 
270 
271        this.charArray = this.codeLookup(inputChar); 
272        if(this.charArray != null)
273           return this.charArray.slice(0, 9);
274        return null; 
275     },
276 
277     codeLookup: function(keys)
278     {
279        var charArray = null; 
280        if(keys == null || keys.length <= 0)
281           return null; 
282 
283        // a valid charArray consist of {key:key, word: word}
284        charArray = this.getValidWord(keys);
285        if(!charArray)
286           return null; 
287 
288        return charArray; 
289     },
290 
291     next: function (endFlag)
292     {
293        if(!this.charArray)
294           return null; 
295 
296        // FireinputLog.debug(this,"this.charIndex: " + this.charIndex);
297        // if the next 9 are already displayed, return null
298        if((this.charIndex+10) >= this.charArray.length)
299           return null; 
300 
301        var i = this.charIndex; 
302        if(!endFlag)
303            this.charIndex += 9; 
304        else 
305        {
306            i = this.charArray.length-9; 
307            i -= 9; 
308            this.charIndex = i>0 ? i:0; 
309        }
310        // FireinputLog.debug(this,"this.charIndex: " + this.charIndex);
311        return {charArray:this.charArray.slice(this.charIndex, this.charIndex+9), validInputKey: this.validInputKey};
312     }, 
313 
314     prev: function (homeFlag)
315     {
316        if(!this.charArray)
317           return null; 
318        // FireinputLog.debug(this,"this.charIndex: " + this.charIndex);
319        // if the previous 9 are already displayed, return null
320        if((this.charIndex-9) < 0)
321           return null; 
322 
323        if(!homeFlag)
324           this.charIndex -= 9; 
325        else
326           this.charIndex = 0; 
327        
328        // FireinputLog.debug(this,"this.charIndex: " + this.charIndex);
329        return {charArray: this.charArray.slice(this.charIndex, this.charIndex+9), validInputKey: this.validInputKey};
330     }, 
331 
332     isBeginning: function()
333     {
334        return this.charIndex == 0; 
335     },
336 
337     isEnd: function()
338     {
339        return (this.charIndex+10) >= this.charArray.length; 
340     }, 
341 
342     canAutoInsert: function()
343     {
344        return this.autoInsertion;       
345     }, 
346 
347     getValidWord: function(key)
348     {
349        var wordArray = null; 
350        var userArray = new Array(); 
351        var wordList = new Array(); 
352 
353        if(!key)
354           return null; 
355 
356        var keyInitial = key.substring(0, 1); 
357        if(key.length >= 3)
358           keyInitial = key.substring(0, 3); 
359        if(!this.keyWubiHash.hasItem(keyInitial))
360              return null; 
361  
362        // only enable autoinsertion for 4 keys 
363        if(key.length >= 4)
364           this.autoInsertion = true; 
365 
366        var wubiWordList = this.keyWubiHash.getItem(keyInitial); 
367 
368        var wubiWordArray = wubiWordList.split(","); 
369 
370        wordArray = new Array(); 
371 
372        for(var i=0; i < wubiWordArray.length; i++)
373        {
374           var wubiWord = wubiWordArray[i].split("=>"); 
375           
376           if(wubiWord[0].search(new RegExp("^" + key)))
377              continue; 
378 
379           // FireinputLog.debug(this,"wubiWordArray[i]: " + FireinputUnicode.getUnicodeString(wubiWordArray[i]));
380 
381           var word = ""; 
382           try 
383           {
384              word = wubiWord[1].match(/[\D\.]+/g)[0];
385           }        
386           catch(e) {}
387 
388 
389           if(word.length <= 0) 
390              continue; 
391 
392 
393           if(typeof(wordList[word]) != 'undefined')
394              continue; 
395 
396           var encodedWord = FireinputEncoding.getEncodedString(word, this.encodingMode);
397 
398           wordList[word] = 1; 
399           if(this.userCodeHash && this.userCodeHash.hasItem(word))
400           {
401              var ufreq = this.userCodeHash.getItem(word);
402              /* use this way other than push to have better performance 
403               * http://aymanh.com/9-javascript-tips-you-may-not-know
404               */
405              userArray[userArray.length] = {key: wubiWord[0], word:word+ufreq.freq, encodedWord:encodedWord+ufreq.freq}; 
406           }
407           else
408           {
409              var freq = wubiWord[1].match(/[\d\.]+/g)[0];
410              wordArray[wordArray.length] = {key:wubiWord[0], word:wubiWord[1], encodedWord:encodedWord+freq};
411           }
412        }
413 
414        // free it 
415        wordList = null; 
416 
417        //FireinputLog.debug(this,"wordArray: " + this.getKeyWord(wordArray));
418        if(userArray.length <= 0)         
419           return wordArray; 
420 
421        // sort the first 10 items 
422        if(userArray.length < 10)
423        {
424           arrayInsert(userArray, userArray.length, wordArray.slice(0, 9)); 
425           userArray.sort(this.sortCodeArray); 
426           arrayInsert(userArray, userArray.length, wordArray.slice(10, wordArray.length)); 
427        }
428        else
429        {
430           userArray.sort(this.sortCodeArray); 
431           arrayInsert(userArray, userArray.length, wordArray.slice(0, wordArray.length)); 
432        }
433 
434        //FireinputLog.debug(this,"userArray: " + this.getKeyWord(userArray));
435        return userArray; 
436     },
437 
438     flushUserTable: function()
439     {
440        if(this.userCodeHash && this.userTableChanged)
441        { 
442           FireinputSaver.save(this.userCodeHash);
443        }
444     },
445 
446     updateFrequency: function(word, key, initKey)
447     {
448        var freq = word.match(/[\d\.]+/g)[0];
449        var chars = word.match(/[\D\.]+/g)[0];
450        if(!this.userCodeHash)
451           this.userCodeHash = new FireinputHash();
452 
453        var newfreq = 0; 
454        if(this.userCodeHash.hasItem(chars))
455        {
456           var charopt = this.userCodeHash.getItem(chars); 
457           var freq1 = charopt.freq; 
458           newfreq = parseInt("0xFFFFFFFF", 16) - freq1; 
459 
460           if(typeof(initKey) == "undefined")
461               initKey = charopt.initKey; 
462        }
463        else
464        {
465           newfreq = parseInt("0xFFFFFFFF", 16) - freq; 
466 
467           if(typeof(initKey) == "undefined")
468           {
469               initKey = key.substring(0, 1);
470               if(key.length >= 3)
471                  initKey = key.substring(0, 3);
472           }
473        }
474 
475        if(newfreq)
476        {
477           newfreq /= Math.pow(2, 16); 
478           if(newfreq < 1) newfreq = 1; 
479        }
480        freq = Math.round(newfreq) + parseInt(freq); 
481 
482        this.userCodeHash.setItem(chars, {freq: freq, key: key, initKey: initKey});
483 
484        this.userTableChanged = true; 
485 
486        // FireinputLog.debug(this,"word: " + word);
487        //FireinputLog.debug(this,"chars: " + chars + ", freq: " + freq);
488        //FireinputLog.debug(this,"chars: " + chars + ", key: " + key + ", initKey: " + initKey);
489        return freq; 
490     },
491 
492     storeUserPhrase: function(userPhrase)
493     {
494        return; 
495     }
496     
497 });


syntax highlighted by Code2HTML, v. 0.9.1