/*
 *  Standard JavaScript © 1996-2012, Horus Web Engineering Ltd
 *
 *  $Id: horus.js,v 1.308 2012-01-26 15:16:11 horus Exp $
 *
 *  licensed under the terms of the GNU Lesser General Public License:
 *    http://www.opensource.org/licenses/lgpl-license.php
 *
 */

if (!window.horus) window.horus={ window: window };

// unreliable browser detects... don't use for anything critical
//
horus.opera=window.opera || navigator.userAgent.indexOf('Opera')>=0;

horus.khtml=
  !horus.opera &&
  (navigator.userAgent.indexOf('Safari')>=0 || navigator.userAgent.indexOf('KHTML')>=0);

horus.webkit=
  horus.khtml && navigator.userAgent.indexOf('Webkit')>=0 ?
  navigator.userAgent.replace(/^.*Webkit\/(\d+\.\d+).*$/, '$1') : false;

horus.gecko=!horus.opera && !horus.khtml && navigator.userAgent.indexOf('Gecko')>=0;

horus.firefox=
  horus.gecko && navigator.userAgent.indexOf('Firefox')>=0 ?
  Number(navigator.userAgent.replace(/^.*Firefox\/(\d+\.\d+).*$/, '$1')) : 0;

horus.ie=!horus.opera && document.all;
horus.iefix=[];

if (horus.ie) {
  horus.iewin=navigator.userAgent.indexOf('Windows')>=0;
  horus.iemac=!horus.iewin;
  horus.ie=navigator.userAgent.match(/MSIE (\d+\.\d+)/);
  if (horus.ie) horus.ie=horus.ie[1];
  if (horus.ie) horus.ie=Number(horus.ie);
  horus.ieold=horus.iewin && horus.ie<7;
  horus.brokenDOM=horus.iewin && horus.ie<8;
  horus.noCSS3=horus.iewin && horus.ie<9;
} else
  horus.iewin=horus.iemac=horus.ieold=horus.brokenDOM=horus.noCSS3=false;

horus.$goCheck=false;	// kludgearound for IE/Win and Opera weirdness


horus.script={
  fn:
    function ( script ) {
      if (script.indexOf('.')<0) script=script+'.js';
      if (script.charAt(0)!='/' && script.indexOf(':')<0) script='/common/js/'+script;
      return script;
    },

  wrap:
    function ( fnin, scriptin ) {
      var script=horus.script.fn(scriptin);
      var fn=fnin;
      var wrap=window;
      var component;
      var point=fn.indexOf('.');

      if (point>=0) {
	if (point==0) fn='horus'+fn;
	fn=fn.split('.');

	while (fn.length>1) {
	  component=fn.shift();
	  wrap=wrap[component];
	}

	fn=fn[0];
      }

      wrap[fn]=
        function () {
	  return horus.script.autoloader(script, wrap, fn, arguments, this);
        };

    },

  loaded:
    function ( script, state ) {
      script=horus.script.fn(script);
      if (state==null) state=2;
      var oldstate=horus.script.state[script];
      if (oldstate==null) oldstate=0;
      if (oldstate>=state) return oldstate;
      horus.script.state[script]=state;

      if (state==2) {
	var pending=horus.script.pending[script];

	if (pending) {
	  delete horus.script.pending[script];

	  while (pending.length) {
	    var action=pending.shift();
	    action.wrap[action.fn].apply(action.object, action.argv);
	  }
	}
      }

      return null;
    },

  loading:
    function ( debug ) {
      var loading=[];

      for (var script in horus.script.state)
	if (horus.script.state[script]==1) loading.push(script);

      if (!loading.length)
	loading=null;
      else if (debug)
	alert(debug+' [ '+loading.join(', ')+' ]');

      return loading;
    },

  loadOne:
    function ( script ) {
      if (horus.script.loaded(script, 1)) return;

      if (horus.script.broken) {
	document.writeln('<script type="text/javascript" src="'+script+'"></script>');	 
	horus.script.loaded(script);
      } else {
	var s=document.createElement('script');
	s.type='text/javascript';
	s.src=script;
	s.async=true;

	if (horus.brokenDOM)
	  s.onreadystatechange=
	    function () {
	      if (this.readyState=='loaded' || this.readyState=='complete')
		horus.script.loaded(script);

	    };

	else
	  s.onload=function () { horus.script.loaded(script) };

	document.head.appendChild(s);
      }
    },

  loader:
    function ( argv, offset ) {
      for (var i=offset; i<argv.length; i++)
	horus.script.loadOne(horus.script.fn(argv[i]));

    },

  isLoaded: function ( script ) { return horus.script.loaded(script, 0) },
  load:     function () { horus.script.loader(arguments, 0) },
  loadIf:   function ( condition ) { if (condition) horus.script.loader(arguments, 1) },

  autoloader:
    function ( script, wrap, fn, argv, object ) {
      script=horus.script.fn(script);
      if (!object) object=window;
      if (horus.script.isLoaded(script)==2) return wrap[fn].apply(object, argv);
      if (!horus.script.pending[script]) horus.script.pending[script]=[];

      horus.script.pending[script].push
        ({ wrap: wrap, fn: fn, argv: argv, object: object });

      horus.script.loadOne(script);
      return false;
    },

  autoload:
    function () {
      if (!document.head) document.head=document.getElementsByTagName('head')[0];

      for (var a=0; a<arguments.length; a+=2) {
	var script=arguments[a];

	if (horus.script.broken)
	  horus.script.load(script);
	else {
	  var fn=arguments[a+1];

	  if (fn instanceof Array)
	    for (var f=0; f<fn.length; horus.script.wrap(fn[f++], script));
	  else
	    horus.script.wrap(fn, script);

	}
      }
    },

  broken: horus.ieold,
  state: {},
  pending: {}
};

horus.script.autoload
  ('btt',         '.btt',
   'call',        [ '.call', '.setTimeout' ],
   'controls',    '.appendControl',
   'defer',       [ '.defer', '.defer.assign' ],
   'dom',         [ '.insertChild', '.childText', '.replaceHTML', '.setClass',
		    '.addClass', '.removeClass' ],
   'geometry',    [ '.scrollTo', '.scroller' ],
   'matchheight', [ '.matchHeight', '.matchHeight.lock', '.matchHeight.unlock' ],
   'selshift',    [ '.seladd', '.seladd.insert', '.selshift' ],
   'testurl',     '.testurl',
   'trace',       [ '.trace', '.alert', '.alertIf', '.timer', '.counter' ],
   'visible',     [ '.visibility', '.visible', '.visible.put' ],
   'xmlhttp',     '.ajax');


if (horus.iewin) {
  // fix broken getElementById method
  //
  horus.script.loadIf(!document.ELEMENT_NODE, 'ie-node-types.js');
  horus.script.loadIf(horus.brokenDOM, 'ie-getelement-fix.js');
  horus.script.loadIf(horus.brokenDOM, 'ie-dom-fixes');

  // kludge in PNG alpha transparency for 5.5 <= IE/Win < 7
  //
  horus.script.loadIf(horus.ie>=5.5 && horus.ie<7, 'pngfix.js');

  // min-width hack code for ie<7
  //
  horus.script.loadIf(horus.ie<7, 'minwidth.js');

  // Chris Ridings' Google Autolink blocker
  //
  horus.script.load('autoblink.js');
}

// form/page processing symbolic constants as per Perl and ColdFusion
//
horus.FORM_ENTRY    = 0;
horus.FORM_SUBMIT   = -1;
horus.FORM_REFRESH  = -2;
horus.FORM_RESET    = -3;
horus.FORM_NEW      = -4;
horus.FORM_ADD      = -5;
horus.FORM_DELETE   = -6;
horus.FORM_EDIT     = -7;
horus.FORM_NEXT     = -8;
horus.FORM_PREVIOUS = -9;
horus.FORM_SAVE     = -10;
horus.FORM_RESTORE  = -11;
horus.FORM_PAYMENT  = -12;

horus.HTML_FOCUS    = 0x01;
horus.HTML_SCROLL   = 0x02;
horus.HTML_WAITBOX  = 0x04;
horus.HTML_NOBACK   = 0x08;
horus.HTML_VISIBLE  = 0x10;


horus.NBSP = '\u00a0';
horus.NUL  = '\u2400';
horus.BR   = [ 'br' ];


Function.prototype.horus$standardApply=Function.prototype.apply;

Function.prototype.apply=
  function () {
    var argv=arguments[0];
    var obj;

    if (arguments.length==1 && horus.isArray(argv))
      obj=window;
    else {
      obj=argv;
      argv=arguments[1];
    }

    return argv ? this.horus$standardApply(obj, argv) : this.horus$standardApply(obj);
  };


// fix (IE: totally, Safari: partially) broken String split method
//
if ('ab'.split(/a(.)/).length<3) {
  String.prototype.horus$brokenSplit=String.prototype.split;

  String.prototype.split=
    function(pattern, limit) {
      var split;

      if (!(pattern instanceof RegExp && '('.test(pattern)))
	split=this.horus$brokenSplit(pattern, limit);
      else {
	var str=this;
	split=[];

	while (str.length>0) {
	  var point=str.search(pattern);
	  if (point<0) { split.push(str); break }
	  split.push(str.substring(0, point));
	  var snip=str.match(pattern);
	  str=str.substring(point+snip[0].length);
	  snip.shift();
	  while (snip.length>0) split.push(snip.shift());
	}
      }

      if (split.length) {
	if (limit && limit<split.length) split=split.slice(0, limit);

	if (typeof split[0]=='object')
	  for (var i=0; i<split.length; i++) split[i]=split[i].toString();

      }

      return split;
    };

}


