/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
(c) 2006-2008: Martin Stubenschrott
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/
liberator.util = { //{{{
Timer: function Timer(minInterval, maxInterval, callback)
{
let timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
this.doneAt = 0;
this.latest = 0;
this.notify = function (aTimer)
{
timer.cancel();
this.doneAt = Date.now() + minInterval;
this.latest = 0;
callback(this.arg);
}
this.tell = function (arg)
{
if (arg !== undefined)
this.arg = arg;
if (this.doneAt == -1)
timer.cancel();
else if (Date.now() >= this.doneAt)
return this.notify();
let timeout = minInterval;
if (this.latest)
timeout = Math.min(minInterval, this.latest - Date.now());
else
this.latest = Date.now() + maxInterval;
timer.initWithCallback(this, timeout, timer.TYPE_ONE_SHOT);
this.doneAt = -1;
}
this.reset = function ()
{
timer.cancel();
this.doneAt = 0;
}
this.flush = function ()
{
if (this.latest)
this.notify();
}
},
arrayIter: function (ary)
{
let length = ary.length;
for (let i = 0; i < length; i++)
yield ary[i];
},
blankDocument: function (bodyId)
{
let mainWindowID = liberator.config.mainWindowID || "main-window";
let fontSize = document.defaultView.getComputedStyle(document.getElementById(mainWindowID), null)
.getPropertyValue("font-size");
return 'data:application/xhtml+xml,' + encodeURI('' +
'' +
)
},
clip: function (str, length)
{
return str.length <= length ? str : str.substr(0, length - 3) + "...";
},
// TODO: use :highlight color groups
// if "processStrings" is true, any passed strings will be surrounded by " and
// any line breaks are displayed as \n
colorize: function (arg, processStrings)
{
var type = typeof arg;
// some objects like window.JSON or getBrowsers()._browsers need the try/catch
try
{
if (type == "number")
{
return {arg};
}
else if (type == "string")
{
if (processStrings)
arg = <>"{arg.replace(/\n/, "\\n")}">;
return {arg};
}
else if (type == "boolean")
{
return {arg};
}
else if (arg == null || arg == "undefined")
{
return {arg};
}
else if (type == "object" || type == "function")
{
// for java packages value.toString() would crash so badly
// that we cannot even try/catch it
if (/^\[JavaPackage.*\]$/.test(arg))
return <>[JavaPackage]>;
var str = arg.toString();
if (typeof str == "string") // can be "undefined"
return <>{str}>;
else
return <>undefined>;
}
}
catch (e)
{
return <>]]>>;
}
return <>]]>>;
},
copyToClipboard: function (str, verbose)
{
var clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
.getService(Components.interfaces.nsIClipboardHelper);
clipboardHelper.copyString(str);
if (verbose)
liberator.echo("Yanked " + str, liberator.commandline.FORCE_SINGLELINE);
},
escapeHTML: function (str)
{
// XXX: the following code is _much_ slower than a simple .replace()
// :history display went down from 2 to 1 second after changing
//
// var e = window.content.document.createElement("div");
// e.appendChild(window.content.document.createTextNode(str));
// return e.innerHTML;
return str.replace(/&/g, "&").replace(//g, ">");
},
escapeRegex: function (str)
{
return str.replace(/([\\{}()[\].?*+])/g, "\\$1");
},
// Flatten an array:
// [["foo", "bar"], ["baz"]] -> ["foo", "bar", "baz"]
flatten: function (ary)
{
if (ary.length == 0)
return [];
return Array.concat.apply(Array, ary);
},
formatBytes: function (num, decimalPlaces, humanReadable)
{
const unitVal = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
var unitIndex = 0;
var tmpNum = parseInt(num, 10) || 0;
var strNum = [tmpNum + ""];
if (humanReadable)
{
while (tmpNum >= 1024)
{
tmpNum /= 1024;
if (++unitIndex > (unitVal.length - 1))
break;
}
let decPower = Math.pow(10, decimalPlaces);
strNum = ((Math.round(tmpNum * decPower) / decPower) + "").split(".", 2);
if (!strNum[1])
strNum[1] = "";
while (strNum[1].length < decimalPlaces) // padd with "0" to the desired decimalPlaces)
strNum[1] += "0";
}
for (let u = strNum[0].length - 3; u > 0; u -= 3) // make a 10000 a 10,000
strNum[0] = strNum[0].substring(0, u) + "," + strNum[0].substring(u, strNum[0].length);
if (unitIndex) // decimalPlaces only when > Bytes
strNum[0] += "." + strNum[1];
return strNum[0] + " " + unitVal[unitIndex];
},
// generates an Asciidoc help entry, "command" can also be a mapping
generateHelp: function (command, extraHelp)
{
var start = "", end = "";
if (command instanceof liberator.Command)
start = ":";
else if (command instanceof liberator.Option)
start = end = "'";
var ret = "";
var longHelp = false;
if ((command.help && command.description) && (command.help.length + command.description.length) > 50)
longHelp = true;
// the tags which are printed on the top right
for (let j = command.names.length - 1; j >= 0; j--)
ret += "|" + start + command.names[j] + end + "| ";
if (longHelp)
ret += "+";
ret += "\n";
// the usage information for the command
var usage = command.names[0];
if (command.specs) // for :commands
usage = command.specs[0];
usage = usage.replace(/{/, "\\\\{").replace(/}/, "\\\\}");
usage = usage.replace(/'/, "\\'").replace(/`/, "\\`");
ret += "||" + start + usage + end + "||";
if (usage.length > 15)
ret += " +";
ret += "\n________________________________________________________________________________\n";
// the actual help text
if (command.description)
{
ret += command.description + "."; // the help description
if (extraHelp)
ret += " +\n" + extraHelp;
}
else
ret += "Sorry, no help available";
// add more space between entries
ret += "\n________________________________________________________________________________\n\n\n";
return ret;
},
highlightURL: function (str, force)
{
if (force || /^[a-zA-Z]+:\/\//.test(str))
return {str};
else
return str;
},
// if color = true it uses HTML markup to color certain items
objectToString: function (object, color)
{
/* Use E4X literals so html is automatically quoted
* only when it's asked for. Noone wants to see <
* on their console or :map :foo in their buffer
* when they expect :map :foo.
*/
XML.prettyPrinting = false;
if (object === null)
return "null";
if (typeof object != "object")
return false;
var string = "";
var obj = "";
try
{ // for window.JSON
obj = object.toString();
}
catch (e)
{
obj = "