extra: import at 3.0.1 beta 1
[mozilla-extra.git] / extensions / venkman / resources / content / venkman-utils.js
blobf5eea454e57df9b5673c045ea449d500b5073f2f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is mozilla.org code.
17  *
18  * The Initial Developer of the Original Code is
19  * New Dimensions Consulting, Inc.
20  * Portions created by the Initial Developer are Copyright (C) 1999
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *   Robert Ginda, rginda@ndcico.com, original author
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 var dumpln;
41 var dd;
43 const nsIBaseWindow         = Components.interfaces.nsIBaseWindow;
44 const nsIXULWindow          = Components.interfaces.nsIXULWindow;
45 const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
46 const nsIWebNavigation      = Components.interfaces.nsIWebNavigation;
47 const nsIDocShellTreeItem   = Components.interfaces.nsIDocShellTreeItem;
49 var utils = new Object();
51 if (typeof document == "undefined") /* in xpcshell */
53     dumpln = print;
55 else
57     if (typeof dump == "function")
58         dumpln = function (str) {dump (str + "\n");}
59     else if (jsenv.HAS_RHINO)
60     {
61         dumpln = function (str) {
62                      var out = java.lang.System.out;
63                      out.println(str); out.flush();
64                  }
65     }
66     else
67         dumpln = function () {} /* no suitable function */
70 if (DEBUG) {
71     var _dd_pfx = "";
72     var _dd_singleIndent = "  ";
73     var _dd_indentLength = _dd_singleIndent.length;
74     var _dd_currentIndent = "";
75     var _dd_lastDumpWasOpen = false;
76     var _dd_timeStack = new Array();
77     var _dd_disableDepth = Number.MAX_VALUE;
78     var _dd_currentDepth = 0;
79     dd = function _dd (str) {
80              if (typeof str != "string") {
81                  dumpln (str);
82              } else if (str[str.length - 1] == "{") {
83                  ++_dd_currentDepth;
84                  if (_dd_currentDepth >= _dd_disableDepth)
85                      return;
86                  if (str.indexOf("OFF") == 0)
87                      _dd_disableDepth = _dd_currentDepth;
88                  _dd_timeStack.push (new Date());
89                  if (_dd_lastDumpWasOpen)
90                      dump("\n");
91                  dump (_dd_pfx + _dd_currentIndent + str);
92                  _dd_currentIndent += _dd_singleIndent;
93                  _dd_lastDumpWasOpen = true;
94              } else if (str[0] == "}") {
95                  if (--_dd_currentDepth >= _dd_disableDepth)
96                      return;
97                  _dd_disableDepth = Number.MAX_VALUE;
98                  var sufx = (new Date() - _dd_timeStack.pop()) / 1000 + " sec";
99                  _dd_currentIndent = 
100                      _dd_currentIndent.substr (0, _dd_currentIndent.length -
101                                                _dd_indentLength);
102                  if (_dd_lastDumpWasOpen)
103                      dumpln (str + " " + sufx);
104                  else
105                      dumpln (_dd_pfx + _dd_currentIndent + str + " " + sufx);
106                  _dd_lastDumpWasOpen = false;
107              } else {
108                  if (_dd_currentDepth >= _dd_disableDepth)
109                      return;
110                  if (_dd_lastDumpWasOpen)
111                      dump ("\n");
112                  dumpln (_dd_pfx + _dd_currentIndent + str);
113                  _dd_lastDumpWasOpen = false;
114              }    
115          }
116 } else {
117     dd = function (){};
120 var jsenv = new Object();
121 jsenv.HAS_SECURITYMANAGER = ((typeof netscape == "object") &&
122                              (typeof netscape.security == "object"));
123 jsenv.HAS_XPCOM = ((typeof Components == "object") &&
124                    (typeof Components.classes == "object"));
125 jsenv.HAS_JAVA = (typeof java == "object");
126 jsenv.HAS_RHINO = (typeof defineClass == "function");
127 jsenv.HAS_DOCUMENT = (typeof document == "object");
129 /* Dumps an object in tree format, recurse specifiec the the number of objects
130  * to recurse, compress is a boolean that can uncompress (true) the output
131  * format, and level is the number of levels to intitialy indent (only useful
132  * internally.)  A sample dumpObjectTree (o, 1) is shown below.
134  * + parent (object)
135  * + users (object)
136  * | + jsbot (object)
137  * | + mrjs (object)
138  * | + nakkezzzz (object)
139  * | *
140  * + bans (object)
141  * | *
142  * + topic (string) 'ircclient.js:59: nothing is not defined'
143  * + getUsersLength (function) 9 lines
144  * *
145  */
146 function dumpObjectTree (o, recurse, compress, level)
148     var s = "";
149     var pfx = "";
151     if (typeof recurse == "undefined")
152         recurse = 0;
153     if (typeof level == "undefined")
154         level = 0;
155     if (typeof compress == "undefined")
156         compress = true;
157     
158     for (var i = 0; i < level; i++)
159         pfx += (compress) ? "| " : "|  ";
161     var tee = (compress) ? "+ " : "+- ";
163     for (i in o)
164     {
165         var t;
166         try
167         {
168             t = typeof o[i];
169         
170             switch (t)
171             {
172                 case "function":
173                     var sfunc = String(o[i]).split("\n");
174                     if (sfunc[2] == "    [native code]")
175                         sfunc = "[native code]";
176                     else
177                         sfunc = sfunc.length + " lines";
178                     s += pfx + tee + i + " (function) " + sfunc + "\n";
179                     break;
180                     
181                 case "object":
182                     s += pfx + tee + i + " (object) " + o[i] + "\n";
183                     if (!compress)
184                         s += pfx + "|\n";
185                     if ((i != "parent") && (recurse))
186                         s += dumpObjectTree (o[i], recurse - 1,
187                                              compress, level + 1);
188                     break;
189                     
190                 case "string":
191                     if (o[i].length > 200)
192                         s += pfx + tee + i + " (" + t + ") " + 
193                             o[i].length + " chars\n";
194                     else
195                         s += pfx + tee + i + " (" + t + ") '" + o[i] + "'\n";
196                     break;
197                     
198                 default:
199                     s += pfx + tee + i + " (" + t + ") " + o[i] + "\n";
200             }
201         }
202         catch (ex)
203         {
204             s += pfx + tee + i + " (exception) " + ex + "\n";
205         }
207         if (!compress)
208             s += pfx + "|\n";
210     }
212     s += pfx + "*\n";
213     
214     return s;
215     
218 function getService(contractID, iface)
220     var rv;
221     var cls = Components.classes[contractID];
223     if (!cls)
224         return null;
226     switch (typeof iface)
227     {
228         case "undefined":
229             rv = cls.getService();
230             break;
232         case "string":
233             rv = cls.getService(Components.interfaces[iface]);
234             break;
236         case "object":
237             rv = cls.getService(iface);
238             break;
240         default:
241             rv = null;
242             break;
243     }
245     return rv;
249 function safeHTML(str)
251     function replaceChars(ch)
252     {
253         switch (ch)
254         {
255             case "<":
256                 return "&lt;";
257             
258             case ">":
259                 return "&gt;";
260                     
261             case "&":
262                 return "&amp;";
263                     
264             case "'":
265                 return "&#39;";
266                     
267             case '"':
268                 return "&quot;";
269         }
271         return "?";
272     };
274     return String(str).replace(/[<>&"']/g, replaceChars);
277 function safeCSV(str)
279     function replaceChars(ch)
280     {
281         switch (ch)
282         {
283             case '"':
284                 return '""';
285         }
287         return "?";
288     }
290     return '"' + String(str).replace(/"/g, replaceChars) + '"';
293 function alert(msg, parent, title)
295     var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
296     var nsIPromptService = Components.interfaces.nsIPromptService;
297     var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
298     if (!parent)
299         parent = window;
300     if (!title)
301         title = MSG_ALERT;
302     ps.alert (parent, title, msg);
305 function confirm(msg, parent, title)
307     var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
308     var nsIPromptService = Components.interfaces.nsIPromptService;
309     var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
310     if (!parent)
311         parent = window;
312     if (!title)
313         title = MSG_CONFIRM;
314     return ps.confirm (parent, title, msg);
317 function prompt(msg, initial, parent, title)
319     var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
320     var nsIPromptService = Components.interfaces.nsIPromptService;
321     var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
322     if (!parent)
323         parent = window;
324     if (!title)
325         title = MSG_PROMPT;
326     var rv = { value: initial };
328     if (!ps.prompt (parent, title, msg, rv, null, {value: null}))
329         return null;
331     return rv.value;
334 function alertCheck(msg, checkMsg, checkVal, parent, title)
336     const PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
337     const nsIPromptService = Components.interfaces.nsIPromptService;
338     var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
339     if (!parent)
340         parent = window;
341     if (!title)
342         title = MSG_ALERT;
343     
344     var checkBoxWrapper = {value: !!checkVal};
345     ps.alertCheck(parent, title, msg, checkMsg, checkBoxWrapper);
346     return checkBoxWrapper.value;
350 function getChildById (element, id)
352     var nl = element.getElementsByAttribute("id", id);
353     return nl.item(0);
356 function openTopWin (url)
358     var window = getWindowByType ("navigator:browser");
359     if (window)
360     {
361         var base = getBaseWindowFromWindow (window);
362         if (base.enabled)
363         {
364             window.focus();
365             window._content.location.href = url;
366             return window;
367         }
368     }
370     return openDialog (getBrowserURL(), "_blank", "chrome,all,dialog=no", url);
372     
373 function getWindowByType (windowType)
375     const MEDIATOR_CONTRACTID =
376         "@mozilla.org/appshell/window-mediator;1";
377     const nsIWindowMediator  = Components.interfaces.nsIWindowMediator;
379     var windowManager =
380         Components.classes[MEDIATOR_CONTRACTID].getService(nsIWindowMediator);
382     return windowManager.getMostRecentWindow(windowType);
385 function htmlVA (attribs, href, contents)
387     if (!attribs)
388         attribs = {"class": "venkman-link", target: "_content"};
389     else if (attribs["class"])
390         attribs["class"] += " venkman-link";
391     else
392         attribs["class"] = "venkman-link";
394     if (typeof contents == "undefined")
395     {
396         contents = htmlSpan();
397         insertHyphenatedWord (href, contents);
398     }
399     
400     return htmlA (attribs, href, contents);
403 function insertHyphenatedWord (longWord, containerTag)
405     var wordParts = splitLongWord (longWord, MAX_WORD_LEN);
406     containerTag.appendChild (htmlWBR());
407     for (var i = 0; i < wordParts.length; ++i)
408     {
409         containerTag.appendChild (document.createTextNode (wordParts[i]));
410         if (i != wordParts.length)
411             containerTag.appendChild (htmlWBR());
412     }
415 function insertLink (matchText, containerTag)
417     var href;
418     var linkText;
419     
420     var trailing;
421     ary = matchText.match(/([.,]+)$/);
422     if (ary)
423     {
424         linkText = RegExp.leftContext;
425         trailing = ary[1];
426     }
427     else
428     {
429         linkText = matchText;
430     }
432     var ary = linkText.match (/^(\w[\w-]+):/);
433     if (ary)
434     {
435         if (!("schemes" in utils))
436         {
437             var pfx = "@mozilla.org/network/protocol;1?name=";
438             var len = pfx.length
440             utils.schemes = new Object();
441             for (var c in Components.classes)
442             {
443                 if (c.indexOf(pfx) == 0)
444                     utils.schemes[c.substr(len)] = true;
445             }
446         }
447         
448         if (!(ary[1] in utils.schemes))
449         {
450             insertHyphenatedWord(matchText, containerTag);
451             return;
452         }
454         href = linkText;
455     }
456     else
457     {
458         href = "http://" + linkText;
459     }
461     var anchor = htmlVA (null, href, "");
462     insertHyphenatedWord (linkText, anchor);
463     containerTag.appendChild (anchor);
464     if (trailing)
465         insertHyphenatedWord (trailing, containerTag);
466     
469 function insertQuote (matchText, containerTag, msgtype)
471     if (msgtype[0] == "#")
472     {
473         containerTag.appendChild(document.createTextNode(matchText));
474         return;
475     }
476     
477     if (matchText == "``")
478         containerTag.appendChild(document.createTextNode("\u201c"));
479     else
480         containerTag.appendChild(document.createTextNode("\u201d"));
483 /* length should be an even number >= 6 */
484 function abbreviateWord (str, length)
486     if (str.length <= length || length < 6)
487         return str;
489     var left = str.substr (0, (length / 2) - 1);
490     var right = str.substr (str.length - (length / 2) + 1);
492     return left + "..." + right;
495 function toBool (val)
497     switch (typeof val)
498     {
499         case "boolean":
500             return val;
501     
502         case "number":
503             return val != 0;
504             
505         default:
506             val = String(val);
507             /* fall through */
509         case "string":
510             return (val.search(/true|on|yes|1/i) != -1);
511     }
513     return null;
516 /* some of the drag and drop code has an annoying appetite for exceptions.  any
517  * exception raised during a dnd operation causes the operation to fail silently.
518  * passing the function through one of these adapters lets you use "return
519  * false on planned failure" symantics, and dumps any exceptions caught
520  * to the console. */
521 function Prophylactic (parentObj, fun)
523     function adapter ()
524     {
525         var ex;
526         var rv = false;
527         
528         try
529         {
530             rv = fun.apply (parentObj, arguments);
531         }
532         catch (ex)
533         {
534             dd ("Prophylactic caught an exception:\n" +
535                 dumpObjectTree(ex));
536         }
537         
538         if (!rv)
539             throw "goodger";
541         return rv;
542     };
543     
544     return adapter;
547 function argumentsAsArray (args, start)
549     if (typeof start == "undefined")
550         start = 0;
552     if (start >= args.length)
553         return null;
554     
555     var rv = new Array();
556     
557     for (var i = start; i < args.length; ++i)
558         rv.push(args[i]);
559     
560     return rv;
563 function splitLongWord (str, pos)
565     if (str.length <= pos)
566         return [str];
568     var ary = new Array();
569     var right = str;
570     
571     while (right.length > pos)
572     {
573         /* search for a nice place to break the word, fuzzfactor of +/-5, 
574          * centered around |pos| */
575         var splitPos =
576             right.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
578         splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
579         ary.push(right.substr (0, splitPos));
580         right = right.substr (splitPos);
581     }
583     ary.push (right);
585     return ary;
588 function wrapText (str, width)
590     var rv = "";
591     while (str.length > width)
592     {
593         rv += str.substr(0, width) + "\n";
594         str = str.substr(width);
595     }
596     return rv + str;
599 function wordCap (str)
601     if (!str)
602         return str;
603     
604     return str[0].toUpperCase() + str.substr(1);
608  * Clones an existing object (Only the enumerable properties
609  * of course.) use as a function..
610  * var c = Clone (obj);
611  * or a constructor...
612  * var c = new Clone (obj);
613  */
614 function Clone (obj)
616     var robj = new Object();
618     for (var p in obj)
619         robj[p] = obj[p];
621     return robj;
622     
625 function getXULWindowFromWindow (win)
627     var rv;
628     //dd ("getXULWindowFromWindow: before: getInterface is " + win.getInterface);
629     try
630     {
631         var requestor = win.QueryInterface(nsIInterfaceRequestor);
632         var nav = requestor.getInterface(nsIWebNavigation);
633         var dsti = nav.QueryInterface(nsIDocShellTreeItem);
634         var owner = dsti.treeOwner;
635         requestor = owner.QueryInterface(nsIInterfaceRequestor);
636         rv = requestor.getInterface(nsIXULWindow);
637     }
638     catch (ex)
639     {
640         rv = null;
641         //dd ("not a nsIXULWindow: " + formatException(ex));
642         /* ignore no-interface exception */
643     }
645     //dd ("getXULWindowFromWindow: after: getInterface is " + win.getInterface);
646     return rv;
649 function getBaseWindowFromWindow (win)
651     var rv;
652     //dd ("getBaseWindowFromWindow: before: getInterface is " + win.getInterface);
653     try
654     {
655         var requestor = win.QueryInterface(nsIInterfaceRequestor);
656         var nav = requestor.getInterface(nsIWebNavigation);
657         var dsti = nav.QueryInterface(nsIDocShellTreeItem);
658         var owner = dsti.treeOwner;
659         requestor = owner.QueryInterface(nsIInterfaceRequestor);
660         rv = requestor.getInterface(nsIBaseWindow);
661     }
662     catch (ex)
663     {
664         rv = null;
665         //dd ("not a nsIXULWindow: " + formatException(ex));
666         /* ignore no-interface exception */
667     }
669     //dd ("getBaseWindowFromWindow: after: getInterface is " + win.getInterface);
670     return rv;
673 function getSpecialDirectory(name)
675     if (!("directoryService" in utils))
676     {
677         const DS_CTR = "@mozilla.org/file/directory_service;1";
678         const nsIProperties = Components.interfaces.nsIProperties;
679     
680         utils.directoryService =
681             Components.classes[DS_CTR].getService(nsIProperties);
682     }
683     
684     return utils.directoryService.get(name, Components.interfaces.nsIFile);
687 function getPathFromURL (url)
689     var ary = url.match(/^(.*\/)([^\/?#]*)(\?|#|$)/);
690     if (ary)
691         return ary[1];
693     return url;
696 function getFileFromPath (path)
698     var ary = path.match(/\/([^\/?#;]+)(\?|#|$|;)/);
699     if (ary)
700         return ary[1];
702     return path;
705 function getURLSpecFromFile (file)
707     if (!file)
708         return null;
710     const IOS_CTRID = "@mozilla.org/network/io-service;1";
711     const LOCALFILE_CTRID = "@mozilla.org/file/local;1";
713     const nsIIOService = Components.interfaces.nsIIOService;
714     const nsILocalFile = Components.interfaces.nsILocalFile;
715     
716     if (typeof file == "string")
717     {
718         var fileObj =
719             Components.classes[LOCALFILE_CTRID].createInstance(nsILocalFile);
720         fileObj.initWithPath(file);
721         file = fileObj;
722     }
723     
724     var service = Components.classes[IOS_CTRID].getService(nsIIOService);
725     /* In sept 2002, bug 166792 moved this method to the nsIFileProtocolHandler
726      * interface, but we need to support older versions too. */
727     if ("getURLSpecFromFile" in service)
728         return service.getURLSpecFromFile(file);
730     var nsIFileProtocolHandler = Components.interfaces.nsIFileProtocolHandler;
731     var fileHandler = service.getProtocolHandler("file");
732     fileHandler = fileHandler.QueryInterface(nsIFileProtocolHandler);
733     return fileHandler.getURLSpecFromFile(file);
736 function getCommonPfx (list)
738     var pfx = list[0];
739     var l = list.length;
740     
741     for (var i = 1; i < l; i++)
742     {
743         for (var c = 0; c < pfx.length; c++)
744             if (pfx[c] != list[i][c])
745                 pfx = pfx.substr (0, c);
746     }
748     return pfx;
752 function renameProperty (obj, oldname, newname)
755     if (oldname == newname)
756         return;
757     
758     obj[newname] = obj[oldname];
759     delete obj[oldname];
760     
763 function newObject(contractID, iface)
765     if (!jsenv.HAS_XPCOM)
766         return null;
768     var obj = Components.classes[contractID].createInstance();
769     var rv;
771     switch (typeof iface)
772     {
773         case "string":
774             rv = obj.QueryInterface(Components.interfaces[iface]);
775             break;
777         case "object":
778             rv = obj.QueryInterface[iface];
779             break;
781         default:
782             rv = null;
783             break;
784     }
786     return rv;
787     
790 function keys (o)
792     var rv = new Array();
793     
794     for (var p in o)
795         rv.push(p);
797     return rv;
798     
801 function parseSections (str, sections)
803     var rv = new Object();
804     var currentSection;
806     for (var s in sections)
807     {
808         if (!currentSection)
809             currentSection = s;
810         
811         if (sections[s])
812         {
813             var i = str.search(sections[s]);
814             if (i != -1)
815             {
816                 rv[currentSection] = str.substr(0, i);
817                 currentSection = 0;
818                 str = RegExp.rightContext;
819                 str = str.replace(/^(\n|\r|\r\n)/, "");
820             }
821         }
822         else
823         {
824             rv[currentSection] = str;
825             str = "";
826             break;
827         }
828     }
830     return rv;
833 function replaceStrings (str, obj)
835     if (!str)
836         return str;
837     for (var p in obj)
838         str = str.replace(RegExp(p, "g"), obj[p]);
839     return str;
842 function stringTrim (s)
844     if (!s)
845         return "";
846     s = s.replace (/^\s+/, "");
847     return s.replace (/\s+$/, "");
850 function formatDateOffset (seconds, format)
852     seconds = Math.floor(seconds);
853     var minutes = Math.floor(seconds / 60);
854     seconds = seconds % 60;
855     var hours   = Math.floor(minutes / 60);
856     minutes = minutes % 60;
857     var days    = Math.floor(hours / 24);
858     hours = hours % 24;
860     if (!format)
861     {
862         var ary = new Array();
863         if (days > 0)
864             ary.push (days + " days");
865         if (hours > 0)
866             ary.push (hours + " hours");
867         if (minutes > 0)
868             ary.push (minutes + " minutes");
869         if (seconds > 0)
870             ary.push (seconds + " seconds");
872         format = ary.join(", ");
873     }
874     else
875     {
876         format = format.replace ("%d", days);
877         format = format.replace ("%h", hours);
878         format = format.replace ("%m", minutes);
879         format = format.replace ("%s", seconds);
880     }
881     
882     return format;
885 function arrayHasElementAt(ary, i)
887     return typeof ary[i] != "undefined";
890 function arraySpeak (ary, single, plural)
892     var rv = "";
893     
894     switch (ary.length)
895     {
896         case 0:
897             break;
898             
899         case 1:
900             rv = ary[0];
901             if (single)
902                 rv += " " + single;            
903             break;
905         case 2:
906             rv = ary[0] + " and " + ary[1];
907             if (plural)
908                 rv += " " + plural;
909             break;
911         default:
912             for (var i = 0; i < ary.length - 1; ++i)
913                 rv += ary[i] + ", ";
914             rv += "and " + ary[ary.length - 1];
915             if (plural)
916                 rv += " " + plural;
917             break;
918     }
920     return rv;
921     
924 function arrayOrFlag (ary, i, flag)
926     if (i in ary)
927         ary[i] |= flag;
928     else
929         ary[i] = flag;
932 function arrayAndFlag (ary, i, flag)
934     if (i in ary)
935         ary[i] &= flag;
936     else
937         ary[i] = 0;
940 function arrayContains (ary, elem)
942     return (arrayIndexOf (ary, elem) != -1);
945 function arrayIndexOf (ary, elem, start)
947     if (!ary)
948         return -1;
950     var len = ary.length;
952     if (typeof start == "undefined")
953         start = 0;
955     for (var i = start; i < len; ++i)
956     {
957         if (ary[i] == elem)
958             return i;
959     }
961     return -1;
963     
964 function arrayInsertAt (ary, i, o)
967     ary.splice (i, 0, o);
971 function arrayRemoveAt (ary, i)
974     ary.splice (i, 1);
978 function getRandomElement (ary)
980     var i = Math.floor (Math.random() * ary.length)
981         if (i == ary.length) i = 0;
983     return ary[i];
987 function zeroPad (num, decimals)
989     var rv = String(num);
990     var len = rv.length;
991     for (var i = 0; i < decimals - len; ++i)
992         rv = "0" + rv;
993     
994     return rv;
997 function leftPadString (str, num, ch)
999     var rv = "";
1000     var len = rv.length;
1001     for (var i = len; i < num; ++i)
1002         rv += ch;
1003     
1004     return rv + str;
1006     
1007 function roundTo (num, prec)
1009     return Math.round(num * Math.pow (10, prec)) / Math.pow (10, prec);
1012 function randomRange (min, max)
1015     if (typeof min == "undefined")
1016         min = 0;
1018     if (typeof max == "undefined")
1019         max = 1;
1021     var rv = (Math.floor(Math.round((Math.random() * (max - min)) + min )));
1022     
1023     return rv;
1027 function getStackTrace ()
1030     if (!jsenv.HAS_XPCOM)
1031         return "No stack trace available.";
1033     var frame = Components.stack.caller;
1034     var str = "<top>";
1036     while (frame)
1037     {
1038         var name = frame.name ? frame.name : "[anonymous]";
1039         str += "\n" + name + "@" + frame.lineNumber;
1040         frame = frame.caller;
1041     }
1043     return str;
1044     
1047 function getInterfaces (cls)
1049     if (!jsenv.HAS_XPCOM)
1050         return null;
1052     var rv = new Object();
1053     var e;
1055     for (var i in Components.interfaces)
1056     {
1057         try
1058         {
1059             var ifc = Components.interfaces[i];
1060             cls.QueryInterface(ifc);
1061             rv[i] = ifc;
1062         }
1063         catch (e)
1064         {
1065             /* nada */
1066         }
1067     }
1069     return rv;
1070     
1073 function makeExpression (items)
1075     function escapeItem (item, first)
1076     {
1077         // Numbers.
1078         if (item.match(/^[0-9]+$/i))
1079             return "[" + item + "]";
1080         // Words/other items that don't need quoting.
1081         if (item.match(/^[a-z_][a-z0-9_]*$/i))
1082             return (!first ? "." : "") + item;
1083         // Quote everything else.
1084         return "[" + item.quote() + "]";
1085     };
1086     
1087     var expression = escapeItem(items[0], true);
1088     
1089     for (var i = 1; i < items.length; i++)
1090         expression += escapeItem(items[i], false);
1091     
1092     return expression;
1095 function isinstance(inst, base)
1097     /* Returns |true| if |inst| was constructed by |base|. Not 100% accurate,
1098      * but plenty good enough for us. This is to work around the fix for bug
1099      * 254067 which makes instanceof fail if the two sides are 'from'
1100      * different windows (something we don't care about).
1101      */
1102     return (inst && base &&
1103             ((inst instanceof base) ||
1104              (inst.constructor && (inst.constructor.name == base.name))));