String.prototype.horus$standardSplit=String.prototype.split;

String.prototype.split=
  function(pattern, limit, options) {
    if (options==undefined && typeof limit!='number') {
      options=limit;
      limit=undefined;
    }

    var opt={};

    if (options)
      switch (typeof options) {

      case 'boolean':
	opt.sensible=options;
        break;

      case 'string':
	if (options.indexOf(',')>=0)
	  options=options.horus$standardSplit(/, */);
	else
	  options=[ options ];

	for (var i=0; i<options.length; i++) {
	  var option=options[i];
	    
	  if (option.slice(0, 2)=='no')
	    opt[option.slice(-2)]=false;
	  else
	    opt[option]=true;

	}

	break;

      case 'object':
	for (var tag in options) opt[tag]=options[tag];

      }

    if (opt.trim!=null && opt.sensible==null) opt.sensible=true;
    var split;

    if (this.length==0)
      split=opt.sensible ? [] : [ '' ];
    else if (!limit || !opt.sensible && limit>0)
      split=this.horus$standardSplit(pattern, limit);
    else {
      split=this.horus$standardSplit(pattern);
      var right=limit<0;
      if (right) limit=-limit;

      if (split.length>limit)
	if (right && !opt.sensible)
	  split.splice(0, split.length-limit);
	else if (pattern instanceof RegExp) {
	  var match=pattern.exec(this);
	  var substrings=match.length;
	  var matches=(split.length-1)/substrings;

	  if (matches>limit) {
	    var point=match.index;
	    var keep=(limit-1)*substrings;
	    split.splice(right ? 0 : keep, split.length-keep);

	    if (right) {
	      for (var i=0; i<matches-limit+1; i++) {
		point+=match[0].length;
		match=pattern.exec(this.substring(point));
		point+=match.index;
	      }

	      split.unshift(this.substring(0, point));
	    } else {
	      point+=match[0].length;

	      for (var i=0; i<limit-2; i++) {
		match=pattern.exec(this.substring(point));
		point+=match.index+match[0].length;
	      }

	      split.push(this.substring(point));
	    }
	  }
	} else
	  if (right)
	    split.unshift(split.splice(0, split.length-limit+1).join(pattern));
	  else
	    split.push(split.splice(limit-1, split.length-limit+1).join(pattern));

    }

    if (opt.trim)
      if (split instanceof Array)
	for (i=0; i<split.length; i++) split[i]=split[i].replace(/^\s*(.*?)\s*$/, '$1');
      else
	split=split.replace(/^\s*(.*?)\s*$/, '$1');

    return split;
  };


String.prototype.inside=
  function () {
    for (var a=0; a<arguments.length; a++) {
      var aa=arguments[a];
      if (aa && this in aa && aa[this]!=undefined) return aa[this];
    }
  };


String.prototype.asObject=
  function ( missing ) {
    var match=this.split('.', true);
    var value=window;
    while (match.length && value) value=value[match.shift()];
    return horus.checkNull(value, missing);
  };


String.prototype.possessive=
  function ( suffix ) {
    var value=this.rtrim();
    if (value!='') value=(suffix ? '' : value)+(/s$/.test(value) ? '\'' : '\'s');
    return value;
  };


String.prototype.filter=
  function ( options ) {
    var value=this;
    options=horus.options(options);
    if (options.trim) value=value.trim();
    if (options.encode) value=encodeURIComponent(value);
    return value;
  };


String.prototype.compare=
Number.prototype.compare=
  function ( value ) {
    return value==null || this<value ? 1 : this>value ? -1 : 0;
  };


String.prototype.compareNoCase=
  function ( value ) {
    if (value==null) return 1;
    var a=this.toLowerCase();
    var b=value.toLowerCase();
    return a<b ? 1 : a>b ? -1 : 0;
  };


horus.$s={
  p:
    function ( p, e ) {
      e=!e ? 1 : e<0 ? 0 : 2;
      if (!p) p='\\s';
      var r1=horus.$s[p];
      if (!r1) horus.$s[p]=r1=[];
      var r2=r1[e];
      if (!r2) r1[e]=r2=new RegExp([ '^'+p+'*', '^'+p+'*(.*?)'+p+'*$', p+'*$' ][e]);
      return r2;
    }

};


String.prototype.test  = function ( s ) { return String(s).indexOf(this)>=0 };
String.prototype.left  = function ( c ) { return this.slice(0, c) };
String.prototype.right = function ( c ) { return this.slice(-c) };
String.prototype.trim  = function ( p ) { return this.replace(horus.$s.p(p), '$1') };
String.prototype.ltrim = function ( p ) { return this.replace(horus.$s.p(p, -1), '') };
String.prototype.rtrim = function ( p ) { return this.replace(horus.$s.p(p, 1), '') };


Number.prototype.trim=Number.prototype.ltrim=Number.prototype.rtrim=
Number.prototype.toLowerCase=Number.prototype.toUpperCase=
  function () { return String(this) };

Number.$asString=
  function ( m ) {
    return function () { return String.prototype[m].apply(String(this), arguments) };
  };

Number.prototype.split		= Number.$asString('split');
Number.prototype.test		= Number.$asString('test');
Number.prototype.slice		= Number.$asString('slice');
Number.prototype.left		= Number.$asString('left');
Number.prototype.right		= Number.$asString('right');
Number.prototype.inside		= Number.$asString('inside');
Number.prototype.filter		= Number.$asString('filter');
Number.prototype.compareNoCase	= Number.$asString('compareNoCase');


Number.prototype.ordinal=
  function () {
    var n=this;
    if (n<0) n=-n;
    n=n%100;
    if (n>3 && n<21) return 'th';
    n=n%10;
    if (n==1) return 'st';
    if (n==2) return 'nd';
    if (n==3) return 'rd';
    return 'th';
  };


Number.prototype.toCurrency=
  function ( symbol, sep ) {
    if (symbol==null) symbol='£';
    if (sep==null) sep=',';
    var display=this.toFixed(2);
    var test=new RegExp('\\d{4}[.'+sep.left(1)+']');
    var replace=new RegExp('(\\d)(\\d{3})([.'+sep.left(1)+'])');
    while (test.test(display)) display=display.replace(replace, '$1'+sep+'$2$3');
    return symbol+display;
  };


Number.prototype.toGrouped=
  function ( sep ) {
    if (sep==null) sep=',';
    var display=this.toString();
    var test=new RegExp('\\d{4}('+sep+'|$)');
    var replace=new RegExp('(\\d)(\\d{3})('+sep+'|$)');
    while (test.test(display)) display=display.replace(replace, '$1'+sep+'$2$3');
    return display;
  };


Number.prototype.toSize=
  function ( options ) {
    options=horus.options
      (options, { units: '', decimals: 2, decimal: false, point: '.', unitclass: '' },
       { string: 'units', numeric: 'decimals', boolean: 'decimal' });

    var space='';
    var size=this;
    var factor=options.decimal ? 1000 : 1024;
    var magnitude=-1;
    while (++magnitude<=4 && size>=factor) size/=factor;

    if (options.units!='' && options.units.left(1)==' ') {
      space=' ';
      options.units=options.units.ltrim();
    }

    if (magnitude>0) {
      switch (magnitude) {

      case 1: magnitude=options.decimal ? 'k' : 'K';	break;
      case 2: magnitude='M';				break;
      case 3: magnitude='G';				break;
      case 4: magnitude='T';				break;

      }

      if (options.point=='')
	options.point=magnitude;
      else
	options.units=magnitude+options.units;

    }

    size=size.toFixed(options.decimals);
    if (options.point!='.') size=size.replace(/\./g, options.point);

    if (options.units!='') {
      options.units=space+options.units;

      if (options.unitclass)
	size=[ size, [ 'span', { className: options.unitclass }, options.units ] ];
      else
	size+=options.units;

    }

    return size;
  };


Number.prototype.plural=
  function () {
    return this==1 ?
      (arguments.length>1 ? arguments[1] : '') :
      (arguments.length>0 ? arguments[0] : 's');

  };


Boolean.parse=
  function ( value ) {
    if (horus.isBoolean(value)) return value;
    if (horus.isNumber(value)) return Number(value)!=0;

    if (horus.isString(value))
      return /^\s*(t(rue)?|y(es)?)\s*$/i.test(value) ? true :
	/^\s*(f(alse)?|no?)?\s*$/i.test(value) ? false : null;

    if (value==null) return false;
    if (value instanceof Array) return value.length>0;
    return null;
  };


Array.prototype.isEmpty=function () { return this.length==0 };


Array.prototype.clone=
  function () {
    var clone=new Array(this.length);
    for (var i=0; i<this.length; i++) clone[i]=this[i];
    if (arguments.length) clone.addList.apply(clone, arguments);
    return clone;
  };


Array.prototype.copy=
  function ( from ) {
    this.length=from.length;
    for (var i=0; i<from.length; i++) this[i]=from[i];
    return this;
  };


Array.prototype.insert=
  function () {
    var argc=arguments.length;
    var sorter=arguments[argc-1];

    if (typeof sorter=='function')
      argc--;
    else
      sorter=horus.compare;

    for (var i=0; i<argc; i++) {
      var insertion=arguments[i];

      for (var position=0; position<this.length; position++)
	if (sorter(insertion, this[position])>=0) {
	  this.splice(position, 0, insertion);
	  break;
	}

      if (position==this.length) this.push(insertion);
    }

    return position;
  };


