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