// -*- java -*-
/*
Copyright (c) 2005 Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
Copyright (c) 2005 LShift Ltd. <query@lshift.net>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

///////////////////////////////////////////////////////////////////////////
// Simple utility methods that make working with Javascript more pleasant.
///////////////////////////////////////////////////////////////////////////

// Invokes its argument on each element of this, in order.
Array.prototype.foreach = function(f) {
    for (var index = 0; index < this.length; index++) {
        f(this[index]);
    }
};

// Invokes its argument on each element of this, in order,
// and collects the results into a fresh array.
Array.prototype.map = function(f) {
    var result = [];
    for (var index = 0; index < this.length; index++) {
        result.push(f(this[index]));
    }
    return result;
};

// Collects all elements of this that return true when given to the
// predicate f into a fresh array, and returns the new array.
Array.prototype.filter = function(f) {
    var result = [];
    for (var index = 0; index < this.length; index++) {
	if (f(this[index])) {
	    result.push(this[index]);
	}
    }
    return result;
};

// Searches for the first element of this that satisfies the predicate
// f.
Array.prototype.find = function(f) {
    for (var index = 0; index < this.length; index++) {
	if (f(this[index])) {
	    return(this[index]);
	}
    }
    return undefined;
};

// Same as foreach, but makes use of the prototype.js bind() method.
Array.prototype.foreachBind = function (x, f) {
    this.foreach(f.bind(x));
};

// Same as map, but makes use of the prototype.js bind() method.
Array.prototype.mapBind = function (x, f) {
    return this.map(f.bind(x));
};

// Returns a copy of this with leading and trailing whitespace removed.
String.prototype.trim=function(){
    return this.replace(/^\s*|\s*$/g,'');
};

// Interleaves this between each element of xs, and returns the result.
// For example: ', '.join([1, 2, 3]) ---> "1, 2, 3"
String.prototype.join = function (xs) {
    var result = '';
    var needSep = false;
    xs.foreachBind(this, function (x) {
		       if (needSep) result += this;
		       needSep = true;
		       result += x;
		   });
    return result;
};

// Returns a function that returns the "key" member of its argument.
// Useful as the argument to map() occasionally.
function getter(key) {
    return function (o) { return o[key]; };
}

// Converts any object with a length property into a real array.
// Useful when working with certain DOM objects.
function toArray(x) {
    if (typeof x.length == 'undefined') {
	throw {message: "Cannot convert to array: " + x.toString(),
		name: "UtilToArrayFailure"};
    }
    var result = [];
    for (var i = 0; i < x.length; i++) {
	result.push(x[i]);
    }
    return result;
}

// Borrowed from prototype.js: escapes HTML text, quoting "<", ">" and
// "&" among other things
function escapeHtml(x) {
  var n = document.createElement("div");
  n.appendChild(document.createTextNode(x));
  return n.innerHTML;
};

// Borrowed from prototype.js: unescapes HTML text, unquoting "<", ">"
// and "&" among other things
function unescapeHtml(x) {
  var n = document.createElement("div");
  n.innerHTML = x;
  return n.firstChild.textContent;
};

// Utility for building a human-readable representation of an
// exception object.
function exceptionToString(e) {
    if ((typeof e == typeof {}) && ('message' in e)) {
	return e.message + " (" + e.name + ")";
    } else {
	return e.toString();
    }
}

// Builds and returns an array containing all the key names of the
// object o.
function allKeys(o) {
    var result = [];
    for (x in o) { result.push(x); }
    return result;
}

///////////////////////////////////////////////////////////////////////////
// Methods for debug logging
//
// To use these, add a div with id="logArea" to your page.

var log_Pending = [];

function log(m) {
    var l = $("logArea");
    if (l) {
 	while (log_Pending.length > 0) {
 	    l.value += log_Pending.shift();
 	}
	l.value += m + "\n";
	l.scrollTop = l.scrollHeight; // not strictly correct, but close enough
    } else {
	log_Pending.push(m + "\n");
    }
};

function clearLog(m) {
    var l = $("logArea");
    if (l) {
        l.value = '';
    }
}