Array.prototype.find=
  function ( value, point ) {
    if (point==null) point=0;
    while (point<this.length && this[point]!=value) point++;
    return point<this.length ? point : null;
  };


Array.prototype.addList=
  function () {
    for (var i=0; i<arguments.length; i++) {
      var item=arguments[i];

      if (horus.isArray(item))
	for (var j=0; j<item.length; this.push(item[j++]));
      else
	this.push(item);

    }

    return this;
  };


Array.prototype.addString=
  function () {
    var prev=this.length && horus.isString(this[this.length-1]);

    for (var i=0; i<arguments.length; i++) {
      var obj=arguments[i];
      var next=horus.isString(obj);

      if (prev && next)
	this[this.length-1]+=obj;
      else
	this.push(obj);

      prev=next;
    }

    return this;
  };


Array.prototype.lambda=
  function () {
    var options=arguments[0];
    var result;
    var offset=0;

    if (!(typeof options=='function' || options instanceof Array)) {
      options=horus.options
	(options, { inplace: false, hash: false, offset: 0 },
	 { boolean: 'inplace', numeric: 'offset' });

      result=options.hash;
      if (result && typeof result=='boolean') result={};
      offset=1;
    } else
      options={ offset: 0 };

    if (!options.hash) result=options.inplace ? this : [];
    var argc=this.length;
    var i=options.offset;
    this.$result=result;

    while (i<argc) {
      var item=this[i];
      this.$index=i;
      this.$odd=((i-options.offset)&1)==0;

      for (var j=offset; j<arguments.length && item!=undefined; j++) {
	var fn=arguments[j];

	if (fn instanceof Array) {
	  var argv=fn.clone();
	  fn=fn[0];
	  argv[0]=item;
	  item=fn.apply(this, argv);
	} else
	  item=fn.call(this, item);

      }

      if (options.inplace) {
	if (item==undefined) {
	  this.splice(i--, 1);
	  argc--;
	} else if (item!==this[i])
	  this[i]=item;

      } else if (item!=undefined && !options.hash)
	result.push(item);

      i++;
    }

    delete this.$result;
    delete this.$index;
    return result;
  };


Array.prototype.clean=
  function ( inplace ) {
    return this.lambda(inplace, horus.notBlank);
  };


Array.prototype.enumerate=
  function ( inplace ) {
    return this.lambda(inplace, horus.notBlank, Number);
  };


Array.prototype.first = function () { if (this.length) return this[0] };
Array.prototype.last  = function () { if (this.length) return this[this.length-1] };
Array.prototype.clear = function () { this.length=0 };


horus.subclass=
  function ( superclass ) {
    if (!superclass)
      superclass=horus.hash;
    else if (superclass.constructor && superclass.constructor!=Function)
      superclass=superclass.constructor;

    var init, superinit, className;

    for (var a=1; a<arguments.length; a++) {
      var arg=arguments[a];

      if (typeof arg=='function')
	init=arg;
      else if (arg instanceof Array || horus.isBoolean(arg))
	superinit=arg;
      else if (horus.isString(arg))
	className=arg;

    }

    var constructor;

    if (superinit==null) {
      constructor=init ?
	function () { init.apply(this, arguments) } :
        function () {};

    } else if (superinit instanceof Array) {
      constructor=init ?
	function () { superclass.apply(this, superinit); init.apply(this, arguments) } :
        function () { superclass.apply(this, superinit) };

    } else if (superinit) {
      constructor=init ?
	function () { superclass.apply(this, arguments); init.apply(this, arguments) } :
        function () { superclass.apply(this, arguments) };

    } else {
      constructor=init ?
	function () { superclass.call(this); init.apply(this, arguments) } :
        function () { superclass.call(this) };

    }

    var deferInit=function () {};
    deferInit.prototype=superclass.prototype;
    constructor.$superclass=superclass.prototype;
    constructor.prototype=new deferInit();
    constructor.prototype.constructor=constructor;
    if (className!=null) constructor.className=className;
    return constructor;
  };


horus.typeOf=
  function ( obj, fromstring ) {
    var t=typeof obj;

    if (t=='object' && obj!=null)
      if (obj instanceof String)
	t='string';
      else if (obj instanceof Number)
	t='number';
      else if (obj instanceof Boolean)
	t='boolean';

    if (fromstring && t=='string')
      if (/^\s*(t(rue)?|f(alse)?|y(es)?|no?)?\s*$/i.test(obj))
	t='boolean';
      else if (/^\s*[+-]?\d+(\.\d*)?\s*$/.test(obj))
	t='number';

    return t;
  };


horus.isString=
  function ( obj ) {
    return horus.typeOf(obj)=='string';
  };


horus.isBoolean=
  function ( obj, fromstring ) {
    return horus.typeOf(obj, fromstring)=='boolean';
  };


horus.isNumber=
  function ( obj ) {
    return horus.typeOf(obj, true)=='number';
  };


horus.isArray=
  function ( obj ) {
    if (obj==null || typeof obj!='object') return false;
    if (obj instanceof Array) return true;
    if (typeof obj.length!='number') return false;
    if (typeof obj.callee=='function') return true;
    if (!horus.isNode(obj) && (!obj.length || horus.isNode(obj[0]))) return true;
    return false;
  };


horus.isSimpleValue=
  function ( obj ) {
    return /^(string|number|boolean)$/.test(horus.typeOf(obj));
  };


horus.isNode=
  function ( obj ) {
    if (!obj || typeof obj!='object') return false;
    return horus.iewin ? obj.nodeType : obj instanceof Node;
  };


horus.isElement=
  function ( obj, tag ) {
    if (!(horus.isNode(obj) && obj.nodeType==1)) return false;
    return !tag || obj.tagName.toLowerCase()==tag.toLowerCase();
  };


horus.isDocument=
  function ( obj ) {
    if (!obj || typeof obj!='object') return false;
    if (horus.iewin) return 'URL' in obj && 'forms' in obj && 'referrer' in obj;

    try {
      return obj instanceof Document;
    } catch ( err ) {
      return 'URL' in obj && 'forms' in obj && 'referrer' in obj;
    }
  };


horus.isWindow=
  function ( obj ) {
    if (!obj || typeof obj!='object' || obj.window!=obj) return false;
    if (horus.iewin) return horus.isDocument(obj.document);

    try {
      return obj instanceof Window;
    } catch ( err ) {
      return horus.isDocument(obj.document);
    }
  };


horus.notBlank=
  function ( v ) {
    return v!=null &&
           (typeof v=='string' || typeof v=='object' && v instanceof String) &&
           v!='' && !/^\s*$/.test(v) ? v : undefined;

  };


horus.hasValue=
  function ( obj ) {
    switch (horus.typeOf(obj)) {

    case 'string':    return obj!='';
    case 'number':    return obj!=0;
    case 'boolean':   return obj!=false;
    case 'function':  return true;
    case 'undefined': return false;

    case 'object':
      if (obj==null) return false;
      if (typeof obj.isEmpty=='function') return !obj.isEmpty();
      if (typeof obj.hasValue=='function') return obj.hasValue();
      if (horus.isElement(obj) || obj instanceof RegExp) return true;
      return !horus.hash.empty(obj);

    }
  };


horus.isEmpty=function ( obj ) { return !horus.hasValue(obj) };


horus.className=
  function ( object, className ) {
    var name=
      typeof object.className=='function' ? object.className() :
      object.className ? object.className :
      object.constructor ? object.constructor.className : null;

    if (className==null) return name;
    if (typeof className!='string') className=horus.className(className);
    return name==className;
  };


horus.toArray=
  function ( object ) {
    if (object instanceof Array) return object;
    var result=new Array(object.length);
    for (var i=0; i<object.length; result.push(object[i++]));
    return result;
  };


horus.toString=
  function ( arg, quote, done ) {
    if (arg==null) return horus.NUL;
    if (quote && horus.isString(arg)) return '“'+arg+'”';
    if (horus.isSimpleValue(arg)) return arg;
    if (!done) done=[];
    var loop=done.find(arg);
    done.push(arg);

    var tag='';

    if (horus.isElement(arg)) {
      tag=arg.tagName.toLowerCase();
      if (arg.name) tag+=':'+arg.name;
      if (arg.id) tag+='#'+arg.id;
      if (arg.className) tag+='.'+arg.className;
    } else {
      tag=horus.className(arg);
      if (tag==null) tag='';
    }

    if (horus.isArray(arg)) {
      if (loop)
	arg='[ … ]';
      else {
	var text=[];
	for (var i=0; i<arg.length; i++) text.push(horus.toString(arg[i], true, done));
	arg='[ '+text.join(', ')+' ]';
      }
    } else
      if (arg.constructor==Function)
	arg=arg.toString().replace(/\{(.+|\n+)*}/, '{ … }');
      else if (arg.toString && arg.toString!=Object.prototype.toString &&
	       arg.toString!=horus.hash.prototype.toString)
	arg=arg.toString();
      else if (loop)
	arg='{ ∞ }';
      else {
	var keys=horus.keys(arg);

	for (var k=0; k<keys.length; k++) {
	  var key=keys[k];
	  keys[k]=key+': '+horus.toString(arg[key], true, done);
	}

	arg='{ '+keys.join(', ')+' }';
      }

    if (tag!='') arg='«'+tag+'»'+arg;
    return arg;
  };


horus.keys=
  function ( hash ) {
    var all, sorter;
    var keys=[];

    if (arguments.length>1)
      if (typeof arguments[1]=='function')
	sorter=arguments[1];
      else {
	all=arguments[1];
	if (arguments.length>2) sorter=arguments[2];
      }

    for (var key in hash)
      if (all || hash.hasOwnProperty && hash.hasOwnProperty(key)) keys.push(key);

    return (sorter ? keys.sort(sorter) : keys);
  };


