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 FireinputIMEPanel = 
 38 {
 39     // debug: 0 disable, non-zero enable 
 40     debug: 1,
 41 
 42     myComposeEnabled: false, 
 43     // last input key chars before composing a new phrase 
 44     myInputChar: "",
 45     // table lookup delay timer 
 46     myKeyTimer: null,
 47     // save user typing history 
 48     mySaveHistory: true,
 49     // auto insertion if there is one choice 
 50     myAutoInsert: false,
 51     // update word freq 
 52     myUpdateFreq: true,
 53     // enable inputkey exact match. not extra AMB 
 54     myInputKeyExactMatch: false,
 55 
 56     // last selected element to be used repeatedly 
 57     myLastSelectedElementValue: "",
 58     
 59     // IME input field focused status. Popup/some key handling will be surpressed if it's true 
 60     myIMEInputFieldFocusedStatus: false,
 61 
 62     // work selection font size
 63     mySelectionFontSize: 9, 
 64 
 65     // number of selection word/phrase
 66     myNumWordSelection: 9, 
 67 
 68     initialize: function()
 69     {
 70        initPref();  
 71     },
 72 
 73     initPref: function()
 74     {
 75        this.mySaveHistory = fireinputPrefGetDefault("saveHistory"); 
 76        this.myAutoInsert = fireinputPrefGetDefault("autoInsert");
 77        this.myUpdateFreq = fireinputPrefGetDefault("updateFreq");
 78        this.myInputKeyExactMatch = fireinputPrefGetDefault("inputKeyExactMatch");
 79        this.myNumWordSelection = fireinputPrefGetDefault("wordselectionNum"); 
 80        this.mySelectionFontSize = fireinputPrefGetDefault("wordselectionFontsize"); 
 81 
 82        this.initPanelUI(); 
 83     }, 
 84 
 85     getInputChar: function()
 86     {
 87        return this.myInputChar; 
 88     }, 
 89 
 90     setInputChar: function(char)
 91     {
 92        this.myInputChar = char; 
 93     }, 
 94 
 95     getNumWordSelection: function()
 96     {
 97        return this.myNumWordSelection; 
 98     }, 
 99 
100     getComposeEnabled: function()
101     {
102        return this.myComposeEnabled; 
103     },
104 
105     enableComposeEditor: function(flag)
106     {
107        this.myComposeEnabled = flag;
108     },
109  
110     getLastSelectedElementValue: function()
111     {
112        return this.myLastSelectedElementValue; 
113     }, 
114 
115     getIMEInputFieldFocusedStatus: function()
116     {
117        return this.myIMEInputFieldFocusedStatus; 
118     },
119 
120     setIMEInputFieldFocusedStatus: function(status)
121     {
122        this.myIMEInputFieldFocusedStatus = status; 
123     }, 
124 
125     initPanelUI: function()
126     {
127        var handle = document.getElementById("fireinputField"); 
128        var color = fireinputPrefGetDefault("inputboxFontcolor"); 
129        var fontsize = fireinputPrefGetDefault("inputboxFontsize"); 
130 
131        /*This might need to be handled differently if it works on win/mac */
132        handle.style.color = color; 
133        handle.style.fontSize = fontsize + "pt"; 
134 
135        handle = document.getElementById("fireinputIMEList"); 
136        color = fireinputPrefGetDefault("wordselectionFontcolor"); 
137        handle.style.color = color; 
138 
139        handle = document.getElementById("fireinputLongPanel"); 
140        handle.style.color = color; 
141       
142     },
143 
144     insertCharByIndex: function(event, index)
145     {
146        var idf = document.getElementById("fireinputField");
147        // if the keyCode is Enter, we want to output char + input key left in idf 
148        if(event.keyCode == KeyEvent.DOM_VK_RETURN)
149        {
150           this.insertCharToTarget(event, Fireinput.getTarget(), index, true, true);
151           return; 
152        }
153 
154        // process insertChar on-demand  
155        if(this.myComposeEnabled)
156        {
157           this.insertCharToComposer(event, index, "false"); 
158        }
159        else if(idf.selectionEnd < idf.value.length)
160        {
161           event.preventDefault();
162           event.stopPropagation();
163           // there are still some room on the left. Show the selected word from 0 until this position 
164           var subInputKeys = idf.value.substring(idf.selectionEnd, idf.value.length); 
165           var result = this.getCharByPos(index);
166           if(result)
167           {
168              FireinputComposer.addToPanel("false", result);
169           }
170 
171           // update inputField value and caret position 
172           idf.value = subInputKeys; 
173           this.myInputChar = subInputKeys.substring(0, 1); 
174           FireinputUtils.setCaretTo(idf, 1); 
175           // number key should be surpressed 
176 
177           this.findCharWithDelay(); 
178 
179        }
180        else 
181           this.insertCharToTarget(event, Fireinput.getTarget(), index, true);
182         
183     }, 
184 
185     sendStringToPanel: function(codeArray, inputKey)
186     {
187        if(!codeArray || codeArray.length <= 0)
188        {
189           this.hideChars(); 
190           return; 
191        }
192 
193        var fontsize = fireinputPrefGetDefault("wordselectionFontsize"); 
194 
195        var inputPanelElement = document.getElementById('fireinputIMEList'); 
196 
197        // FireinputLog.debug(this,"codeArray: " + Fireinput.getCurrentIME().getKeyWord(codeArray)); 
198        var codeArrayLength = codeArray.length; 
199        for (var i = 0; i < codeArrayLength; i++)
200        {
201           var codeValue = "";
202           if(typeof(codeArray[i].encodedWord) != 'undefined')
203              codeValue = codeArray[i].encodedWord.replace(/[\d\.]+$/g, ''); 
204           else
205              codeValue = FireinputUnicode.getUnicodeString(codeArray[i].word.replace(/[\d\.]+$/g, '')); 
206              
207           var elementId = "fireinputIMEList_label" + (i+1); 
208           if(document.getElementById(elementId))
209           {
210               var element = document.getElementById(elementId); 
211               element.setAttribute("value",  (i+1) + '.' + codeValue);
212               element.setAttribute("tooltiptext", "键: " + codeArray[i].key+"/右点搜索“"+codeValue+"”");
213               element.setAttribute("hiddenvalue", codeValue);
214               element.setAttribute("hiddenkey", codeArray[i].key);
215               element.setAttribute("hiddeninputkey", inputKey);
216               element.setAttribute("hiddenword", codeArray[i].word);
217               element.setAttribute("ufreq", (typeof(codeArray[i].ufreq) == 'undefined') ? 'true' : codeArray[i].ufreq);
218               element.style.fontSize = this.mySelectionFontSize + "pt"; 
219               continue; 
220           }
221                
222            var element = document.createElement("label"); 
223            element.setAttribute("value",  (i+1) + '.' + codeValue);
224            element.setAttribute("tooltiptext", "键: " + codeArray[i].key+"/右点搜索“"+codeValue+"”");
225            element.setAttribute("hiddenvalue", codeValue);
226            element.setAttribute("hiddenkey", codeArray[i].key);
227            element.setAttribute("hiddeninputkey", inputKey);
228            element.setAttribute("hiddenword", codeArray[i].word);
229            element.setAttribute("ufreq", (typeof(codeArray[i].ufreq) == 'undefined') ? 'true' : codeArray[i].ufreq);
230 
231            element.setAttribute("id", elementId); 
232            element.setAttribute("class", "charinputlabel"); 
233 
234            element.style.fontSize = this.mySelectionFontSize + "pt"; 
235 
236            var self = this; 
237            element.onclick = function(event) { self.insertCharToComposerByMouse(event);}; 
238            inputPanelElement.appendChild(element);
239        }
240 
241        // hide all other values 
242        this.hideChars(codeArray.length); 
243 
244        // check whether it needs to enable auto insertion 
245        if(this.myAutoInsert && Fireinput.getCurrentIME().canAutoInsert() && codeArray.length == 1)
246           this.insertCharToTarget(Fireinput.getEvent(), Fireinput.getTarget(), 1, true);
247 
248        // add long table  to panel 
249        if(this.mySaveHistory)
250           FireinputLongTable.addToPanel(); 
251     },
252  
253     hideAndCleanInput: function()
254     {
255        if(Fireinput.getInputBarStatus())
256        { 
257           var id = document.getElementById("fireinputIMEContainer"); 
258           id.hidePopup(); 
259        }
260 
261        var idf = document.getElementById("fireinputField");
262        idf.value = ""; 
263        this.myInputChar = ""; 
264 
265 
266        // hide all old chars 
267        this.hideChars(0); 
268     }, 
269 
270 
271     hideChars: function(start)
272     {
273        start = start || 0; 
274 
275        for (var i = this.myNumWordSelection; i >= start; i--)
276        {
277           var elementId = "fireinputIMEList_label" + (i+1);
278           if(document.getElementById(elementId))
279           {
280              var element = document.getElementById(elementId);
281              element.setAttribute("value",  "");
282              element.setAttribute("hiddenvalue", "");
283              element.setAttribute("hiddenkey", "");
284              element.setAttribute("hiddenword", "");
285              element.setAttribute("hiddeninputkey", "");
286           }
287        }
288     }, 
289 
290     getCharByMouse: function (event)
291     {
292        var clickTarget = event.target; 
293        var elementId = clickTarget.getAttribute("id"); 
294        return this.getCharByPos(elementId); 
295     }, 
296 
297     getCharByPos: function(i)
298     {
299        if(Fireinput.getInputBarStatus())
300        {
301           var elementName = "fireinputIMEList_label" + i;
302           if(/fireinputIMEList_label/.test(i))
303              elementName = i;
304 
305           var elementId = document.getElementById(elementName);
306           if(!elementId)
307              return null;
308 
309 
310           var value = elementId.getAttribute("hiddenvalue");
311           var key = elementId.getAttribute("hiddenkey");
312           var inputkey = elementId.getAttribute("hiddeninputkey");
313           var word = elementId.getAttribute("hiddenword");
314           if(value.length <= 0 || key.length <= 0 || word.length <= 0 ||inputkey.length <= 0)
315              return null;
316 
317           word = word.match(/[\D\.]+/g)[0];
318           return {inputkey: inputkey, key: key, value: value, word: word }; 
319        }
320 
321        return null; 
322 
323     }, 
324 
325     insertCharToComposer: function (event, i, cas)
326     {
327        if(event)
328        { 
329           event.preventDefault();
330           event.stopPropagation(); 
331        }
332 
333        var result = this.getCharByPos(i);
334        var composeWasEnabled = this.myComposeEnabled; 
335        // FireinputLog.debug(this, "result: " + result + ", this.myComposeEnabled:" + this.myComposeEnabled);
336        if(result)
337        {
338           FireinputComposer.addToPanel(cas, result); 
339           if(composeWasEnabled)
340           {
341              // move the focus to fireinputField 
342              FireinputUtils.setFocus(document.getElementById("fireinputField"));
343           }
344        } 
345     },
346  
347     insertCharToComposerByMouse: function (event)
348     {
349        var result = this.getCharByMouse(event); 
350 
351        // right click to launch search 
352        if(event.button == 2)
353        {
354           FireinputWebSearch.loadByMouse(result.value);
355           return; 
356        }
357 
358        if(!Fireinput.getCurrentIME().canComposeNew() || !FireinputComposer.hasSet())
359        {
360           this.insertCharToTargetByValue(result.value);
361           this.hideAndCleanInput(); 
362           return; 
363        }
364 
365        var composeWasEnabled = this.myComposeEnabled; 
366 
367        if(result)
368        {
369           FireinputComposer.addToPanel("false", result);
370        } 
371 
372        if(composeWasEnabled)
373        {
374           FireinputUtils.setFocus(document.getElementById("fireinputField"));
375        }
376    
377        var idf = document.getElementById("fireinputField");
378        if(idf.selectionEnd < idf.value.length)
379        {
380           var subInputKeys = idf.value.substring(idf.selectionEnd, idf.value.length); 
381           // update inputField value and caret position 
382           idf.value = subInputKeys; 
383           this.myInputChar = subInputKeys.substring(0, 1); 
384           FireinputUtils.setCaretTo(idf, 1); 
385           this.findCharWithDelay(); 
386        }
387        else 
388        { 
389           idf.value = ""; 
390           this.myInputChar = ""; 
391        }
392     }, 
393 
394     insertCharToTargetByValue: function (charstr)
395     {
396        FireinputUtils.insertCharAtCaret(this.myTarget, charstr);
397        // add into long table 
398        if(this.mySaveHistory)
399           FireinputLongTable.addIntoLongTable(this.myTarget.target,charstr);
400     }, 
401 
402     insertCharToTarget: function (event, target, i, hideInput, outputAll)
403     {
404        if(Fireinput.getInputBarStatus())
405        {
406           event.preventDefault();
407           event.stopPropagation(); 
408 
409           var elementName; 
410           if(/fireinputIMEList_label/.test(i))
411              elementName = i; 
412           else 
413              elementName = "fireinputIMEList_label" + i; 
414           var elementId = document.getElementById(elementName); 
415           if(!elementId || this.myInputChar.length <= 0 )
416           {
417              this.insertAllCharsToTarget(target, hideInput, null, outputAll); 
418              return; 
419           }
420 
421           var value = elementId.getAttribute("hiddenvalue");
422           var key = elementId.getAttribute("hiddenkey");
423           var word = elementId.getAttribute("hiddenword"); 
424           var ufreq = elementId.getAttribute("ufreq") == 'true' ? true : false; 
425           if(value.length <= 0 || key.length <= 0 || word.length <= 0)
426           {
427              this.insertAllCharsToTarget(target, hideInput, null, outputAll); 
428              return; 
429           }
430 
431           this.insertAllCharsToTarget(target, hideInput, {key: key, word: word, value: value, ufreq: ufreq}, outputAll); 
432        }
433     }, 
434 
435     insertAllCharsToTarget: function (target, hideInput, keyWordResult, outputAll)
436     {
437        if(Fireinput.getInputBarStatus())
438        {
439           var value = "";
440           var key = ""; 
441           var word = ""; 
442           var ufreq = true; 
443           var keyInField = this.myInputChar; 
444           if(keyWordResult)
445           {
446              value = keyWordResult.value; 
447              key = keyWordResult.key; 
448              word = keyWordResult.word; 
449              ufreq = keyWordResult.ufreq; 
450           }
451 
452           var insertValue = value; 
453           var composeWord = FireinputComposer.getComposeWord(); 
454           // if there is no composed word, and no valid word selection, and doesn't ask 
455           // to output the key in pinyin field, we just hide here 
456           if(composeWord.key.length <= 0 && key.length <= 0 && !outputAll)
457           {
458              var id = document.getElementById("fireinputIMEContainer");
459 	     id.hidePopup(); 
460              return; 
461           }
462 
463           // combine both composed and now value 
464           insertValue = composeWord.value + insertValue; 
465 
466           // hide the inputbar after everything is written 
467           if(hideInput)
468           {
469              var id = document.getElementById("fireinputIMEContainer");
470 	     id.hidePopup(); 
471              // also clear off the input bar 
472              var idf = document.getElementById("fireinputField");
473              idf.value = ""; 
474           }
475 
476           // reset inputChar
477           this.myInputChar = ""; 
478 
479 
480           // the outputAll is from just Enter press which just prints out the input key 
481           if(outputAll)
482           {
483              FireinputUtils.insertCharAtCaret(target, composeWord.ikey + keyInField); 
484              // no need to go further, just return here 
485              return; 
486           }
487           else 
488              FireinputUtils.insertCharAtCaret(target, insertValue);
489 
490           // keep the last selected element to insert repeatedly 
491           this.myLastSelectedElementValue = insertValue; 
492 
493           //FireinputLog.debug(this, "word: " + word + ", key: " + key + ", ufreq: " +  ufreq); 
494           // update the frequency or save as new word 
495           if(composeWord.key.length > 0)
496           {
497              var newPhraseArray = []; 
498              newPhraseArray.push({key: composeWord.key + " " + key, word: composeWord.word + word}); 
499              //FireinputLog.debug(this, "newPhraseArray: " + composeWord.key + " " + key + ", word: " +  composeWord.word + word); 
500              Fireinput.getCurrentIME().storeUserPhrase(newPhraseArray); 
501           }
502           else if(this.myUpdateFreq && ufreq)
503              Fireinput.getCurrentIME().updateFrequency(word, key);
504 
505           if(this.mySaveHistory)
506              FireinputLongTable.addIntoLongTable(target.target, insertValue);
507        }   
508     },
509 
510     IMEInputFieldMouseEvent: function(event)
511     {
512        if(event.button == 2)
513        {
514           event.preventDefault();
515           event.stopPropagation(); 
516           this.hideAndCleanInput(); 
517           return;   
518        }
519 
520        if(event.button != 0)
521           return; 
522     },
523 
524     IMEInputFieldFocusEvent: function(event)
525     {
526        // return if it's initially popup 
527        if(this.myIMEInputFieldFocusedStatus == false)
528        {
529           this.myIMEInputFieldFocusedStatus = true;
530           return; 
531        }
532 
533        // don't do anything if there is no input char 
534        var idf = document.getElementById("fireinputField");
535        if(idf.value.length <= 0)
536           return; 
537 //     why we need this ? 
538 //       this.findChar(); 
539     },
540 
541     IMEInputFieldOnInputEvent: function(event)
542     {
543        if(!this.myIMEInputFieldFocusedStatus)
544           return; 
545     }, 
546        
547     prevSel: function (homeFlag)
548     {
549        if(!this.canPrevSel())
550           return; 
551 
552        var idf = document.getElementById("fireinputField");
553        
554        // send to IME method to query the string 
555        var result = Fireinput.getCurrentIME().prev(homeFlag);
556        // FireinputLog.debug(this,"call prev, length: " + codeArray.length); 
557        if(!result || !result.charArray)
558           this.disableSelButton(true, false); 
559        else if(homeFlag || Fireinput.getCurrentIME().isBeginning())
560           this.disableSelButton(true, false); 
561        else
562           this.disableSelButton(false, false); 
563 
564        if(result && result.charArray)      
565           this.sendStringToPanel(result.charArray, result.validInputKey);
566        else 
567           this.sendStringToPanel(null, null);
568     },
569 
570     nextSel: function(endFlag)
571     {
572        if(!this.canNextSel())
573           return; 
574 
575        var idf = document.getElementById("fireinputField");
576 
577        // send to IME method to query the string 
578        var result = Fireinput.getCurrentIME().next(endFlag); 
579        // FireinputLog.debug(this,"call next, length: " + codeArray.length); 
580        if(!result || !result.charArray || result.charArray.length < this.myNumWordSelection)
581           this.disableSelButton(false, true);
582        else if (endFlag || Fireinput.getCurrentIME().isEnd())
583           this.disableSelButton(false, true);
584        else    
585           this.disableSelButton(false, false);
586 
587        if(result && result.charArray)      
588           this.sendStringToPanel(result.charArray, result.validInputKey);
589        else 
590           this.sendStringToPanel(null, null);
591     },
592 
593     disableSelButton: function(prevFlag, nextFlag)
594     {
595        var button = document.getElementById("fireinputNextSelButton");
596        if(nextFlag)
597           button.disabled = true;
598        else 
599           button.disabled =  ""; 
600 
601        button = document.getElementById("fireinputPrevSelButton");
602        if(prevFlag)
603           button.disabled = true; 
604        else
605           button.disabled = ""; 
606     }, 
607 
608     canPrevSel: function()
609     {
610        var button = document.getElementById("fireinputPrevSelButton");
611        return (!button.disabled); 
612     },
613 
614     canNextSel: function()
615     {
616        var button = document.getElementById("fireinputNextSelButton");
617        return (!button.disabled); 
618     },
619  
620     findCharWithDelay: function(delayMSec)
621     {
622        if(typeof(delayMSec) == 'undefined')
623          delayMSec = 100; 
624        if(this.myKeyTimer)
625          clearTimeout(this.myKeyTimer); 
626 
627        FireinputLog.debug(this, "findCharWithDelay: call findChar");
628        var self = this; 
629        this.myKeyTimer = setTimeout(function () { self.findChar(); }, delayMSec); 
630     },
631  
632     findChar: function()
633     {
634       
635        if(this.myInputChar.length <= 0)
636           return; 
637  
638        var singleWord = false; 
639        // force single word to be chosen for next look up 
640        if(FireinputComposer.hasSet())
641           singleWord = false; 
642 
643        FireinputLog.debug(this, "findChar: " + this.myInputChar);
644  
645        // send to IME method to query the string 
646        var result = Fireinput.getCurrentIME().find(this.myInputChar, singleWord, this.myInputKeyExactMatch);
647        this.sendStringToPanel(result.charArray, result.validInputKey);
648        if(!result.charArray || result.charArray.length < this.myNumWordSelection)
649           this.disableSelButton(true, true); 
650        else if (Fireinput.getCurrentIME().isEnd())
651           this.disableSelButton(true, true);
652        else
653           this.disableSelButton(true, false);
654       
655        if(!Fireinput.getCurrentIME().canComposeNew())
656           return; 
657 
658        FireinputLog.debug(this, "result.validInputKey: " + result.validInputKey);
659        FireinputLog.debug(this, "this.myInputChar: " + this.myInputChar);
660        if(result && result.charArray && result.charArray.length > 0 && 
661           this.myInputChar.length > result.validInputKey.length)
662        {
663           var newvalue = this.myInputChar.substr(result.validInputKey.length, this.myInputChar.length); 
664           this.insertCharToComposer(null, 1, "true");
665           var idf = document.getElementById("fireinputField");
666 	  FireinputLog.debug(this,"newvalue:" + newvalue + ", idf.value: " + idf.value + ", this.myInputChar: " + this.myInputChar);
667           idf.value = newvalue + idf.value.replace(this.myInputChar, ""); 
668           FireinputUtils.setCaretTo(idf, newvalue.length); 
669           this.myInputChar = newvalue; 
670           this.findChar(); 
671        }
672 
673        // FireinputLog.debug(this, "after findChar, this.myInputChar: " + this.myInputChar);
674 
675     },
676   
677     findCharWithKey: function(inputChar)
678     {
679        if(!inputChar || inputChar.length <= 0)
680           return; 
681 
682        //FireinputLog.debug(this, "Send key: inputChar: " + inputChar);
683  
684        // send to IME method to query the string 
685        var result = Fireinput.getCurrentIME().find(inputChar);
686        this.sendStringToPanel(result.charArray, result.validInputKey);
687        if(!result.charArray || result.charArray.length < this.myNumWordSelection)
688           this.disableSelButton(true, true); 
689        else if (Fireinput.getCurrentIME().isEnd())
690           this.disableSelButton(true, true);
691        else
692           this.disableSelButton(true, false);
693     }, 
694    
695 }; 


syntax highlighted by Code2HTML, v. 0.9.1