// Copyright (c) Gate17 System www.gate17.net by Marc Krisnanto. All rights reserved.

// Type - until JS2 but probably keep anyway --------------------------------

function isUndefined  (o) { return typeof (o) == "undefined"; }
function isNull       (o) { return o === null; }
function isBoolean    (o) { return typeof (o) == "boolean"; }
function isNumber     (o) { return typeof (o) == "number" && isFinite (o); }
function isString     (o) { return typeof (o) == "string"; }
function isFunction   (o) { return typeof (o) == "function"; }
function isRealObject (o) { return o && !isUndefined (o.Class); }

function isArray (o)
{
	// XXX null is an object!
	if (isNull (o))
		return false;

	if (typeof (o) == "object")
	{
		if (!isNumber (o.length))
			return false;

		if (o.constructor && (o.constructor.toString().indexOf("Array") > -1 || o.constructor == Array))
			return true;
    }
	return false;
}


function isElement (o)
{
	return o.tagName ? true : false;
}


// Array - http://developer.mozilla.org -------------------------------------

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}


if (!Array.prototype.lastIndexOf)
{
  Array.prototype.lastIndexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]);
    if (isNaN(from))
    {
      from = len - 1;
    }
    else
    {
      from = (from < 0)
           ? Math.ceil(from)
           : Math.floor(from);
      if (from < 0)
        from += len;
      else if (from >= len)
        from = len - 1;
    }

    for (; from > -1; from--)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}

if (!Array.prototype.every)
{
  Array.prototype.every = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          !fun.call(thisp, this[i], i, this))
        return false;
    }

    return true;
  };
}


if (!Array.prototype.filter)
{
  Array.prototype.filter = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var res = new Array();
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
      {
        var val = this[i]; // in case fun mutates this
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }

    return res;
  };
}

if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        fun.call(thisp, this[i], i, this);
    }
  };
}


if (!Array.prototype.map)
{
  Array.prototype.map = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var res = new Array(len);
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        res[i] = fun.call(thisp, this[i], i, this);
    }

    return res;
  };
}


if (!Array.prototype.some)
{
  Array.prototype.some = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          fun.call(thisp, this[i], i, this))
        return true;
    }

    return false;
  };
}


Array.prototype.clear = function ()
{
	var a = this;
	while (a.length)
		a.pop ();
	return a;
};


Array.prototype.shallowCopy = function ()
{
	var r = [];
	var a = this;
	for (var i = 0; i < a.length; i++)
		r.push (a [i]);
	return r;
};


Array.prototype.contains = function (x)
{
	var n = this.length;
	for (var i = 0; i < n; i++)
	{
		if (this [i] == x)
			return true;
	}
	return false;
};


Array.prototype.sortByCol = function (i, dir, type)
{
	var prop = (type.charAt (0) == ".") ? type.substr (1) : null;

	this.sort (function (a, b)
		{
			a = a[i];
			b = b[i];
			switch (type)
			{
				case "int":
				case "i":
					a = parseInt (a);
					b = parseInt (b);
					break;

				case "float":
				case "f":
					a = parseFloat (a);
					b = parseFloat (b);
					break;

				case "date":
				case "d":
					if (isString (a))
						a = Date.parseString (a);
					if (isString (b))
						b = Date.parseString (b);
					a = Date.compare (a, b);
					return (a && dir) ? (a < 0 ? 1 : -1) : a;

				case "money":
				case "m":
					if (typeof (Currency) != "undefined")
					{
						if (Currency.BASE && Currency.EXRATETODAY)
						{
							a = Money (a).baseValue (Currency.BASE, Currency.EXRATETODAY);
							b = Money (b).baseValue (Currency.BASE, Currency.EXRATETODAY);
						}
					}
					break;

				case "object":
				case "o":
					a = ('name' in a) ? a.name : ('title' in a) ? a.title : a;
					b = ('name' in b) ? b.name : ('title' in b) ? b.title : b;
					break;

				default:
					if (prop)
					{
						a = a [prop];
						b = b [prop];
					}
					else if (isFunction (type))
					{
						a = type (a);
						b = type (b);
					}
			}			
			return dir ? (a > b ? -1 : (a < b ? 1 : 0)) : (a > b ? 1 : (a < b ? -1 : 0));
		});

	return this;
};