horus.isEqual=
  function ( a, b ) {
    if (typeof a!=typeof b) {
      var at=horus.typeOf(a, true);
      var bt=horus.typeOf(b, true);
      if (at!=bt) return false;
      if (typeof a!=at) a=at=='number' ? Number(a) : horus.toBoolean(a);
      if (typeof b!=bt) b=bt=='number' ? Number(b) : horus.toBoolean(b);
      return a==b;
    }

    if (a==b) return true;
    if (a==null || b==null || at!='object') return false;

    if (a instanceof Array) {
      if (!(b instanceof Array)) return false;
      if (a.length|=b.length) return false;

      for (var i=0; i<a.length; i++)
	if (!horus.isEqual(a[i], b[i])) return false;

    }

    if (!horus.isEqual(horus.keys(a), horus.keys(b))) return false;

    for (var k in a)
      if (!horus.isEqual(a[k], b[k])) return false;

    return true;
  };


horus.clone=
  function ( from, deep, done ) {
    if (!from || typeof from!='object' || horus.isSimpleValue(from)) return from;

    if (deep)
      if (!done)
	done={ from: [], to: [] };
      else {
	var found=done.from.find(from);
	if (found!=null) return done.to[found];
	if (typeof deep=='number') --deep;
      }

    var isArray=horus.isArray(from);

    var to=
      isArray ? new Array(from.length) :
      from instanceof horus.hash ? new horus.hash : {};

    if (deep) {
      done.from.push(from);
      done.to.push(to);
    }

    if (isArray)
      for (var i=0; i<from.length; i++)
	to[i]=deep ? horus.clone(from[i], deep, done) : from[i];

    for (var attr in from)
      to[attr]=deep ? horus.clone(from[attr], deep, done) : from[attr];

    return to;
  }


horus.hash=
  function () {
    if (!(this instanceof horus.hash)) return new horus.hash(arguments);
    if (arguments.length) this.merge(arguments);
  };


horus.hash.className='horus.hash';


horus.hash.merge=
  function ( hash, argv, offset, keys, delimiter ) {
    if (offset==null) offset=0;

    if (keys && horus.isString(keys))
      keys=horus.hash.merge({}, [ keys ], 0, null, delimiter);

    var key;

    for (var arg=offset; arg<argv.length; arg++) {
      var from=argv[arg];

      if (from!=null)
	if (horus.isString(from)) {
	  if (!delimiter) delimiter=',';
	  from=from.toLowerCase().split(delimiter, 'trim');

	  for (var i=0; i<from.length; i++) {
	    var item=from[i];
	    if (item=='') continue;
	    var split=item.search(/ *= */);

	    if (split>=0) {
	      var val=item.substring(split+1);

	      if (val=='true' || val=='false')
		val=val=='true';
	      else if (val.match(/^[+-]?\d+$/))
		val=Number(val);

	      key=item.substring(0, split);
	      if (!keys || key in keys) hash[key]=val;
	    } else
	      if (item.substring(0, 2)=='no') {
		key=item.substring(2);
		if (!keys || key in keys) hash[key]=false;
	      } else if (!keys || item in keys)
		hash[item]=true;

	  }
	} else
	  if (horus.isArray(from))
	    horus.hash.merge(hash, from, 0, keys);
	  else
	    for (key in from)
	      if (!keys || key in keys) hash[key]=from[key];

    }

    return hash;
  };


horus.hash.key=
  function ( hash ) {
    for (var key in hash)
      if (!hash.hasOwnProperty || hash.hasOwnProperty(key)) return key;

    return undefined;
  };


horus.hash.get=
  function ( hash, key, val ) {
    if (key==undefined) key=horus.hash.key(hash);
    return key in hash ? hash[key] : val ;
  };


horus.hash.$remove=
  function ( hash, argv, offset ) {
    if (offset==null) offset=0;

    for (var a=offset; a<argv.length; a++) {
      var key=argv[a];

      if (key in hash && (!hash.hasOwnProperty || hash.hasOwnProperty(key)))
	try {
	  delete hash[key];
	} catch ( err ) {
	  hash[key]=undefined; // IE!
	}

    }
  };


horus.hash.keys   = horus.keys;
horus.hash.size   = function ( hash ) { return horus.keys(hash).length };
horus.hash.empty  = function ( hash ) { return horus.hash.key(hash)==null };
horus.hash.add    = function ( hash ) { return horus.hash.merge(hash, arguments, 1) };
horus.hash.remove = function ( hash ) { horus.hash.$remove(hash, arguments, 1) };
horus.hash.copy   = function ()       { return horus.hash.merge({}, arguments) };


horus.hash.prototype.merge=
  function ( arg ) {
    if (arguments.length>1 &&
	(arg instanceof RegExp || typeof arg=='string' && arg.length==1))
      return horus.hash.merge(this, arguments, 1, null, arg);
    else
      return horus.hash.merge(this, arguments);

  };


horus.hash.prototype.get=
  function ( key, val ) {
    return horus.hash.get(this, key, val);
  };


horus.hash.prototype.add      = horus.hash.prototype.merge;
horus.hash.prototype.remove   = function ()       { horus.hash.$remove(this, arguments) };
horus.hash.prototype.clone    = function ( deep ) { return horus.clone(this, deep) };
horus.hash.prototype.toString = function ()       { return horus.toString(this, true) };
horus.hash.prototype.key      = function ()       { return horus.hash.key(this) };
horus.hash.prototype.keys     = function ( sort ) { return horus.keys(this, sort) };
horus.hash.prototype.size     = function ()       { return this.keys().length };
horus.hash.prototype.isEmpty  = function ()       { return this.key()==null };
horus.hash.prototype.isEqual  = function ( hash ) { return horus.isEqual(this, hash) };


horus.options=
  function ( optionsin, def, key, delimiter ) {
    var optionsout=new horus.hash;
    if (def) optionsout.merge(delimiter, def);

    if (key && optionsin!=null) {
      var select=horus.typeOf(optionsin);

      if (typeof key=='object' && /^(string|number|boolean|function)$/.test(select) &&
	  select in key && !(select=='string' && (optionsin=='' || /=/.test(optionsin))))
	optionsin=key[select]+'='+optionsin;
      else if (select=='boolean' && horus.isString(key))
	optionsin=key+'='+optionsin;

    }

    if (optionsin) optionsout.merge(delimiter, optionsin);
    return optionsout;
  };


horus.options.data=
  function ( section, tag, data ) {
    if (!horus[section]) horus[section]={};
    section=horus[section];
    var options=section.data;
    if (!options) options=section.data={};

    if (tag) {
      if (horus.isElement(tag)) tag=horus.toId.base(tag, -1);
      var options=options[tag];

      if (options==null)
	options=section.data[tag]={};
      else if (typeof options=='number' && options>=0 &&
	       section.opt && options<section.opt.length)
	options=section.opt[options];

      if (data) horus.hash.add(options, data);
    }

    return options;
  };


horus.options.get=
  function ( section, tag, def, key, delimiter ) {
    return horus.options(horus.options.data(section, tag), def, key, delimiter);
  };


horus.options.set=
  function ( options, source, keys, delimiter ) {
    if (!horus.isArray(source)) source=[ source ];
    return horus.hash.merge(options, source, 0, keys, delimiter);
  };


horus.options.save=
  function ( into, options, keys, prefix ) {
    if (prefix==null) prefix='$';

    for (var key in options)
      if (!keys || key in keys) into[prefix+key]=options[key];

  };


horus.callable=
  function ( method ) {
    if (!method) return false;
    if (typeof method=='function') return true;
    if (!(method instanceof Array)) return false;
    if (typeof method[0]=='function') return true;
    if (typeof method[0]!='object' || method.length<2) return false;
    if (typeof method[1]=='function') return true;
    if (!horus.isString(method)) return false;
    if (typeof method[0][method[1]]=='function') return true;
    return false;
  };


horus.deframe=
  function ( replace ) {
    if (window.parent==window) return;

    if (replace)
      window.top.location.replace(window.location.href);
    else
      window.top.location.href=window.location.href;

  };


horus.mailto=
  function ( name, domain, subject, body ) {
    var url='mailto:'+encodeURIComponent(name+'@'+domain);
    if (subject!='') url+='?subject='+encodeURIComponent(subject);
    if (body!='') url+=(subject=='' ? '?' : '&')+'body='+encodeURIComponent(body);
    document.location.href=url;
  };


horus.etacsufbo=
  function ( to, code, action ) {
    var decode=action || action==null;
    if (decode) to=decodeURIComponent(to);
    var count=to.length;
    var link=[];

    for (var i=0; i<count; i++)
      link.push(to.charCodeAt(i)^code.charCodeAt(i%code.length));

    link=String.fromCharCode.apply(link);

    if (action)
      document.location.href='mailto:'+link;
    else if (decode)
      document.write(link.replace(/@/, '﹫'));
    else
      return encodeURIComponent(link);

  };


// return window vertical scroll position
//
horus.scrollv=
  function () {
    if (document.documentElement && document.documentElement.scrollTop)
      return document.documentElement.scrollTop;

    if (document.body && document.body.scrollTop!=null)
      return document.body.scrollTop;

    return window.pageYOffset;
  };


// return window horizontal scroll position
//
horus.scrollh=
  function () {
    if (document.documentElement && document.documentElement.scrollLeft)
      return document.documentElement.scrollLeft;

    if (document.body && document.body.scrollLeft!=null)
      return document.body.scrollLeft;

    return window.pageXOffset;
  };


