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 FireinputXPC =
38 {
39 CC: function(cName)
40 {
41 return Components.classes[cName];
42 },
43
44 CI: function(ifaceName)
45 {
46 return Components.interfaces[ifaceName];
47 },
48
49 getService: function(cName, ifaceName)
50 {
51 return this.CC(cName).getService(this.CI(ifaceName));
52 },
53
54 createInstance: function(cName, ifaceName)
55 {
56 return this.CC(cName).createInstance(this.CI(ifaceName));
57 },
58
59 QueryInterface: function(obj, iface)
60 {
61 return obj.QueryInterface(iface);
62 },
63
64 getIOService: function()
65 {
66 return this.getService("@mozilla.org/network/io-service;1", "nsIIOService");
67 }
68
69
70 };
71
72
73 var FireinputEnv =
74 {
75 getEnv: function(name)
76 {
77 var gEnv = FireinputXPC.getService("@mozilla.org/process/environment;1", "nsIEnvironment");
78 return gEnv.get(name);
79 },
80
81 getOS: function()
82 {
83 var gOS = FireinputXPC.getService("@mozilla.org/network/protocol;1?name=http", "nsIHttpProtocolHandler");
84 var oscpu = gOS.oscpu;
85 if(/linux/i.test(oscpu))
86 return "Linux";
87 if(/Mac/i.test(oscpu))
88 return "Mac";
89 if(/Windows/i.test(oscpu))
90 return "Windows";
91
92 return "Linux";
93 },
94
95 isLinux: function()
96 {
97 return this.getOS() == 'Linux';
98 },
99
100 isWindows: function()
101 {
102 return this.getOS() == 'Windows';
103 },
104
105 isMac: function()
106 {
107 return this.getOS() == 'Mac';
108 }
109
110 };
111
112
113 var FireinputPref =
114 {
115
116 getPref: function(name, type)
117 {
118 var gPref = FireinputXPC.getService("@mozilla.org/preferences-service;1", "nsIPrefBranch");
119 var prefName = prefDomain + "." + name;
120
121 if(type == 'undefined')
122 type = gPref.getPrefType(prefName);
123
124 if (type == "INT" || type == gPref.PREF_INT)
125 return gPref.getIntPref(prefName);
126 else if (type == "BOOL" || type == gPref.PREF_BOOL)
127 return gPref.getBoolPref(prefName);
128 else if(type == "STRING" || type == gPref.PREF_STRING)
129 return gPref.getCharPref(prefName);
130 else
131 return null;
132 },
133
134 setPref: function(name, type, value)
135 {
136 var gPref = FireinputXPC.getService("@mozilla.org/preferences-service;1", "nsIPrefBranch");
137 var prefName = prefDomain + "." + name;
138
139 if(type == 'undefined')
140 type = gPref.getPrefType(prefName);
141
142 if (type == "INT" || type == gPref.PREF_INT)
143 gPref.setIntPref(prefName, value);
144 else if (type == "BOOL" || type == gPref.PREF_BOOL)
145 gPref.setBoolPref(prefName, value);
146 else
147 gPref.setCharPref(prefName, value);
148 },
149
150 addObserver: function(aTopic, aOwnsWeak)
151 {
152 var gPref = FireinputXPC.getService("@mozilla.org/preferences-service;1", "nsIPrefBranch");
153 var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranch2);
154 pbi.addObserver(prefDomain, aTopic, aOwnsWeak);
155 }
156
157 };
158
159 var FireinputUnicode =
160 {
161
162 getUnicodeString: function(text, charset)
163 {
164 var gUnicode = FireinputXPC.getService("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
165 gUnicode.charset = charset ? charset : "UTF-8";
166 try
167 {
168 return gUnicode.ConvertToUnicode(text);
169 }
170 catch(ex)
171 {
172 return text;
173 }
174 },
175
176 convertFromUnicode: function(text, charset)
177 {
178 try
179 {
180 var gUnicode = FireinputXPC.getService("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
181 gUnicode.charset = charset ? charset : "UTF-8";
182 text = gUnicode.ConvertFromUnicode(text);
183 return text + gUnicode.Finish();
184 }
185 catch(ex)
186 {
187 return text;
188 }
189 }
190 };
191
192
193 var FireinputUtils =
194 {
195 debug: 0,
196
197 $STR: function(name)
198 {
199 return document.getElementById("strings_fireinput").getString(name);
200 },
201
202 $STRF: function(name, args)
203 {
204 return document.getElementById("strings_fireinput").getFormattedString(name, args);
205 },
206
207 loadURI: function(url)
208 {
209 if(gBrowser.currentURI.spec == 'about:blank' || gBrowser.currentURI.spec == '')
210 gBrowser.loadURI(url);
211 else
212 gBrowser.selectedTab = gBrowser.addTab(url);
213 },
214
215 setFocus: function(target)
216 {
217 try {
218 target.focus();
219 }
220 catch(e)
221 {
222 }
223 },
224
225 insertCharAtCaret: function (element, text, sourceType, insertMode)
226 {
227 if(!element)
228 return;
229
230 // text field. We can only insert bb code here
231 if(element.target.setSelectionRange)
232 {
233 // setFocus before set any values to avoid being reset by target onfocus event - normally happens to textfield or textarea
234 this.setFocus(element.target);
235
236 var start = element.selectionStart;
237 var end = element.selectionEnd;
238 var target = element.target;
239
240 // hack: the onfocus event will select all. We try to avoid this.
241 if(element.focused == 0 && start != end && end == target.value.length)
242 {
243 start = end;
244 }
245
246 var scrollTop = target.scrollTop;
247 var scrollLeft = target.scrollLeft;
248 var isBottom = (scrollTop + target.clientHeight) == target.scrollHeight;
249 if(typeof(sourceType) != 'undefined' && sourceType == IMAGE_SOURCE_TYPE)
250 {
251 if(insertMode == IMAGE_INSERT_BBCODE_URL)
252 text = "[img]" + text + "[/img]";
253 }
254
255 if(start != null)
256 {
257 target.value = target.value.substr(0, start)
258 + text + target.value.substr(end, target.value.length);
259 this.setCaretTo(target, start + text.length);
260 }
261 else
262 {
263 target.value += text;
264 this.setCaretTo(target, target.value.length);
265 }
266
267 if(typeof(scrollLeft) != "undefined")
268 target.scrollLeft = scrollLeft;
269 if(typeof(scrollTop) != "undefined")
270 {
271 if(!isBottom)
272 target.scrollTop = scrollTop;
273 else
274 target.scrollTop = target.scrollHeight;
275 }
276
277 // some target might have oninput event. Since the value is changed by script programmatically,
278 // this event won't fire
279 // Why do this way: the oninput won't show up as item of target. It must go to DOM tree to find
280 // the attribute. Unfortunately if it's added by addEventListener, it won't be executed
281 // The following code has security issue
282 /*
283 if(target.id != '')
284 {
285 var win = target.ownerDocument.defaultView;
286 while (win.parent != win)
287 win = window.parent;
288
289 if(win.document)
290 {
291 var node = win.document.getElementById(target.id);
292 if(node.hasAttribute("oninput"))
293 {
294 var f = node.getAttribute("oninput");
295 try {
296 eval(f);
297 } catch(e) { }
298 }
299 }
300 }
301 */
302 }
303 else if(element.documentTarget)
304 {
305 var win = element.target;
306 var doc = win.content.document;
307 var selection = win.getSelection();
308
309 if(!selection.focusNode)
310 return;
311
312 //FireinputLog.debug(this, "selection.focusNode.nodeType: " + selection.focusNode.nodeType);
313 //FireinputLog.debug(this, "selection.focusNode.name: " + selection.focusNode.nodeName);
314 //FireinputLog.debug(this, "selection.focusNode.value: " + selection.focusNode.nodeValue);
315 //FireinputLog.debug(this, "selection.focusOffset: " + selection.focusOffset);
316
317 // get the first range of the selection
318 // delete content if the collpase is not true
319 var range = selection.getRangeAt(0);
320 if(!range.collapsed)
321 range.deleteContents();
322
323 if (selection.focusNode.nodeType == Node.TEXT_NODE)
324 {
325
326 if(typeof(sourceType) != 'undefined' && sourceType == IMAGE_SOURCE_TYPE)
327 {
328 var imgNode = doc.createElement("img");
329 imgNode.setAttribute("src", text);
330
331 var startContainer = range.startContainer;
332 var startOffset = range.startOffset;
333 var beforeNode = startContainer.splitText(startOffset);
334 var parentNode = beforeNode.parentNode;
335 parentNode.insertBefore(imgNode, beforeNode);
336 selection.collapse(beforeNode, 0);
337 }
338 else
339 {
340 var endOffset = selection.focusOffset;
341 var nodeValue = selection.focusNode.nodeValue;
342
343 nodeValue = nodeValue.substr(0,endOffset) + text +
344 nodeValue.substr(endOffset, nodeValue.length);
345 selection.focusNode.nodeValue = nodeValue;
346 selection.collapse(selection.focusNode, endOffset+text.length);
347 }
348
349 }
350 else
351 {
352 this.insertAtSelection(selection, text, sourceType);
353 }
354 }
355 else
356 {
357 element.target.value += text;
358 }
359 },
360
361 setCaretTo: function (obj, pos)
362 {
363 if(obj.selectionStart)
364 {
365 // should turn on focus ?
366 // obj.focus();
367 obj.setSelectionRange(pos, pos);
368 }
369 },
370
371 getCaretPos: function(obj)
372 {
373 return obj.selectionEnd;
374 },
375
376 insertAtSelection: function(selection, text, sourceType)
377 {
378 var range = selection.getRangeAt(0);
379 var ancestorContainer = range.commonAncestorContainer;
380 var doc = ancestorContainer.ownerDocument;
381
382 var startContainer = range.startContainer;
383 var startOffset = range.startOffset;
384
385 try {
386 if (ancestorContainer == doc.body)
387 ancestorContainer = doc.documentElement;
388 } catch (e) { }
389
390
391 // each path is a "child sequence" (a.k.a. "tumbler") that
392 // descends from the ancestor down to the boundary point
393 var startPath = this.getPath(ancestorContainer, startContainer);
394
395 startContainer = ancestorContainer;
396
397 var canInsert = ancestorContainer.hasChildNodes();
398 if (canInsert)
399 {
400 var i;
401 for (i = startPath ? startPath.length-1 : -1; i >= 0; i--)
402 {
403 startContainer = startContainer.childNodes.item(startPath[i]);
404 }
405
406 var tmpNode = "";
407 if(typeof(sourceType) != 'undefined' && sourceType == IMAGE_SOURCE_TYPE)
408 {
409 tmpNode = doc.createElement("img");
410 tmpNode.setAttribute("src", text);
411 }
412 else
413 tmpNode = doc.createTextNode(text);
414
415 startContainer.insertBefore(tmpNode, startContainer.childNodes.item(startOffset));
416 selection.collapse(startContainer, startOffset+1);
417 }
418 },
419
420 getPath: function (ancestor, node)
421 {
422 var n = node;
423 var p = n.parentNode;
424 if (n == ancestor || !p)
425 return null;
426
427 var path = new Array();
428 if (!path)
429 return null;
430 do
431 {
432 for (var i = 0; i < p.childNodes.length; i++)
433 {
434 if (p.childNodes.item(i) == n)
435 {
436 path[path.length] = i;
437 break;
438 }
439 }
440 n = p;
441 p = n.parentNode;
442 } while (n != ancestor && p);
443
444 return path;
445 },
446
447 findPosX: function(obj)
448 {
449 var curleft = obj.offsetLeft;
450 if(obj.offsetParent)
451 {
452 var osParent = obj.offsetParent;
453 while(osParent)
454 {
455 curleft += osParent.offsetLeft;
456 osParent = osParent.offsetParent;
457 }
458 }
459 else if(obj.x)
460 curleft += obj.x;
461 return curleft;
462 },
463
464 findPosY: function(obj)
465 {
466 var curtop = obj.offsetTop;
467 if(obj.offsetParent)
468 {
469 var osParent = obj.offsetParent;
470 while(osParent)
471 {
472 curtop += osParent.offsetTop;
473 osParent = osParent.offsetParent;
474 }
475 }
476 else if(obj.y)
477 curtop += obj.y;
478 return curtop;
479 },
480
481 getDocumentScrollTop: function(document)
482 {
483 if(!document)
484 return 0;
485 return document.documentElement.scrollTop ||
486 (document.body ? document.body.scrollTop : 0);
487 },
488
489 getDocumentScrollLeft: function(document)
490 {
491 if(!document)
492 return 0;
493
494 return document.documentElement.scrollLeft ||
495 (document.body ? document.body.scrollLeft : 0);
496 },
497
498 getExtensionPath : function()
499 {
500 try
501 {
502 var chromeRegistry = FireinputXPC.getService("@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry");
503 var uri = FireinputXPC.createInstance("@mozilla.org/network/standard-url;1", "nsIURI");
504
505 uri.spec = "chrome://fireinput/content/";
506
507 var path = chromeRegistry.convertChromeURL(uri);
508 if (typeof(path) == "object") path = path.spec
509
510 if (path.length >= 4 && path.substr(0, 4) == "jar:")
511 {
512 path = path.substring(4); // remove "jar:" at the beginning
513 }
514
515 return path;
516 }
517 catch (e)
518 {
519 }
520
521 return undefined;
522 },
523
524 getAppRootPath: function()
525 {
526 var path = this.getExtensionPath();
527
528 path = path.substring(0, path.lastIndexOf("/extensions/fireinput@software"));
529 return path;
530 },
531
532 trimString: function(str)
533 {
534 return str.replace(/^\s+/g, '').replace(/\s+$/g, '');
535 },
536
537 getLocaleString: function (stringKey)
538 {
539 var stringBundle = FireinputXPC.getService("@mozilla.org/intl/stringbundle;1", "nsIStringBundleService");
540 var propertyBundle = "chrome://fireinput/locale/fireinput.properties";
541 var bundle = stringBundle.createBundle(propertyBundle);
542 var str = "";
543 try
544 {
545 str = bundle.GetStringFromName(stringKey);
546 }
547 catch (e)
548 {
549 }
550
551 return str;
552 },
553
554 getIMENameString: function(value)
555 {
556 var defaultLanguage = fireinputPrefGetDefault("interfaceLanguage");
557 switch(value)
558 {
559 case SMART_PINYIN:
560 default:
561 return this.getLocaleString('fireinput.pinyin.quan.label' + defaultLanguage);
562 break;
563 case ZIGUANG_SHUANGPIN:
564 return this.getLocaleString('fireinput.pinyin.shuang.ziguang.label' + defaultLanguage);
565 break;
566 case MS_SHUANGPIN:
567 return this.getLocaleString('fireinput.pinyin.shuang.ms.label' + defaultLanguage);
568 break;
569 case CHINESESTAR_SHUANGPIN:
570 return this.getLocaleString('fireinput.pinyin.shuang.chinesestar.label' + defaultLanguage);
571 break;
572 case SMARTABC_SHUANGPIN:
573 return this.getLocaleString('fireinput.pinyin.shuang.smartabc.label' + defaultLanguage);
574 break;
575 case WUBI_86:
576 return this.getLocaleString('fireinput.wubi86.label' + defaultLanguage);
577 break;
578 case WUBI_98:
579 return this.getLocaleString('fireinput.wubi98.label' + defaultLanguage);
580 break;
581 case CANGJIE_5:
582 return this.getLocaleString('fireinput.cangjie5.label' + defaultLanguage);
583 break;
584 }
585
586 return "";
587 },
588
589 isValidForDirectInput: function(str)
590 {
591 if(!str)
592 return false;
593 // check whether it's email
594 if(str.indexOf(".") > 0 && str.indexOf("@") > 0)
595 return true;
596
597 // check whether it's url
598 var reg = new RegExp("^[A-Za-z]+://[A-Za-z0-9-]+\.[A-Za-z0-9]+");
599 if(reg.test(str))
600 return true;
601
602 return false;
603 },
604
605 notify: function(aSubject, aTopic, aData)
606 {
607 var os = FireinputXPC.getService("@mozilla.org/observer-service;1", "nsIObserverService");
608 os.notifyObservers(aSubject, aTopic, aData);
609 return true;
610 }
611 };
612
613 var FireinputHash = function()
614 {
615 this.items = new Array();
616 this.length = 0;
617 };
618
619 FireinputHash.prototype =
620 {
621 foreach: function(callback)
622 {
623 for (var k in this.items)
624 {
625 if(callback) callback(k, this.items[k]);
626 }
627 },
628
629 removeItem: function(in_key)
630 {
631 var tmp_value;
632 if (typeof(this.items[in_key]) != 'undefined')
633 {
634 this.length--;
635 var tmp_value = this.items[in_key];
636 delete this.items[in_key];
637 }
638
639 return tmp_value;
640 },
641
642 getItem: function(in_key)
643 {
644 if (typeof(this.items[in_key]) == 'undefined')
645 return null;
646 else
647 return this.items[in_key];
648 },
649
650 setItem: function(in_key, in_value)
651 {
652 if (typeof(in_value) != 'undefined')
653 {
654 if (typeof(this.items[in_key]) == 'undefined')
655 {
656 this.length++;
657 }
658
659 this.items[in_key] = in_value;
660 }
661
662 return in_value;
663 },
664
665 hasItem: function(in_key) {
666 return typeof(this.items[in_key]) != 'undefined';
667 }
668 };
669
670 var Ajax = function() {};
671 Ajax.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
672
673 Ajax.prototype =
674 {
675 transport: null,
676
677 setOptions: function(options)
678 {
679 this.options =
680 {
681 method: 'post',
682 asynchronous: true,
683 contextType: 'application/x-www-form-urlencoded',
684 parameters: ''
685 }
686
687 extend(this.options, options || {})
688 },
689
690 responseIsSuccess: function()
691 {
692 return this.transport.status == undefined
693 || this.transport.status == 0
694 || (this.transport.status >= 200 && this.transport.status < 300);
695 },
696
697 responseIsFailure: function()
698 {
699 return !this.responseIsSuccess();
700 },
701
702 request: function(url)
703 {
704 this.transport = new XMLHttpRequest();
705
706 var parameters = this.options.parameters || '';
707 if (parameters.length > 0) parameters += '&_=';
708
709 try
710 {
711 this.url = url;
712 if (this.options.method == 'get' && parameters.length > 0)
713 this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
714
715 this.transport.open(this.options.method, this.url,
716 this.options.asynchronous);
717 if (this.options.asynchronous)
718 {
719 this.transport.onreadystatechange = bind(this.onStateChange, this);
720 setTimeout(bind(function() {this.respondToReadyState(1)}, this), 10);
721 }
722
723 this.setRequestHeaders();
724
725 var body = this.options.postBody ? this.options.postBody : parameters;
726 this.transport.send(this.options.method == 'post' ? body : null);
727
728 }
729 catch (e)
730 {
731 this.dispatchException(e);
732 }
733 },
734
735 setRequestHeaders: function()
736 {
737 var requestHeaders =
738 ['X-Requested-With', 'XMLHttpRequest',
739 'X-Prototype-Version', '1.0',
740 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
741
742 if (this.options.method == 'post')
743 {
744 requestHeaders.push('Content-type', this.options.contentType);
745
746 if (this.transport.overrideMimeType)
747 requestHeaders.push('Connection', 'close');
748 }
749
750 if (this.options.requestHeaders)
751 requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
752
753 for (var i = 0; i < requestHeaders.length; i += 2)
754 this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
755 },
756
757 onStateChange: function()
758 {
759 var readyState = this.transport.readyState;
760 if (readyState != 1)
761 this.respondToReadyState(this.transport.readyState);
762 },
763
764 header: function(name)
765 {
766 try {
767 return this.transport.getResponseHeader(name);
768 }
769 catch (e) {}
770
771 return null;
772 },
773
774 evalJSON: function()
775 {
776 try {
777 return eval('(' + this.header('X-JSON') + ')');
778 } catch (e) {}
779
780 return null;
781 },
782
783 evalResponse: function()
784 {
785 try {
786 return eval(this.transport.responseText);
787 }
788 catch (e)
789 {
790 this.dispatchException(e);
791 }
792
793 return null;
794 },
795
796
797 respondToReadyState: function(readyState)
798 {
799 var event = Ajax.Events[readyState];
800 var transport = this.transport, json = this.evalJSON();
801
802 if (event == 'Complete')
803 {
804 try {
805 (this.options['on' + this.transport.status]
806 || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
807 )(transport, json);
808 }
809 catch (e)
810 {
811 this.dispatchException(e);
812 }
813 if ((this.header('Content-type') || '').match(/^text\/javascript/i))
814 this.evalResponse();
815 }
816
817 try {
818 if(typeof(this.options['on' + event]) != "undefined")
819 (this.options['on' + event])(transport, json);
820 }
821 catch (e) {
822 this.dispatchException(e);
823 }
824
825 },
826 dispatchException: function(exception)
827 {
828 if(this.options.onException)
829 (this.options.onException)(this, exception);
830 }
831 };
832
833
syntax highlighted by Code2HTML, v. 0.9.1