1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Mozilla Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/MPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is JSIRC Library
15
* The Initial Developer of the Original Code is New Dimensions Consulting,
16
* Inc. Portions created by New Dimensions Consulting, Inc. are
17
* Copyright (C) 1999 New Dimenstions Consulting, Inc. All
21
* Robert Ginda, rginda@ndcico.com, original author
24
* JavaScript utility functions.
26
* 1999-08-15 rginda@ndcico.com v1.0
29
var utils = new Object();
36
var _dd_singleIndent = " ";
37
var _dd_indentLength = _dd_singleIndent.length;
38
var _dd_currentIndent = "";
39
var _dd_lastDumpWasOpen = false;
40
var _dd_timeStack = new Array();
41
var _dd_disableDepth = Number.MAX_VALUE;
42
var _dd_currentDepth = 0;
43
dd = function _dd(str) {
44
if (typeof str != "string") {
46
} else if (str == "") {
47
dump("<empty-string>\n");
48
} else if (str[str.length - 1] == "{") {
50
if (_dd_currentDepth >= _dd_disableDepth)
52
if (str.indexOf("OFF") == 0)
53
_dd_disableDepth = _dd_currentDepth;
54
_dd_timeStack.push (new Date());
55
if (_dd_lastDumpWasOpen)
57
dump (_dd_pfx + _dd_currentIndent + str);
58
_dd_currentIndent += _dd_singleIndent;
59
_dd_lastDumpWasOpen = true;
60
} else if (str[0] == "}") {
61
if (--_dd_currentDepth >= _dd_disableDepth)
63
_dd_disableDepth = Number.MAX_VALUE;
64
var sufx = (new Date() - _dd_timeStack.pop()) / 1000 + " sec";
66
_dd_currentIndent.substr(0, _dd_currentIndent.length -
68
if (_dd_lastDumpWasOpen)
69
dump(str + " " + sufx + "\n");
71
dump(_dd_pfx + _dd_currentIndent + str + " " +
73
_dd_lastDumpWasOpen = false;
75
if (_dd_currentDepth >= _dd_disableDepth)
77
if (_dd_lastDumpWasOpen)
79
dump(_dd_pfx + _dd_currentIndent + str + "\n");
80
_dd_lastDumpWasOpen = false;
87
var jsenv = new Object();
88
jsenv.HAS_SECURITYMANAGER = ((typeof netscape == "object") &&
89
(typeof netscape.security == "object"));
90
jsenv.HAS_XPCOM = ((typeof Components == "object") &&
91
(typeof Components.classes == "object"));
92
jsenv.HAS_JAVA = (typeof java == "object");
93
jsenv.HAS_RHINO = (typeof defineClass == "function");
94
jsenv.HAS_DOCUMENT = (typeof document == "object");
95
jsenv.HAS_NSPR_EVENTQ = jsenv.HAS_DOCUMENT;
96
jsenv.HAS_STREAM_PROVIDER = ("nsIStreamProvider" in Components.interfaces);
98
function dumpObject (o, pfx, sep)
103
sep = (typeof sep == "undefined") ? " = " : sep;
104
pfx = (typeof pfx == "undefined") ? "" : pfx;
108
if (typeof (o[p]) != "function")
109
s += pfx + p + sep + o[p] + "\n";
111
s += pfx + p + sep + "function\n";
118
/* Dumps an object in tree format, recurse specifiec the the number of objects
119
* to recurse, compress is a boolean that can uncompress (true) the output
120
* format, and level is the number of levels to intitialy indent (only useful
121
* internally.) A sample dumpObjectTree (o, 1) is shown below.
127
* | + nakkezzzz (object)
131
* + topic (string) 'ircclient.js:59: nothing is not defined'
132
* + getUsersLength (function) 9 lines
135
function dumpObjectTree (o, recurse, compress, level)
140
if (typeof recurse == "undefined")
142
if (typeof level == "undefined")
144
if (typeof compress == "undefined")
147
for (var i = 0; i < level; i++)
148
pfx += (compress) ? "| " : "| ";
150
var tee = (compress) ? "+ " : "+- ";
168
var sfunc = String(o[i]).split("\n");
169
if (sfunc[2] == " [native code]")
170
sfunc = "[native code]";
172
if (sfunc.length == 1)
173
sfunc = String(sfunc);
175
sfunc = sfunc.length + " lines";
176
s += pfx + tee + i + " (function) " + sfunc + "\n";
180
s += pfx + tee + i + " (object)";
191
if ((i != "parent") && (recurse))
192
s += dumpObjectTree (o[i], recurse - 1,
193
compress, level + 1);
197
if (o[i].length > 200)
198
s += pfx + tee + i + " (" + t + ") " +
199
o[i].length + " chars\n";
201
s += pfx + tee + i + " (" + t + ") '" + o[i] + "'\n";
205
s += pfx + tee + i + " (" + t + ") ?\n";
209
s += pfx + tee + i + " (" + t + ") " + o[i] + "\n";
224
function ecmaEscape(str)
226
function replaceNonPrintables(ch)
228
var rv = ch.charCodeAt().toString(16);
231
else if (rv.length == 3)
233
else if (rv.length == 4)
239
// Replace any character that is not in the 69 character set
240
// [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./]
241
// with an escape sequence. Two digit sequences in the form %XX are used
242
// for characters whose codepoint is less than 255, %uXXXX for all others.
243
// See section B.2.1 of ECMA-262 rev3 for more information.
244
return str.replace(/[^A-Za-z0-9@*_+.\-\/]/g, replaceNonPrintables);
247
function ecmaUnescape(str)
249
function replaceEscapes(seq)
251
var ary = seq.match(/([\da-f]{1,2})(.*)|u([\da-f]{1,4})/);
258
// two digit escape, possibly with cruft after
259
rv = String.fromCharCode(parseInt(ary[1], 16)) + ary[2];
263
// four digits, no cruft
264
rv = String.fromCharCode(parseInt(ary[3], 16));
270
// Replace the escape sequences %X, %XX, %uX, %uXX, %uXXX, and %uXXXX with
271
// the characters they represent, where X is a hexadecimal digit.
272
// See section B.2.2 of ECMA-262 rev3 for more information.
273
return str.replace(/%u?([\da-f]{1,4})/ig, replaceEscapes);
276
function replaceVars(str, vars)
278
// replace "string $with a $variable", with
279
// "string " + vars["with"] + " with a " + vars["variable"]
281
function doReplace(symbol)
283
var name = symbol.substr(1);
290
return str.replace(/(\$\w[\w\d\-]+)/g, doReplace);
293
function formatException (ex)
295
if (ex instanceof Error)
296
return getMsg (MSG_FMT_JSEXCEPTION, [ex.name, ex.message, ex.fileName,
303
* Clones an existing object (Only the enumerable properties
304
* of course.) use as a function..
305
* var c = Clone (obj);
306
* or a constructor...
307
* var c = new Clone (obj);
311
var robj = new Object();
320
function Copy(source, dest, overwrite)
325
for (var p in source)
327
if (overwrite || !(p in dest))
335
* matches a real object against one or more pattern objects.
336
* if you pass an array of pattern objects, |negate| controls wether to check
337
* if the object matches ANY of the patterns, or NONE of the patterns.
339
function matchObject (o, pattern, negate)
341
negate = Boolean(negate);
343
function _match (o, pattern)
345
if (pattern instanceof Function)
351
/* nice to have, but slow as molases, allows you to match
352
* properties of objects with obj$prop: "foo" syntax */
356
p.substr(1,p.length).replace (/\$/g, "."));
361
if (pattern[p] instanceof Function)
363
if (!pattern[p](val))
368
var ary = (new String(val)).match(pattern[p]);
380
if (!(pattern instanceof Array))
381
return Boolean (negate ^ _match(o, pattern));
383
for (var i in pattern)
384
if (_match (o, pattern[i]))
391
function matchEntry (partialName, list)
394
if ((typeof partialName == "undefined") ||
395
(String(partialName) == ""))
398
var ary = new Array();
402
if (list[i].indexOf(partialName) == 0)
410
function encodeChar(ch)
412
return "%" + ch.charCodeAt(0).toString(16);
415
function escapeFileName(fileName)
417
return fileName.replace(/[^\w\d.,#\-_]/g, encodeChar);
420
function getCommonPfx (list)
425
for (var i = 0; i < l; i++)
427
for (var c = 0; c < pfx.length; ++c)
429
if (c >= list[i].length)
431
pfx = pfx.substr (0, c);
436
if (pfx[c] != list[i][c])
437
pfx = pfx.substr (0, c);
446
function openTopWin (url)
448
return openDialog (getBrowserURL(), "_blank", "chrome,all,dialog=no", url);
451
function getWindowByType (windowType)
453
const MEDIATOR_CONTRACTID =
454
"@mozilla.org/appshell/window-mediator;1";
455
const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
458
Components.classes[MEDIATOR_CONTRACTID].getService(nsIWindowMediator);
460
return windowManager.getMostRecentWindow(windowType);
463
function renameProperty (obj, oldname, newname)
466
if (oldname == newname)
469
obj[newname] = obj[oldname];
474
function newObject(contractID, iface)
476
if (!jsenv.HAS_XPCOM)
479
var obj = Components.classes[contractID].createInstance();
482
switch (typeof iface)
485
rv = obj.QueryInterface(Components.interfaces[iface]);
489
rv = obj.QueryInterface[iface];
501
function getContentWindow(frame)
505
if (!frame || !("contentWindow" in frame))
508
return frame.contentWindow;
512
// throws exception is contentWindow is gone
517
function getPriv (priv)
519
if (!jsenv.HAS_SECURITYMANAGER)
526
netscape.security.PrivilegeManager.enablePrivilege(priv);
530
dd ("getPriv: unable to get privlege '" + priv + "': " + e);
550
var rv = new Array();
559
function stringTrim (s)
563
s = s.replace (/^\s+/, "");
564
return s.replace (/\s+$/, "");
568
/* the offset should be in seconds, it will be rounded to 2 decimal places */
569
function formatDateOffset (offset, format)
571
var seconds = roundTo(offset % 60, 2);
572
var minutes = Math.floor(offset / 60);
573
var hours = Math.floor(minutes / 60);
574
minutes = minutes % 60;
575
var days = Math.floor(hours / 24);
580
var ary = new Array();
582
ary.push (getMsg(MSG_DAYS, days));
584
ary.push (getMsg(MSG_HOURS, hours));
586
ary.push (getMsg(MSG_MINUTES, minutes));
587
if (seconds > 0 || offset == 0)
588
ary.push (getMsg(MSG_SECONDS, seconds));
590
format = ary.join(", ");
594
format = format.replace ("%d", days);
595
format = format.replace ("%h", hours);
596
format = format.replace ("%m", minutes);
597
format = format.replace ("%s", seconds);
603
function arrayHasElementAt(ary, i)
605
return typeof ary[i] != "undefined";
608
function arrayContains (ary, elem)
610
return (arrayIndexOf (ary, elem) != -1);
613
function arrayIndexOf (ary, elem)
622
function arrayInsertAt (ary, i, o)
624
ary.splice (i, 0, o);
627
function arrayRemoveAt (ary, i)
632
/* length should be an even number >= 6 */
633
function abbreviateWord (str, length)
635
if (str.length <= length || length < 6)
638
var left = str.substr (0, (length / 2) - 1);
639
var right = str.substr (str.length - (length / 2) + 1);
641
return left + "..." + right;
645
* Inserts the string |hyphen| into string |str| every |pos| characters.
646
* If there are any wordbreaking characters in |str| within -/+5 characters of
647
* of a |pos| then the hyphen is inserted there instead, in order to produce a
650
function hyphenateWord (str, pos, hyphen)
652
if (str.length <= pos)
654
if (typeof hyphen == "undefined")
657
/* search for a nice place to break the word, fuzzfactor of +/-5, centered
660
str.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
662
splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
663
var left = str.substr (0, splitPos);
664
var right = hyphenateWord(str.substr (splitPos), pos, hyphen);
666
return left + hyphen + right;
670
* Like hyphenateWord, except individual chunks of the word are returned as
671
* elements of an array.
673
function splitLongWord (str, pos)
675
if (str.length <= pos)
678
var ary = new Array();
681
while (right.length > pos)
683
/* search for a nice place to break the word, fuzzfactor of +/-5,
684
* centered around |pos| */
686
right.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
688
splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
689
ary.push(right.substr (0, splitPos));
690
right = right.substr (splitPos);
698
function getRandomElement (ary)
701
return ary[Math.floor(Math.random() * ary.length)];
705
function roundTo (num, prec)
708
return Math.round(num * Math.pow (10, prec)) / Math.pow (10, prec);
712
function randomRange (min, max)
715
if (typeof min == "undefined")
718
if (typeof max == "undefined")
721
return Math.floor(Math.random() * (max - min + 1)) + min;
725
function getStackTrace ()
728
if (!jsenv.HAS_XPCOM)
729
return "No stack trace available.";
731
var frame = Components.stack.caller;
736
var name = frame.name ? frame.name : "[anonymous]";
737
str += "\n" + name + "@" + frame.lineNumber;
738
frame = frame.caller;
745
function getInterfaces (cls)
747
if (!jsenv.HAS_XPCOM)
750
var rv = new Object();
753
for (var i in Components.interfaces)
757
var ifc = Components.interfaces[i];
758
cls.QueryInterface(ifc);
772
* Calls a named function for each element in an array, sending
773
* the same parameter each call.
775
* @param ary an array of objects
776
* @param func_name string name of function to call.
777
* @param data data object to pass to each object.
779
function mapObjFunc(ary, func_name, data)
782
* WARNING: Caller assumes resonsibility to verify ary
787
ary[i][func_name](data);
791
* Passes each element of an array to a given function object.
793
* @param func a function object.
794
* @param ary an array of values.
796
function map(func, ary) {
799
* WARNING: Caller assumnes responsibility to verify
808
function getSpecialDirectory(name)
810
if (!("directoryService" in utils))
812
const DS_CTR = "@mozilla.org/file/directory_service;1";
813
const nsIProperties = Components.interfaces.nsIProperties;
815
utils.directoryService =
816
Components.classes[DS_CTR].getService(nsIProperties);
819
return utils.directoryService.get(name, Components.interfaces.nsIFile);
822
function getFileFromURLSpec(url)
824
const FILE_CTRID = "@mozilla.org/network/protocol;1?name=file";
825
const nsIFileProtocolHandler = Components.interfaces.nsIFileProtocolHandler;
827
var handler = Components.classes[FILE_CTRID].createInstance();
828
handler = handler.QueryInterface(nsIFileProtocolHandler);
829
return handler.getFileFromURLSpec(url);
832
function getURLSpecFromFile (file)
837
const IOS_CTRID = "@mozilla.org/network/io-service;1";
838
const LOCALFILE_CTRID = "@mozilla.org/file/local;1";
840
const nsIIOService = Components.interfaces.nsIIOService;
841
const nsILocalFile = Components.interfaces.nsILocalFile;
843
if (typeof file == "string")
846
Components.classes[LOCALFILE_CTRID].createInstance(nsILocalFile);
847
fileObj.initWithPath(file);
851
var service = Components.classes[IOS_CTRID].getService(nsIIOService);
852
/* In sept 2002, bug 166792 moved this method to the nsIFileProtocolHandler
853
* interface, but we need to support older versions too. */
854
if ("getURLSpecFromFile" in service)
855
return service.getURLSpecFromFile(file);
857
var nsIFileProtocolHandler = Components.interfaces.nsIFileProtocolHandler;
858
var fileHandler = service.getProtocolHandler("file");
859
fileHandler = fileHandler.QueryInterface(nsIFileProtocolHandler);
860
return fileHandler.getURLSpecFromFile(file);
863
function alert(msg, parent, title)
865
var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
866
var nsIPromptService = Components.interfaces.nsIPromptService;
867
var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
872
ps.alert (parent, title, msg);
875
function confirm(msg, parent, title)
877
var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
878
var nsIPromptService = Components.interfaces.nsIPromptService;
879
var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
884
return ps.confirm (parent, title, msg);
887
function prompt(msg, initial, parent, title)
889
var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
890
var nsIPromptService = Components.interfaces.nsIPromptService;
891
var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
896
rv = { value: initial };
898
if (!ps.prompt (parent, title, msg, rv, null, {value: null}))
904
function promptPassword(msg, initial, parent, title)
906
var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
907
var nsIPromptService = Components.interfaces.nsIPromptService;
908
var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
913
rv = { value: initial };
915
if (!ps.promptPassword (parent, title, msg, rv, null, {value: null}))
921
function getHostmaskParts(hostmask)
924
// A bit cheeky this, we try the matches here, and then branch
925
// according to the ones we like.
926
var ary1 = hostmask.match(/(\S*)!(\S*)@(.*)/);
927
var ary2 = hostmask.match(/(\S*)@(.*)/);
928
var ary3 = hostmask.match(/(\S*)!(.*)/);
930
rv = { nick: ary1[1], user: ary1[2], host: ary1[3] };
932
rv = { nick: "*", user: ary2[1], host: ary2[2] };
934
rv = { nick: ary3[1], user: ary3[2], host: "*" };
936
rv = { nick: hostmask, user: "*", host: "*" };
937
// Make sure we got something for all fields.
944
// And re-construct the 'parsed' hostmask.
945
rv.mask = rv.nick + "!" + rv.user + "@" + rv.host;