// return window size
//
horus.windowSize=
  function ( win ) {
    var size={};
    if (!win) win=window;

    if (!win.$widthChecker && win==window && horus.insertChild)
      try {
	win.$widthChecker=horus.insertChild
	  ([ 'div',
             { position: 'absolute', top: '-1px', left: 0,
	       width: '100%', height: '1px' } ]);

      } catch ( err ) {}

    if (win.innerHeight) {
      size.height=win.innerHeight;
      size.width=win.innerWidth;
    } else {
      win=win.document;

      if (win.documentElement && win.documentElement.clientHeight) {
	size.height=win.documentElement.clientHeight;
	size.width=win.documentElement.clientWidth;
      } else {
	size.height=win.body.clientHeight;
	size.width=win.body.clientWidth;
      }
    }

    if (win.$widthChecker) size.width=win.$widthChecker.offsetWidth;
    return size;
  };


// return window size, and top, bottom, left and right coördinates
//
horus.windowPos=
  function () {
    var winsize=
      { width:  horus.$width,
	height: horus.$height,
	top:    horus.scrollv(),
	left:   horus.scrollh() };

    winsize.bottom=winsize.top+winsize.height;
    winsize.right=winsize.left+winsize.width;
    return winsize;
  };


horus.getPosition=
  function ( obj, root, debug ) {
    obj=horus.getElement(obj);
    if (!obj) return null;

    var pos=
      { height: obj.offsetHeight, scrollTop: obj.scrollTop,
	width: obj.offsetWidth, scrollLeft: obj.scrollLeft };

    if (typeof root=='boolean')
      root=root ? null : horus.offsetParent(obj);
    else
      root=root ? horus.getElement(root) : null;

    if (debug)
      var status=[ obj.id+' - '+(root ? root.id : '*') ];

    var ptr=100;
    var lastref;
    pos.top=0;
    pos.left=0;

    while (obj && --ptr>0 && !horus.getPosition.isroot(root, obj, lastref)) {
      var offsetParent=horus.offsetParent(obj);
      if (obj.scrollTop) pos.top-=obj.scrollTop;

      if (!(offsetParent && horus.inside(offsetParent, lastref))) {
	if (debug)
	  status.push
	    (obj.nodeName+'.'+obj.id+': '+obj.offsetTop+', '+obj.offsetLeft+' - '+
	     (offsetParent ? offsetParent.nodeName+'.'+offsetParent.id : horus.NUL));

	if (obj.offsetTop) pos.top+=obj.offsetTop;
	if (obj.offsetLeft) pos.left+=obj.offsetLeft;
	if (horus.brokenDOM && !offsetParent) break;
	lastref=offsetParent;
      }

      obj=obj.parentNode;
    }

    if (debug) {
      status.push('= '+pos.top+', '+pos.left);
      alert(status.join('\n'));
    }

    pos.bottom=pos.top+pos.height-1;
    pos.right=pos.left+pos.width-1;
    return pos;
  };


horus.getPosition.isroot=
  function ( root ) {
    for (var i=1; i<arguments.length; i++) {
      var node=arguments[i];
      if (node && (node==root || node.nodeName=='BODY')) return true;
    }

    return false;
  };


horus.event=
  function ( event, root, scrolled ) {
    if (root==undefined) root=null;

    if (this instanceof horus.event) {
      if (horus.isElement(event) || !event && !window.event ||
	  horus.isArray(event) && horus.isElement(event[0])) {
	if (horus.isArray(event)) event=event[0];
	event={ target: event, srcElement: event, type: 'call' };
      } else if (event && typeof event.length=='number')
	event=event[0];

      if (event && event instanceof horus.event) {
	for (var k in event) this[k]=event[k];
	if (this.root!=root || root && scrolled) this.rebox(root, scrolled);
      } else {
	if (!(event && event.type)) event=window.event || event[0];
	this.rawevent=event;
	this.type=event.type;
	this.altKey=event.altKey;
	this.ctrlKey=event.ctrlKey;
	this.shiftKey=event.shiftKey;
	this.detail=event.detail;
	this.controls={};
	this.rebox(root, scrolled);

	if (window.event) {
	  this.target=event.srcElement;
	  this.currentTarget=event.target;
	  this.keyCode=event.keyCode;

	  if (horus.ieold)
	    if (event.srcElement && event.y!=event.clientY && event.x!=event.clientX &&
		(event.y<0 || event.y>=event.srcElement.offsetHeight ||
		 event.x<0 && event.x>=event.srcElement.offsetWidth)) {
	      this.y+=10-event.y;
	      this.x+=10-event.x;
	    }

	} else {
	  this.target=event.target;
	  this.currentTarget=event.currentTarget;
	  this.keyCode=event.which;
	}
      }
    } else
      return event instanceof horus.event ?
        event.boxed(root, scrolled) : new horus.event(event, root, scrolled);

  };


horus.event.className='horus.event';


horus.event.prototype.boxed=
  function ( root, scrolled ) {
    return this.root!=root || root && scrolled ? new horus.event(this, root) : this;
  };


horus.event.prototype.rebox=
  function ( root, scrolled ) {
    if (root==undefined) root=null;

    if (!('root' in this && root==this.root && !(root && root.scrollTop))) {
      var pos=horus.windowPos();
      this.root=root;

      if (root) {
	root=horus.getPosition(root);
	pos.top-=root.top;
	if (root.scrollTop && !scrolled) pos.top-=root.scrollTop;
	pos.left-=root.left;
	if (root.scrollLeft && !scrolled) pos.left-=root.scrollLeft;
      }

      this.y=pos.top+this.rawevent.clientY;
      this.x=pos.left+this.rawevent.clientX;
    }

    return this;
  };


horus.event.prototype.control=
  function ( item ) {
    if (!item) return this.target;
    var cached=horus.isString(item) ? item : false;
    if (cached && this.controls[cached]) return this.controls[cached];
    var filter;

    if (cached && /^\.?\w+$/.test(item)) {
      if (item.left(1)=='.') item=item.right(-1);
      item=new RegExp('(^| )'+item+'( |$)');
      filter=function (pattern, node) { return pattern.test(node.className) };
    } else {
      item=horus.getTags.parse(item);
      filter=horus.getTags.check;
    }

    for (var node=this.target; node && !filter(item, node); node=node.parentNode);
    if (cached) this.controls[cached]=node;
    return node;
  };


horus.event.prototype.id=
  function ( item ) {
    item=this.control(item);
    while (item && !item.id) item=item.parentNode;
    if (item) item=item.id;
    return item;
  };


horus.event.prototype.toId=
  function ( index, tag ) {
    if (!('$node' in this)) this.$node=horus.toId.id(this.target);
    return this.$node ? horus.toId.split(this.$node, index, tag) : null;
  };


horus.event.prototype.form=
  function () {
    return horus.toForm(this);
  };


horus.event.prototype.character=
  function () {
    if (this.$character==undefined) this.$character=String.fromCharCode(this.keyCode);
    return this.$character;
  };


horus.event.prototype.preventDefault=
  function () {
    if (this.rawevent.preventDefault)
      this.rawevent.preventDefault();
    else
      this.rawevent.returnValue=false;

  };


horus.event.prototype.stopPropagation=
  function () {
    if (this.rawevent.stopPropagation)
      this.rawevent.stopPropagation();
    else
      this.rawevent.cancelBubble=true;

  };


// mousewheel event data thanks to Adomas Paltanavičius, taken from
// http://adomas.org/javascript-mouse-wheel/, but with direction reversed (-ve is up)
//
horus.event.prototype.mouseDelta=
  function () {
    var delta=0;

    if (this.rawevent.wheelDelta) { // IE/Opera
      delta=this.rawevent.wheelDelta/120; 
      if (!horus.opera) delta=-delta;
    } else if (this.rawevent.detail) // gecko
      delta=this.rawevent.detail/3;

    if (this.rawevent.preventDefault) this.rawevent.preventDefault();
    this.rawevent.returnValue=false;
    return delta;
  };


horus.event.prototype.mouseInBox=
  function ( box, scrolled ) {
    if (!box) return false;
    var event=this.boxed(box, scrolled);

    return event.y>=0 && event.y<box.offsetHeight &&
           event.x>=0 && event.x<box.offsetWidth;

  };


horus.eventListener=
  function ( element, event, action, object ) {
    element=horus.getElement(element, false);
    if (horus.gecko && event=='mousewheel') event='DOMMouseScroll';
    var target=object || element;
    var method=action;

    action=object || !window.addEventListener ?
      function () { return method.call(target, new horus.event(arguments)) } :
      function () { return method.call(this, new horus.event(arguments)) };

    if (!element.$events) element.$events={};
    if (!element.$events[event]) element.$events[event]=[];
    element.$events[event].push([ method, target, action ]);

    if (window.addEventListener)
      element.addEventListener(event, action, false);
    else
      element.attachEvent('on'+event, action);

  };


horus.removeListener=
  function ( element, event, action, object ) {
    element=horus.getElement(element, false);
    if (!element.$events) return;
    var events=element.$events[event];
    if (!events) return;
    var target=object || element;

    for (var i=0; i<events.length; i++)
      if (events[i][0]==action && events[i][1]==target) {
	action=events[i][2];

	if (window.removeEventListener)
	  element.removeEventListener(event, action, false);
	else
	  element.detachEvent('on'+event, action);

	events.splice(i, 1);
	break;
      }

  };