// String -------------------------------------------------------------------

String.prototype.htmlEscape = function ()
{
	var s = this;
	return s.replace(/&/g, '&amp;').replace (/>/g, '&gt;').replace(/</g, '&lt;');
};


String.prototype.xmlEscape = function ()
{
	var s = this;
	return s.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
  return s;
}

String.prototype.htmlUnescape = function ()
{
	var s = this;
	s = s.replace (/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
	return s;
};


if (!String.prototype.trim)
{
	String.prototype.trim = function ()
	{
		return this.replace (/^\s+/, '').replace (/\s+$/, '');
	};
}


if (!String.prototype.ltrim)
{
	String.prototype.ltrim = function ()
	{
		return this.replace (/^\s+/,'');
	};
}


if (!String.prototype.rtrim)
{
	String.prototype.rtrim = function ()
	{
		return this.replace (/\s+$/,'');
	};
}


String.prototype.tidy = function ()
{
	var s = this;
	return s.trim ().replace (/##|@@|\$\$|\^\^|~~|%%|&&/g, '.').replace(/\<\!\[CDATA\[/g, '.').replace(/\]\]\>/g, '.');
};


String.prototype.repeat = function (n)
{
	var s = "";
	while (n-- > 0)
		s += this;
	return s;
};


String.prototype.startsWith = function (v)
{
	v = v.toString ();
	var l = v.length;
	if (l < 1)
		return false;
	var s = this.toString ();
	return s.substr (0, l) == v;
};


String.prototype.endsWith = function (v)
{
	var s = this.toString ();
	v = v.toString ();
	var l = v.length;
	var n = s.length;
	if (l < 1 || l > n)
		return false;
	return s.substr (n - l) == v;
};


String.prototype.firstLine = function ()
{
	var s = this.replace (/^\s+/, '');
	var i = s.indexOf ("\n");
	return (i < 0) ? s : s.substr (0, i);
};


String.prototype.isDigit = function ()
{
	var d = "0123456789";
	var s = this;
	var l = s.length;
	if (l < 1)
		return false;
	for (var i = 0; i < l; i++)
	{
		if (d.indexOf (s.charAt (i)) < 0)
			return false;
	}
	return true;
};


String.prototype.isAlpha = function ()
{
	var d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	var s = this;
	var l = s.length;
	if (l < 1)
		return false;
	for (var i = 0; i < l; i++)
	{
		if (d.indexOf (s.charAt (i)) < 0)
			return false;
	}
	return true;
};


String.prototype.isAlnum = function ()
{
	var d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
	var s = this;
	var l = s.length;
	if (l < 1)
		return false;
	for (var i = 0; i < l; i++)
	{
		if (d.indexOf (s.charAt (i)) < 0)
			return false;
	}
	return true;
};


String.prototype.validChars = function (chars)
{
	var s = this;
	var l = s.length;
	for (var i = 0; i < l; i++)
	{
		if (chars.indexOf (s.charAt (i)) < 0)
			return false;
	}
	return true;
};


String.prototype.invalidChars = function (chars)
{
	var s = this;
	var l = chars.length;
	for (var i = 0; i < l; i++)
	{
		if (s.indexOf (chars.charAt (i)) >= 0)
			return true;
	}
	return false;
};


String.prototype.countChar = function (c)
{
	var s = this;
	var l = s.length;
	var n = 0;
	for (var i = 0; i < l; i++)
	{
		if (s.charAt (i) == c)
			n++;;
	}
	return n;
};


String.prototype.cutLength = function (n)
{
	var s = this;
	var l = s.length;
	return (l <= n) ? s : s.substr (0, n) + '..';
};


String.prototype.breakUp = function (n)
{
	var s = this;
	var a = s.trim().replace (/\s/g, ' ').split (/\s+/);
	var r = [];
	s = '';
	for (var i = 0; i < a.length; i++)
	{
		if (s.length >= n)
		{
			r.push (s.rtrim ());
			s = '';
		}
		s += a[i] + ' ';
	}
	s = s.rtrim ();
	if (s)
		r.push (s);
	return r;
};


String.prototype.toLines = function ()
{
	var s = this.trim ();
	if (s.indexOf ('\n') >= 0)
		return s.split (/\n/);
	return [];
};


String.prototype.fileExt = function ()
{
	// ...foo/bar/baz.tar.gz -> .tar.gz
	var s = this;
	s = s.split ('/');
	s = s [s.length - 1];
	var i = s.indexOf ('.');
	if (i < 0)
		return '';
	return s.substr (i);
};


String.prototype.fileName = function (noext)
{
	var s = this;
	s = s.split ('/');
	s = s [s.length - 1];
	var i = s.indexOf ('.');
	if (i < 0 || !noext)
		return s;
	return s.substr (0, i);
};


String.prototype.fileDir = function ()
{
	var s = this;
	s = s.split ('/');
	s.pop ();
	return s.join ('/');
};


String.prototype.vpctFormat = function (format)
{
	var s = this.toString ();
	return s.indexOf ('%') >= 0 ? s : Money (s).format (format || "t");
};


String.prototype.vpctToMoney = function (a)
{
	var s = this.toString ();
	if (s.indexOf ('%') < 0)
		return Money (s);
	return Money (a).__mul (s);
};


if (String.prototype.quote)
{
	String.prototype.toJs = String.prototype.quote;
}
else
{
	String.prototype.toJs = function ()
	{
		return this.toSource ().replace (/^.*\(/, '').replace (/\)\)$/,'');
	};
}



String.prototype.fixData = function ()
{
	var s = this;

	if (s.indexOf ('$$') >= 0)
		s = s.replace (/\$\$/g, '**');

	if (s.indexOf ('~~') >= 0)
		s = s.replace (/%%/g, '**');

	if (s.indexOf ('@@') >= 0)
		s = s.replace (/@@/g, '**');

	if (s.indexOf ('##') >= 0)
		s = s.replace (/##/g, '**');

	var c = s.substr (s.length - 1);
	if (c == '$' || c == '~' || c == '@' || c == '#')
		s = s.substr (0, s.length - 1);
	return s;
};




// Class - until JS2, simple class system hack ------------------------------

var Class = {};

Class.UNIQUE =
{
	Name        : 1,
	Class       : 1,
	prototype   : 1,
	constructor : 1
};


Class.mixInstance = function (cls, mix)
{
	if (arguments.length > 2)
	{
		for (var i = 2; i < arguments.length; i++)
		{
			p = arguments [i];
			if (p in Class.UNIQUE)
				continue;
			cls.prototype [p] = mix.prototype [p.charAt (0) == "$" ? p.substr (1) : p];
		}
	}
	else
	{
		for (var p in mix.prototype)
		{
			if (p in Class.UNIQUE)
				continue;
			cls.prototype [p] = mix.prototype [p];
		}
	}
};


Class.mixStatic = function (cls, mix /*, args*/)
{
	if (arguments.length > 2)
	{
		for (var i = 2; i < arguments.length; i++)
		{
			var p = arguments [i];
			if (p in Class.UNIQUE)
				continue;

			var g = mix [p.charAt (0) == "$" ? p.substr (1) : p];
			if (isFunction (g))
			{
				var f = function ()
				{
					var me = arguments.callee;
					return me.__f.apply (me.__c, arguments);
				};
				f.__c = cls;
				f.__f = g.__f || g;
				cls [p] = f;
			}
			else
			{
				cls [p] = g;
			}
		}
	}
	else
	{
		for (var p in mix)
		{
			if (p in Class.UNIQUE)
				continue;

			var g = mix [p.charAt (0) == "$" ? p.substr (1) : p];
			if (isFunction (g))
			{
				var f = function ()
				{
					var me = arguments.callee;
					return me.__f.apply (me.__c, arguments);
				};
				f.__c = cls;
				f.__f = g.__f || g;
				cls [p] = f;
			}
			else
			{
				cls [p] = g;
			}
		}
	}
};



//---------------------------------------------------------------------------
// assume JS' int can do BigInt

function BigInt (x)
{
	return parseInt (x);
}

