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


syntax highlighted by Code2HTML, v. 0.9.1