horus.eval=
  function ( val, missing ) {
    if (horus.isString(val))
      try { val=eval(val) } catch ( err ) {}
    else if (horus.callable(val))
      val=horus.call(val);

    if (val==null || horus.isString(val) && val==horus.NUL)
      val=missing==undefined ? null : missing;

    return val;
  };


horus.isNull=
  function ( value, ajaxnull ) {
    return value==null || ajaxnull && horus.isString(value) && value==horus.NUL;
  };


horus.checkNull=
  function ( value, missing, ajaxnull ) {
    if (horus.isNull(value, ajaxnull)) value=missing==undefined ? null : missing;
    return value;
  };


horus.asObject=
  function ( value, missing ) {
    return horus.isString(value) ?
      value.asObject(missing) : horus.checkNull(value, missing);

  };


horus.toObject=
  function ( val ) {
    switch (typeof val) {

    case 'boolean':
      return new Boolean(val);

    case 'number':
      return new Number(val);

    case 'string':
      if (/^(true|false)$/.test(val)) return new Boolean(val=='true');
      if (/^[-+]?(\d*\.)?\d+$/.test(val)) return new Number(val);
      return new String(val);

    default:
      return val;

    }
  };


horus.toBoolean=
  function ( val ) {
    if (typeof val=='boolean') return val;
    if (typeof val=='number') return val!=0;
    if (!Boolean(val)) return false;
    if (val.trim().match(/^(0|f(alse)?|n(o)?|off)?$/i)) return false;
    return true;
  };


horus.toDate=
  function ( val, def ) {
    if (val==null) return def;
    if (val instanceof Date) return val;
    if (val=='') return def;

    var date=
      /^(?:\{ts ')?(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)(?:\.(\d+))?(?:'})?$/.exec(val) ||
      /^(\d+),(\d+),(\d+)(?:,(\d+),(\d+)(?:,(\d+)(?:,(\d+))?)?)?$/.exec(val);

    if (date) {
      for (var i=3; i<8; i++) if (!date[i]) date[i]=0;
      return new Date(date[1], date[2]-1, date[3], date[4], date[5], date[6], date[7]);
    }

    return new Date(val);
  };


horus.toForm=
  function ( theForm, theField ) {
    if (theForm==null || typeof theForm=='boolean')
      if (theField && theField.form)
	theForm=typeof theField.form=='function' ? theField.form() : theField.form;
      else if (theField && theField.length && theField[0].form)
	theForm=theField[0].form;
      else
	for (var i=0; i<document.forms.length; i++) {
	  var thisForm=document.forms[i];
	  var realForm=!/^(statusbox|popover|confirmbox)form/.test(thisForm.name);

	  if (theForm==null || realForm) {
	    theForm=thisForm;
	    if (realForm) break;
	  }
	}

    if (typeof theForm=='number' || typeof theForm=='string')
      theForm=horus.brokenDOM ? horus.iefix.toForm(theForm) : document.forms[theForm];
    else if (theForm) {
      if (theForm instanceof horus.event) theForm=theForm.target;

      if (theForm.form)
	theForm=horus.callable(theForm.form) ? theForm=theForm.form() : theForm.form;
      else if (!theForm.elements)
	theForm=horus.parentTag(theForm, 'form'); // needs dom.js!

    }

    return theForm;
  };


// nasty... have to do this because the argument "array" of a JavaScript
// function isn't a real Array, so we can't splice or shift it
//
horus.argv=
  function ( argv, odd ) {
    var argc=argv.length;
    var splice=odd==null || argc%2==(odd ? 0 : 1);
    var newargs=new Array(splice ? argc : argc+1);
    var outptr=0;
    if (!splice) newargs[outptr++]=null;
    for (inptr=0; inptr<argc; inptr++) newargs[outptr++]=argv[inptr];
    return newargs;
  };


horus.submitArgv=
  function( argv ) {
    argv=horus.argv(argv, false);
    var theForm=argv[0];
    var validate=false;

    if (theForm!=null)
      if (theForm instanceof Array) {
	validate=theForm[1];
	theForm=theForm[0];
      } else if (typeof theForm=='function') {
	validate=theForm;
	theForm=null;
      }

    argv[0]=[ horus.toForm(theForm), validate ];
    return argv;
  };


horus.formArgv=
  function ( argv, odd ) {
    argv=horus.argv(argv, odd);
    argv[0]=horus.toForm(argv[0], argv[1]);
    return argv;
  };


horus.isMulti=
  function ( elem ) {
    if (elem instanceof Array) elem=elem[0];
    if (elem && !elem.tagName && 'length' in elem) return true;
    if (elem.tagName.toLowerCase()!='input') return false;
    return elem.type=='checkbox' || elem.type=='radio';
  };


horus.formField=
  function () {
    var theForm, theField, multi;

    if (arguments[0] instanceof Array) {
      theForm=arguments[0][0];
      theField=arguments[0][1];
      multi=arguments[1];
    } else if (arguments.length>1) {
      theForm=arguments[0];
      theField=arguments[1];
      multi=arguments[2];
    } else
      theField=arguments[0];

    theForm=horus.toForm(theForm, theField);
    var isarray;

    if (typeof theField=='string' && theField.charAt(0)!='#') {
      var theName=theField;

      if (horus.brokenDOM) {
	// it beggars belief, really
	theField=[];
	isarray=true;

	for (var i=0; i<theForm.elements.length; i++)
	  if (theForm.elements[i].name==theName) theField.push(theForm.elements[i]);

	if (theField.length==0) theField=null;
      } else
	theField=theForm.elements[theName];

    } else if (typeof theField=='string')
      theField=document.getElementById(theField.substring(1));

    if (theField) {
      var theTag=theField.tagName || theField[0].tagName;

      if (theTag.toLowerCase()=='input') {
	if (multi==null) multi=horus.isMulti(theField);

	if (multi && !theField.length) {
	  theField=theForm.elements[theField.name];
	  if (!theField.length) theField=[ theField ];
	} else if (!multi && theField.length)
	  theField=theField[0];

      } else if (isarray)
	theField=theField[0];

    }

    return theField;
  };


horus.set=
  function ( theForm, argname, argvalue ) {
    var arg=horus.formField(theForm, argname);
    if (arg) arg.value=argvalue;
  };


horus.toId=
  function ( node, index, tag ) {
    if (!horus.isNumber(node)) {
      node=horus.toId.id(node);
      if (node) node=horus.toId.split(node, index, tag);
    }

    return node;
  };


horus.toId.tag=
  function ( node, index ) {
    node=horus.toId.split(node, index, true);

    node=typeof index=='number' && index<0 ?
      node.lambda(function ( n ) { return n[0] }).join('.') : node[0];

    return node;
  };


horus.toId.base=
  function ( node, index ) {
    node=horus.toId.id(node);
    if (node) node=horus.toId.tag(node, index);
    return node;
  };


horus.toId.split=
  function ( node, index, tag ) {
    if (typeof index=='boolean') {
      tag=index;
      index=null;
    }

    if (index==-1) {
      var result=[];
      var i=0;

      for (;;) {
	index=horus.toId.split(node, i++, tag);
	if (index==null) break;
	result.push(index);
      }

      return result;
    }

    if (index==null)
      index=/^(.*?)(\d*)$/.exec(node);
    else if (index==0)
      index=/^.*?(\D+)(\d*)$/.exec(node);
    else if (index==1)
      index=/^.*?(\D+)(\d+)\D+\d*$/.exec(node);
    else {
      if (!horus.toId.$match) horus.toId.$match=[];

      if (!horus.toId.$match[--index])
	horus.toId.$match[index]=
	  new RegExp('^.*?(\\D+)(\\d+)(\\D+\\d+){'+index+'}\\D+\\d*$');

      index=horus.toId.$match[index].exec(node);
    }

    if (index) {
      node=index[2];
      if (/^\d+$/.test(node)) node=Number(node);
      if (tag) node=[ index[1], node, index[2] ];
    } else
      node=null;

    return node;
  };


horus.toId.id=
  function ( node ) {
    node=horus.toId.node(node);
    return !node ? null : node.id!=null && node.id!='' ? node.id : node.name;
  };


horus.toId.node=
  function ( node ) {
    if (node!=null)
      for (node=horus.getElement(node);
	   node && (node.id==null || node.id=='') &&
	   (node.name==null || node.name=='' ||
	    horus.brokenDOM && node.name=='undefined'); // no, I don't know why
	   node=node.parentNode);

    return node;
  };


horus.getElement=
  function ( item, noevent ) {
    if (!(horus.isNode(item) || horus.isWindow(item)))
      if (horus.isString(item))
	item=document.getElementById(item.left(1)=='#' ? item.right(-1) : item);
      else if (item && typeof item.control=='function')
	item=item.control();
      else if (!noevent)
	try { item=horus.event(item).control() } catch ( err ) {}

    return item;
  };


horus.$makeLink=
  function ( argv ) {
    var url=argv[0];
    var offset=1;

    if (!horus.isString(url)) {
      if (url) offset=0;
      url='';
    }

    if (!/^(https?|ftp|mailto):/.test(url)) {
      if (url=='')
	url=document.location.pathname;
      else {
	var leadin=url.left(1);

	if (leadin=='?')
	  url=document.location.pathname+url;
	else if (leadin!='/')
	  url=document.location.pathname.replace(/\/[^\/]*$/, '/')+url;

      }

      url=document.location.protocol+'//'+document.location.host+url;
    }

    var params=[];

    for (var ptr=offset; ptr<argv.length; ptr++) {
      var arg=argv[ptr];

      if (arg!=null)
	if (horus.isString(arg)) {
	  var point=arg.indexOf('=');

	  if (point<0)
	    params.push(encodeURIComponent(arg));
	  else
	    params.push
	      (encodeURIComponent(arg.left(point))+'='+
	       encodeURIComponent(arg.right(-point-1)));

	} else
	  for (tag in arg) {
	    var value=arg[tag];
	    tag=encodeURIComponent(tag);

	    if (value instanceof Array)
	      for (var i=0; i<value.length; i++)
		params.push(tag+'='+encodeURIComponent(value[i]));

	    else
	      params.push(tag+'='+encodeURIComponent(value));

	  }

    }

    if (params.length) url+=('?'.test(url) ? '&' : '?')+params.join('&');
    return url;
  };


horus.makeLink = function ( url ) { return horus.$makeLink(arguments) };
horus.linkTo   = function ( url ) { document.location.href=horus.$makeLink(arguments) };
horus.noBack   = function ()      { if (window.history) window.history.forward() };
horus.goCheck  = function ()      { if (horus.$goCheck) horus.$goCheck.submit() };


horus.reload=
  function () {
    var force=false, delay, status, node;

    for (var i=0; i<arguments.length; i++) {
      var arg=arguments[i];

      switch (horus.typeOf(arg)) {

      case 'boolean':	force=arg;	break;
      case 'number':	delay=arg;	break;
      case 'string':	status=arg;	break;
      case 'object':	node=arg;	break;

      }
    }

    if (status) horus.status.at(node, status);

    if (delay)
      horus.setTimeout(location, location.reload, delay, force);
    else
      location.reload(force);

  };


// generic form submit
//
horus.doit=
  function ( submitv, argv, noscroll ) {
    argv=horus.submitArgv(argv);
    var parms=argv.shift();
    var theForm=parms[0];
    var validate=parms[1];
    var argc=argv.length;
    var scrollv=noscroll ? 'no' : 0;

    if (submitv==horus.FORM_RESET)
      theForm.reset();
    else if (theForm.onsubmit) {
      status=theForm.onsubmit();
      if (status==false) return false;
    }

    for (var i=0; i<argc; i+=2) {
      var argname=argv[i];
      var argvalue=argv[i+1];

      if (argname=='_scroll')
	scrollv=argvalue;
      else if (argname=='_submit')
	submitv=argvalue;
      else
	horus.set(theForm, argname, argvalue);

    }

    if (horus.script.isLoaded('visible'))
      horus.set(theForm, '_visible', horus.visible.get());

    horus.set(theForm, '_scroll', scrollv=='no' ? 0 : horus.scrollv()+scrollv);
    horus.set(theForm, '_submit', submitv);

    if (validate) {
      var status=validate(theForm);

      if (typeof status=='string')
	if (status!='') {
	  alert(status);
	  status=false;
	} else
	  status=true;

      if (!status) return false;
    }

    if (!theForm.target || theForm.target=='_self')
      for (var ptr=0; ptr<theForm.length; ptr++) {
	var element=theForm.elements[ptr];
	if (element.type=='button' || element.type=='submit') element.disabled=true;
      }

    if (horus.$waitBox && horus.script.isLoaded('popup')) horus.waitBox(true);
    theForm.submit();
    if (horus.iewin || horus.opera) horus.$goCheck=theForm; // IE/Win and Opera
  };


// form submit functions - the submit values are defined in Horus::HTML for Perl
// and cf_submit for ColdFusion
//
horus.go          = function () { return horus.doit(horus.FORM_SUBMIT,   arguments) };
horus.go.Reset    = function () { return horus.doit(horus.FORM_RESET,    arguments) };
horus.go.Entry    = function () { return horus.doit(horus.FORM_ENTRY,    arguments) };
horus.go.Refresh  = function () { return horus.doit(horus.FORM_REFRESH,  arguments) };
horus.go.New      = function () { return horus.doit(horus.FORM_NEW,      arguments) };
horus.go.Add      = function () { return horus.doit(horus.FORM_ADD,      arguments) };
horus.go.Delete   = function () { return horus.doit(horus.FORM_DELETE,   arguments) };
horus.go.Edit     = function () { return horus.doit(horus.FORM_EDIT,     arguments) };
horus.go.Next     = function () { return horus.doit(horus.FORM_NEXT,     arguments) };
horus.go.Previous = function () { return horus.doit(horus.FORM_PREVIOUS, arguments) };
horus.go.Save     = function () { return horus.doit(horus.FORM_SAVE,     arguments) };
horus.go.Restore  = function () { return horus.doit(horus.FORM_RESTORE,  arguments) };
horus.go.Payment  = function () { return horus.doit(horus.FORM_PAYMENT,  arguments) };


horus.focus=
  function () {
    for (var i=0; i<arguments.length; i++) {
      var node=horus.getElement(arguments[i]);
      if (node)	try { node.focus(); return } catch ( err ) {}
    }
  };


horus.$wrap=
  function ( argv, action ) {
    argv=horus.formArgv(argv, true);
    var theForm=argv[0];
    var theName=argv[1];
    if (typeof theName!='string') theName=theName.name;
    if (theForm.$wrap==null) theForm.$wrap={};
    if (theForm.$wrap[theName]==null) theForm.$wrap[theName]=0;

    if (action>0)
      theForm.$wrap[theName]++;
    else if (action<0 && theForm.$wrap[theName]>0)
      theForm.$wrap[theName]--;

    return theForm.$wrap[theName];
  };

horus.wrap    = function () { return horus.$wrap(arguments, 1) };
horus.unwrap  = function () { return horus.$wrap(arguments, -1) };
horus.wrapped = function () { return horus.$wrap(arguments, 0) };


horus.setvar=
  function () {
    var argv=horus.formArgv(arguments, false);
    var elem=horus.formField(argv, false);
    var form=argv[0];
    var value=argv[2];

    if (elem.tagName.toLowerCase()=='select')
      horus.select(elem).set(value);
    else if (elem.type=='radio')
      horus.setradio(form, elem, value);
    else if (elem.type=='checkbox')
      horus.setchecked(form, elem, value);
    else
      elem.value=value==null ? '' : value;

  };


horus.getvar=
  function () {
    var argv=horus.formArgv(arguments, true);
    return horus.getvar.value(horus.formField(argv, false));
  };


horus.getvaropt=
  function () {
    var argv=horus.formArgv(arguments, false);
    return horus.getvar.value(horus.formField(argv, false), argv[2]);
  };


horus.getvar.value=
  function ( elem, filter ) {
    if (!elem) return null;
    var tag=elem.tagName.toLowerCase();

    elem=
      tag=='select' ? horus.select(elem).get() :
      tag=='em' && /(^| )tristate( |$)/.test(elem.parentNode.parentNode.className) ||
      tag=='a' && /(^| )tristate( |$)/.test(elem.className) ? horus.tick.value(elem) :
      elem.type=='radio' ? horus.getradio(elem.form, elem) :
      elem.type=='checkbox' ? horus.getchecked(elem.form, elem) : elem.value;

    if (filter && elem.filter) elem=elem.filter(filter);
    return elem;
  };


horus.form2hash=
  function ( form, exclude ) {
    form=horus.toForm(form);

    var controls=
      horus.getTags.find(form, 'input:type!button!submit!image,textarea,select');

    var data={};
    exclude=new horus.hash(exclude);

    while (controls.length) {
      var control=controls.shift();
      var name=control.name;

      if (name=='' || /___Config$/.test(name) || name in data || name in exclude)
	continue;

      data[name]=document.getElementById(name+'___Config') ?
	FCKeditorAPI.GetInstance(name).GetData() :
	horus.getvar.value(control);

    }

    return data;
  }


horus.getradio=
  function () {
    var argv=horus.formArgv(arguments, true);
    var theRadio=horus.formField(argv, true);
    if (!theRadio) return null;

    for (var i=0; i<theRadio.length; i++)
      if (theRadio[i].checked) return theRadio[i].value;

    return null;
  };


horus.setradio=
  function () {
    var argv=horus.formArgv(arguments, false);
    var theRadio=horus.formField(argv, true);
    var value=argv[2];

    if (value==null)
      for (var i=0; i<theRadio.length; i++)
	if (theRadio[i].checked) {
	  theRadio[i].checked=false;
	  return;
	}

    for (var i=0; i<theRadio.length; i++)
      if (theRadio[i].value==value) {
	theRadio[i].checked=true;
	return;
      }

  };


horus.getchecked=
  function () {
    var argv=horus.formArgv(arguments, true);
    var theBoxes=horus.formField(argv, true);
    if (!theBoxes) return null;
    var checked=[];
    checked.boxes={};

    for (var i=0; i<theBoxes.length; i++) {
      var box=theBoxes[i];
      checked.boxes[box.value]=box.checked;
      if (box.checked) checked.push(box.value);
    }

    return checked;
  };


horus.setchecked=
  function () {
    var argv=horus.formArgv(arguments, false);
    var theBoxes=horus.formField(argv, true);
    var value=argv[2];
    var i;

    if (value!=null)
      if (typeof value=='object') {
	if (value instanceof Array) {
	  var list=value;
	  value={};
	  for (i=0; i<list.length; value[list[i++]]=true);
	}

	for (i=0; i<theBoxes.length; i++)
	  theBoxes[i].checked=horus.hash.get(value, theBoxes[i].value);

      } else if (typeof value=='boolean') {
	for (i=0; i<theBoxes.length; i++) theBoxes[i].checked=value;
      } else {
	for (i=0; i<theBoxes.length; i++)
	  if (theBoxes[i].value==value) theBoxes[i].checked=!theBoxes[i].checked;

      }

  };


horus.clearchecked=
  function () {
    var argv=horus.formArgv(arguments, true);
    argv.push(true);
    var theBoxes=horus.formField(argv, true);
    for (var i=0; i<theBoxes.length; i++) theBoxes[i].checked=false;
  };


horus.select=
  function () {
    if (this instanceof horus.select) {
      var argv=arguments[0];
      if (argv.length==1) this.$select=horus.getElement(argv[0]);
      if (!this.$select) this.$select=horus.formField.apply(argv);
      this.$options=this.$select.options;
      this.$field=this.field();
      this.$form=this.$select.form;
      this.$style=this.$select.style;
    } else
      return new horus.select(arguments);

  };


horus.select.className='horus.select';


horus.select.prototype.field=
  function () {
    if (!horus.iewin) return 'value';

    for (var i=0; i<this.$options.length; i++)
      if (this.$options[i].value!='') return 'value';

    return 'text';
  };


horus.select.prototype.get=
  function ( text ) {
    var field=text ? 'text' : this.$field;

    if (this.$select.type=='select-one') {
      var selectedIndex=this.$select.selectedIndex;
      return selectedIndex>=0 ? this.$options[selectedIndex][field] : null;
    }

    var selected=[];

    for (var i=0; i<this.$options.length; i++)
      if (this.$options[i].selected) selected.push(this.$options[i][field]);

    return selected;
  };


horus.select.prototype.set=
  function ( theValue ) {
    if (horus.wrapped(this.$form, this.$select)) return;
    var oldOption=this.$select.selectedIndex;
    var newOption=-1;

    if (theValue!=null)
      for (var i=0; i<this.$options.length; i++) {
	if (horus.isEqual(this.$options[i][this.$field], theValue)) {
	  newOption=i;
	  break;
	}
      }

    if (newOption!=oldOption) {
      this.$select.selectedIndex=newOption;

      if (this.$select.onchange) {
	horus.wrap(this.$form, this.$select);
	this.$select.onchange(this.$select);
	horus.unwrap(this.$form, this.$select);
      }
    }

    return newOption;
  };


horus.compare=
  function ( a, b ) { return a!=null ? a.compare(b) : b==null ? 0 : -1 };


horus.comparenocase=
  function ( a, b ) { return a!=null ? a.compareNoCase(b) : b==null ? 0 : -1 };


horus.urlparam=
  function ( name, val ) {
    var re=new RegExp('[\?&]' + name + '=([^&]+)', 'i');
    var found=re.exec(window.top.location.search);

    if (!found) {
      re=new RegExp('.*\.cfm.*/' + name + '[.=/]([^?/]+)', 'i');
      found=re.exec(window.top.location.pathname);
    }

    if (found) val=decodeURIComponent(found[1]);
    return val;
  };


horus.getComputedStyle=
  function ( elem, name ) {
    var style;

    if (window.getComputedStyle)
      style=window.getComputedStyle(elem, null);
    else if (elem.currentStyle)
      style=elem.currentStyle;

    return style && name ? style[name] : style;
  };


horus.positioned=
  function ( obj ) {
    if (obj.$positioned==null) {
      var position=horus.getComputedStyle(obj, 'position');

      if (position==undefined ||
	  position=='static' && /^t([dhr]|able)$/i.test(obj.tagName)) {
	for (var child=obj.firstChild;
	     child && !child.offsetParent;
	     child=child.nextSibling);

	obj.$positioned=child && child.offsetParent==obj;
      } else
	obj.$positioned=position!='static';

    }

    return obj.$positioned;
  };


horus.offsetParent=
  function ( obj ) {
    var parent=obj.offsetParent;

    if (horus.brokenDOM || !parent) {
      if (parent && parent.nodeName=='HTML') parent=document.body;

      while (parent && parent!=document.body && !horus.positioned(parent))
	parent=parent.parentNode;

    } else if (!parent || parent.nodeName=='HTML')
      parent=document.body;

    return parent;
  };


horus.inside=
  function ( obj, container ) {
    if (!container) return false;

    while (obj) {
      if (obj==container) return true;
      obj=obj.parentNode;
    }

    return false;
  };


horus.execList=
  function ( thelist ) {
    if (thelist) {
      var delayed=[];

      for (var i=0; i<thelist.length; i++) {
	var theaction=thelist[i];

	if (typeof theaction=='function')
	  theaction();
	else if (theaction instanceof Array) {
	  var offset=0;
	  var argv=[];

	  if (typeof theaction[0]=='number') {
	    var thedelay=theaction[offset++];
	    if (typeof theaction[offset]=='object') argv.push(theaction[offset++]);
	    argv.push(theaction[offset++], thedelay);
	    while (offset<theaction.length) argv.push(theaction[offset++]);
	    delayed.push(argv);
	  } else {
	    var object=typeof theaction=='function' ? window : theaction[offset++];
	    var fn=theaction[offset++];
	    if (typeof fn!='function') fn=object[fn];
	    while (offset<theaction.length) argv.push(theaction[offset++]);
	    fn.apply(object, argv);
	  }
	} else
	  eval(theaction);

	while (delayed.length) horus.setTimeout.apply(window, delayed.shift());
      }
    }
  };


horus.onResize=
  function () {
    if (!horus.onResize.$actions) horus.onResize.$actions=[];

    if (arguments.length==1)
      horus.onResize.$actions.push(arguments[0]);
    else {
      var argv=[];
      for (var i=0; i<arguments.length; i++) argv.push(arguments[i]);
      horus.onResize.$actions.push(argv);
    }
  };


horus.onResize.process=
  function () {
    horus.onResize.pending=null;

    // check if the size has *really* changed to catch spurious IE7 events
    var size=horus.windowSize();
    if (horus.$height==size.height && horus.$width==size.width) return;
    horus.$height=size.height;
    horus.$width=size.width;

    if (horus.onResize.$actions) horus.execList(horus.onResize.$actions);
  };


horus.onResize.event=
  function () {
    if (!horus.onResize.pending)
      horus.onResize.pending=setTimeout(horus.onResize.process, 500);

  };


horus.sizeValue=
  function ( size, units ) {
    if (!size) return '';
    var transform=units=='height' || units=='width';

    if (horus.isNumber(size))
      size=size+(!units || transform ? 'px' : units);

    if (transform && size.right(1)=='%')
      size=Math.floor(horus.windowSize()[units]*Number(size.left(-1))/100)+'px';

    return size;
  };


horus.analytics=
  function ( id ) {
    if (!window._gaq) {
      window._gaq=[];
      var prefix=document.location.protocol=='https:' ? 'https://ssl' : 'http://www';
      horus.script.load(prefix+'.google-analytics.com/ga.js');
    }

    _gaq.push([ horus.sitetag+'._setAccount', id ], [ horus.sitetag+'._trackPageview' ]);
  };


horus.deprecated=
  function ( name, substitute ) {
    var fn=function () { return horus.deprecated.wrapper(name, substitute, arguments) };
    return fn;
  };


horus.deprecated.wrapper=
  function ( name, substitute, argv ) {
    var caller=arguments.callee.caller;
    if (caller) caller=caller.caller;
    if (caller) caller.toString();

    horus.ajax
      ('/common/action/notice.cfm', 'deprecated',
       { name: name, caller: caller, href: location.href }, true);

    return substitute.apply(this, argv);
  };


horus.$optset=0;
horus.$optreset=0;


horus.pageOptions=
  function ( opt ) {
    if (arguments.length==1 || arguments[1])
      horus.$optset|=opt;
    else
      horus.$optreset|=opt;

  };


horus.focusOn=
  function ( item ) {
    if (!horus.$pageLoaded)
      horus.$focusOn=item;
    else if (item)
      horus.focus(item);

  };


horus.onLoad=
  function () {
    if (horus.$pageLoaded)
      horus.execList(arguments);
    else {
      if (!horus.onLoad.$actions) horus.onLoad.$actions=[];
      for (var i=0; i<arguments.length; i++) horus.onLoad.$actions.push(arguments[i]);
    }
  };


horus.onLoad.event=
  function ( opt ) {
    if (!document.body) document.body=document.getElementsByTagName('body')[0];

    if (horus.brokenDOM) {
      if (document.body.className)
	document.body.className+=' ';
      else
	document.body.className='';

      document.body.className+='ie'+Math.floor(horus.ie);
    }

    opt=((opt || 0)|horus.$optset)&~horus.$optreset;
    if (opt&horus.HTML_VISIBLE) horus.visible.put();
    if (opt&horus.HTML_NOBACK) horus.noBack();
    if (opt&horus.HTML_WAITBOX) horus.$waitBox=true;
    if (opt&horus.HTML_SCROLL) horus.scroller();

    if (horus.$focusOn)
      horus.focus(horus.$focusOn);
    else if (opt&horus.HTML_FOCUS)
      horus.focus(window);

    var size=horus.windowSize();
    horus.$height=size.height;
    horus.$width=size.width;
    horus.$pageLoaded=true;
    horus.execList(horus.onLoad.$actions);
    horus.$popflag=true;
  };


horus.onUnload=
  function () {
    if (!horus.onUnload.$actions) {
      horus.onUnload.$actions=[];
      horus.eventListener(window, 'unload', horus.onUnload.event);
    }

    for (var i=0; i<arguments.length; i++) horus.onUnload.$actions.push(arguments[i]);
  };


horus.onUnload.event=
  function ( opt ) {
    horus.execList(horus.onUnload.$actions);
  };


horus.eventListener(window, 'load', horus.onLoad.event);
horus.eventListener(window, 'resize', horus.onResize.event);

