/*
 *  Standard JavaScript © 1996-2010, Horus Web Engineering Ltd
 *
 *  $Id: horus.js,v 1.218 2010-03-15 10:14:55 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.gecko=!horus.opera && !horus.khtml && navigator.userAgent.indexOf('Gecko')>=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;
} else
  horus.iewin=horus.iemac=horus.ieold=horus.brokenDOM=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 () {
      var loading=[];

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

      return loading.length ? loading : null;
    },

  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' ],
   'testurl',  '.testurl',
   'visible',  [ '.visibility', '.visible', '.visible.put' ]);


// implement missing Array methods in Windows IE 5.0
//
horus.script.loadif(!Array.prototype.push, 'ie-win-5.0.js');

if (horus.iewin) {
  // fix broken getElementById method
  //
  horus.script.loadif(horus.brokenDOM, 'ie-getelement-fix.js');

  // 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 ( obj, argv ) {
    if (arguments.length==1 && horus.isArray(obj)) { argv=obj; obj=window }
    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.possessive=
  function () {
    return this+(/s$/.test(this) ? '\'' : '\'s');
  };


String.prototype.trim  = function ()    { return this.replace(/^\s*(.*?)\s*$/, '$1') };
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) };


Number.prototype.trim  = function ()    { return String(this) };
Number.prototype.test  = function ( s ) { return String(this).test(s) };
Number.prototype.left  = function ( c ) { return String(this).left(c) };
Number.prototype.right = function ( c ) { return String(this).right(c) };


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: '.' },
       { string: 'units', numeric: 'decimals', boolean: 'decimal' } );

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

    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=' '+options.units;
    return size+options.units;
  };


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.copy=
  function ( from ) {
    this.length=from.length;
    for (var i=0; i<from.length; i++) this[i]=from[i];
    return this;
  };


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.clean=
  function () {
    for (var i=this.length-1; i>=0; i--)
      if (this[i]=='' || this[i]==null)
	this.splice(i, 1);

    return this;
  };


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


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+\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.callee=='function' && typeof obj.length=='number') return true;
    return false;
  };


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


horus.isNode=
  function ( obj ) {
    if (!obj) return false;

    try { return obj instanceof Node } catch (err) {
      return typeof obj=='object' && obj.nodeType;
    };
  };


horus.isElement=
  function ( obj ) {
    return horus.isNode(obj) && obj.nodeType==1
  };


horus.isWindow=
  function ( obj ) {
    if (!obj) return false;

    try { return obj instanceof Window } catch (err) {
      return typeof obj=='object' && obj.window==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);

    if (horus.isArray(arg)) {
      if (loop) return '[ … ]';
      var text=[];
      for (var i=0; i<arg.length; i++) text.push(horus.toString(arg[i], true, done));
      return '[ '+text.join(', ')+' ]';
    }

    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 (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!=null && tag!='') arg='«'+tag+'»'+arg;
    return arg;
  };


horus._alert=
  function ( argv, offset) {
    var text=[];
    for (var i=offset; i<argv.length; i++) text.push(horus.toString(argv[i]));
    alert(text.join(' '));
  };


horus.alert   = function ()    { horus._alert(arguments, 0) };
horus.alertif = function ( c ) { if (c) horus._alert(arguments, 1) };


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

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

    return (arguments.length<3 ? keys.sort() : sorter ? keys.sort(sorter) : keys);
  };


horus.isEqual=
  function ( a, b ) {
    if (a==b) return true;
    if (a==null || b==null) return false;
    var aa=typeof a;
    if (aa!='object' || aa!=typeof b) 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.hash=function () { horus.hash.merge(this, arguments) };
horus.hash.className='horus.hash';


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

    for (var arg=0; 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);

	      hash[item.substring(0, split)]=val;
	    } else
	      if (item.substring(0, 2)=='no')
		hash[item.substring(2)]=false;
	      else
		hash[item]=true;

	  }
	} else
	  for (var key in from) 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 null;
  };


horus.hash.empty = function ( hash ) { return horus.hash.key(hash)==null };
horus.hash.get   = function ( hash, key, val ) { return key in hash ? hash[key] : val };
horus.hash.add   = function ( hash ) { return horus.hash.merge(hash, arguments, 1) };
horus.hash.copy  = function () { return horus.hash.merge({}, arguments) };


horus.hash.prototype.merge=
  function ( delimiter ) {
    return horus.hash.merge(this, arguments, 1, delimiter);
  };


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


horus.hash.prototype.toString = function () { return horus.toString(this, true) };
horus.hash.prototype.add      = function () { return horus.hash.merge(this, arguments) };
horus.hash.prototype.key      = function () { return horus.hash.key(this) };
horus.hash.prototype.keys     = function () { return horus.keys(this) };
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) {
      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.callable=
  function ( method ) {
    return method && (typeof method=='function' || method instanceof Array);
  };


horus.eventListener=
  function ( element, event, action, object ) {
    if (typeof element=='string') element=document.getElementById(element);
    if (horus.gecko && event=='mousewheel') event='DOMMouseScroll';
    var reqaction=[ action ];

    if (object) {
      var self=object;
      var method=action;
      action=function () { return method.apply(self, arguments) };
      reqaction.push(self, action);
    }

    if (!element._events) element._events={};
    if (!element._events[event]) element._events[event]=[];
    element._events[event].push(reqaction);

    if (window.addEventListener)
      element.addEventListener(event, action, false);
    else if (window.attachEvent)
      element.attachEvent('on'+event, action);
    else {
      var old=element['on'+event];

      element['on'+event]=
	!old ? action :
	function (e) {
          if (!e) e=window.event;
	  old(e, element);
	  return action(e, element);
        };

    }
  };


horus.removeListener=
  function ( element, event, action, object ) {
    if (!element._events) return;
    var events=element._events[event];
    if (!events) return;

    for (var i=0; i<events.length; i++) {
      if (events[i][0]!=action) continue;

      if (object) {
	if (events[i].length<3 || events[i][1]!=object) continue;
	action=events[i][2];
      }

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

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


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

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

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


horus.winscroll=
  function ( width, height, scroll, xpos, ypos ) {
    var options=[];

    if (width) options.push('width='+width);
    if (height) options.push('height='+height);
    if (xpos!=null) options.push((horus.ie ? 'left=' : 'screenX=')+xpos);
    if (ypos!=null) options.push((horus.ie ? 'top=' : 'screenY=')+ypos);

    if (scroll)
      if (scroll instanceof Array) {
	if (scroll[0]) options.push('scrollbars=yes');
	if (scroll[1]) options.push('resizable=yes');
      } else if (horus.isBoolean(scroll)) {
	if (Boolean.parse(scroll)) options.push('scrollbars=yes,resizable=yes');
      } else if (typeof scroll=='object') {
	for (var tag in scroll) options.push(tag+'='+scroll[tag]);
      } else
	options.push(scroll);

    return options.join(',');
  };


horus.placewin=
  function ( name, source, xpos, ypos, width, height, scroll, rethandle ) {
    if (/^opener\./.test(name)) {
      name=name.replace(/^opener\./, '');

      if (opener && opener.horus)
	return opener.horus.placewin
	  (name, source, xpos, ypos, width, height, scroll, rethandle);
	
    }

    var wh=window.open(source, name, horus.winscroll(width, height, scroll, xpos, ypos));
    horus.focus(wh);
    if (rethandle) return wh;
  };


horus.openwin=
  function ( name, source, width, height, scroll, rethandle ) {
    return horus.placewin(name, source, null, null, width, height, scroll, rethandle);
  };


horus.closewin=
  function ( dest ) {
    if (window.opener && window.opener!=window) {
      if (dest) window.opener.document.location.replace(dest);
      window.close();
    } else if (dest)
      window.document.location.replace(dest);

    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;
  };


// Image rollover - normally triggered by onmouseover/onmouseout. The
// assumption is made that the image filename is <something>.<index>.<type>
// where <index> is an integer with zero as the base state.
//
// Parameters:
//   image - Image object, or image name/index within the document
//   over  - (optional) new image index, default 0
//
horus.mouseover=
  function ( image ) {
    var over=arguments.length>1 ? arguments[1] : 0;

    if (image instanceof Array)
      for (var iptr=0; iptr<image.length; iptr++) horus.mouseover(image[iptr], over);
    else {
      if (typeof image=='string') image=document.images[image];

      if (over=='*') {
	var old=Number(image.src.match(/\.(\d+)\.\w+$/)[1]);
	over=old%2 ? old-1 : old+1;
      }

      image.src=image.src.replace(/\.\d+(\.\w+)$/, '.'+over+'$1');
    }
  };


horus.rollover=function ( image ) { horus.mouseover(image, '*') };


// 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.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;
      }
    }

    return size;
  };


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


horus.event=
  function ( event, root ) {
    if (horus.isElement(event))
      event={ target: event, srcElement: event, type: 'call' };
    else if (event && typeof event.length=='number')
      event=event[0];

    if (event && event.rawevent) {
      if (event.root==root) {
	for (var k in event) this[k]=event[k];
	return this;
      }

      event=event.rawevent;
    } 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;
    var pos=horus.windowpos();

    if (root) {
      this.root=root;
      root=horus.getposition(root);
      pos.top-=root.top;
      pos.left-=root.left;
    }

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

    if (window.event) {
      this.target=event.srcElement;
      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.keyCode=event.which;
    }

    return this;
  };


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


horus.event.prototype.control=function () { return this.target };


horus.event.prototype.id=
  function () {
    if (this._id==undefined) this._id=horus.toId(this.target);
    return this._id;
  };


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 (window.event)
      this.rawevent.returnValue=false;
    else
      this.rawevent.preventDefault();

  };


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

  };


horus.mouseinbox=
  function ( event, box ) {
    if (!box) return false;
    event=new horus.event(event, box);

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

  };


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)
      if (theField && theField.form)
	theForm=theField.form;
      else
	for (var i=0; i<document.forms.length; i++)
	  if (theForm==null || document.forms[i].name!='statusboxform') {
	    theForm=document.forms[i];
	    if (!/(^statusboxform$|search)/.test(theForm.name)) break;
	  }

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

      if (theForm.form)
	theForm=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.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 ) {
    node=horus.toId.node(node);

    if (typeof node=='string') {
      if (index) {
	if (!horus.toId.match) horus.toId.match=[];

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

	node=node.replace(horus.toId.match[index], '$1');
      } else
	node=node.replace(/^.*\D(\d*)$/, '$1');

      if (/^\d+$/.test(node)) node=Number(node);
    }

    return node;
  };


horus.toId.node=
  function ( node ) {
    if (node!=null && typeof node=='object') {
      node=horus.getElement(node);

      while (node) {
	if (node.id!=null && node.id!='') { node=node.id; break }
	if (node.name!=null && node.name!='') { node=node.name; break }
	node=node.parentNode;
      }
    }

    return node;
  };


horus.toNode=
  function ( prefix, id ) {
    if (id==null || id=='') return horus.getElement(prefix);
    if (horus.isNode(prefix)) prefix=prefix.id;
    return document.getElementById(prefix.replace(/\d*$/, horus.toId(id)));
  };


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)
	item=new horus.event(item).control();

    return item;
  };


horus._makelink=
  function ( argv ) {
    var url=argv[0];

    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=1; ptr<argv.length; ptr++) {
      var arg=argv[ptr];

      if (arg!=null)
	if (typeof arg=='object')
	  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));

	  }

	else {
	  var point=arg.indexOf('=');

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

	}

    }

    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() };


// 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.reentry     = function () { return horus.doit(horus.FORM_ENTRY,    arguments) };
horus.go          = function () { return horus.doit(horus.FORM_SUBMIT,   arguments) };
horus.reload      = function () { return horus.doit(horus.FORM_REFRESH,  arguments) };
horus.nogo        = function () { return horus.doit(horus.FORM_RESET,    arguments) };
horus.do_new      = function () { return horus.doit(horus.FORM_NEW,      arguments) };
horus.do_add      = function () { return horus.doit(horus.FORM_ADD,      arguments) };
horus.do_delete   = function () { return horus.doit(horus.FORM_DELETE,   arguments) };
horus.do_edit     = function () { return horus.doit(horus.FORM_EDIT,     arguments) };
horus.do_next     = function () { return horus.doit(horus.FORM_NEXT,     arguments) };
horus.do_previous = function () { return horus.doit(horus.FORM_PREVIOUS, arguments) };
horus.do_save     = function () { return horus.doit(horus.FORM_SAVE,     arguments) };
horus.do_restore  = function () { return horus.doit(horus.FORM_RESTORE,  arguments) };
horus.do_payment  = function () { return horus.doit(horus.FORM_PAYMENT,  arguments) };


horus.scroller=
  function () {
    if (document.URL.match(/#/)) return;
    var theForm=horus.formargv(arguments, false)[0];
    if (theForm._scroll) window.scrollTo(0, parseInt(theForm._scroll.value));
  };


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.classbyid  = function ( id, newclass ) { horus.getElement(id).className=newclass };
horus.zindexbyid = function ( id, newz )     { horus.getElement(id).style.zIndex=newz };
horus.getbyid    = function ( id )           { return horus.getElement(id).innerHTML };
horus.setbyid    = function ( id, content )  { horus.getElement(id).innerHTML=content };
horus.addbyid    = function ( id, content )  { horus.getElement(id).innerHTML+=content };


horus.sync=
  function () {
    var argv=horus.formargv(arguments, false);
    horus.set(argv[0], argv[2], horus.formfield(argv).value);
  };


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.setselected(form, elem, 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);
    var elem=horus.formfield(argv, false);
    if (!elem) return null;
    var form=argv[0];
    if (elem.tagName.toLowerCase()=='select') return horus.getselected(form, elem);
    if (elem.type=='radio') return horus.getradio(form, elem);
    if (elem.type=='checkbox') return horus.getchecked(form, elem);
    return horus.formfield(argv).value;
  };


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(form, name);

    }

    return data;
  }


horus.getvaropt=
  function () {
    var argv=horus.formargv(arguments, false);
    var elem=horus.formfield(argv, false);
    if (!elem) return null;
    var thevalue=elem.value;
    var options=horus.options(argv[2]);
    if (options.trim) thevalue=thevalue.trim();
    if (options.encode) thevalue=encodeURIComponent(thevalue);
    return thevalue;
  };


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.brokenselect=
  function ( theSelect ) {
    if (!horus.iewin) return 'value';
    var theOptions=theSelect.options;

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

    return 'text';
  };


horus.getselected=
  function ( theForm, theSelect ) {
    var argv=horus.formargv(arguments, true);
    theSelect=horus.formfield(argv);
    var field=horus.brokenselect(theSelect);
    var theOptions=theSelect.options;

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

    var selected=[];

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

    return selected;
  };


horus._setselected=
  function ( argv ) {
    var theForm=argv[0];
    var theSelect=argv[1];
    if (horus.wrapped(theForm, theSelect)) return;
    var theSelect=horus.formfield(argv);
    var theValue=argv[2];
    var oldOption=theSelect.selectedIndex;
    var newOption=-1;

    if (theValue!=null) {
      var field=horus.brokenselect(theSelect);
      var theOptions=theSelect.options;

      for (var i=0; i<theOptions.length; i++) {
	if (theOptions[i][field]==theValue) {
	  newOption=i;
	  break;
	}
      }
    }

    if (newOption!=oldOption) {
      theSelect.selectedIndex=newOption;

      if (theSelect.onchange) {
	horus.wrap(theForm, theSelect);
	theSelect.onchange();
	horus.unwrap(theForm, theSelect);
      }
    }

    return newOption;
  };


horus.setselected=
  function () {
    var argv=horus.formargv(arguments, false);
    for (var argp=2; argp<argv.length-1 && !argv[argp++]; ++argp);
    argv[2]=argv[argp];
    return horus._setselected(argv);
  };


horus.setselectedif=
  function () {
    var argv=horus.formargv(arguments, true);

    if (argv[2]) {
      argv[2]=argv[3];
      return horus._setselected(argv);
    }
  };


horus.selectall=
  function ( theForm, theSelect ) {
    var argv=horus.formargv(arguments, true);
    theSelect=horus.formfield(argv);
    var theOptions=theSelect.options;
    for (var i=0; i<theOptions.length; theOptions[i++].selected=true);
  };


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


horus.comparenocase=
  function ( a, b ) {
    if (a==null) return b==null ? 0 : -1;
    if (b==null) return 1;
    a=a.toLowerCase();
    b=b.toLowerCase();
    return a>b ? -1 : a<b ? 1 : 0;
  };


horus.seladd=
  function ( theForm, theSelect, theValue, options, sorter ) {
    var argv=horus.formargv(arguments, false);
    theSelect=horus.formfield(argv);
    theValue=argv[2];
    options=horus.options(argv[3]);
    sorter=argv[4];
    var set=options.select || options.replace;

    if (options.clear)
      theSelect.options.length=0;
    else if (options.select && theSelect.type=='select-one' || options.replace)
      theSelect.selectedIndex=-1;

    if (typeof sorter!='function')
      sorter=sorter ? horus.compare : horus.comparenocase;

    if (theValue instanceof Array)
      for (var i=0; i<theValue.length; i++) {
	var newopt=theValue[i];
	if (typeof newopt!='object') newopt=[ newopt ];
	horus.seladd.insert(theSelect, newopt, set, sorter);
      }
    else if (horus.isSimpleValue(theValue))
      horus.seladd.insert(theSelect, theValue, set, sorter);
    else if (theValue!=null)
      for (var tag in theValue)
	horus.seladd.insert(theSelect, [ theValue[tag], tag ], set, sorter);

    return theValue;
  };


horus.seladd.insert=
  function ( select, newopt, set, oldopt ) {
    var value, text;
    if (typeof newopt=='string') newopt=newopt.split(':', 2);

    if (newopt instanceof Array) {
      text=newopt.length<1 ? '' : newopt[0];
      value=newopt.length<2 ? text : newopt[1];
      newopt=new Option(text, value, false, false);
    } else {
      text=newopt.text;
      value=newopt.value;
    }

    select=horus.getElement(select);
    var options=select.options;

    if (typeof oldopt=='function') {
      var sorter=oldopt;
      oldopt=undefined;

      for (var ptr=0; ptr<options.length; ptr++) {
	var opt=options[ptr];

	if (sorter(text, opt.text, value, opt.value)>0) {
	  oldopt=horus.brokenDOM ? ptr : opt;
	  break;
	}
      }
    } else
      if (oldopt==null)
	oldopt=undefined;
      else if (typeof oldopt=='number' && !horus.brokenDOM)
	oldopt=options[oldopt];
      else if (horus.brokenDOM && typeof oldopt!='number') {
	for (ptr=0; ptr<options.length; ptr++)
	  if (options[ptr]==oldopt) {
	    oldopt=ptr;
	    break;
	  }

	if (typeof oldopt!='number') oldopt=undefined;
      }

    if (horus.brokenDOM && oldopt==null)
      newopt=options[options.length]=new Option(newopt.text, newopt.value, false, false);
    else
      select.add(newopt, oldopt);

    newopt.selected=set;
  };


horus.selshift=
  function ( theForm, source, dest, sorter ) {
    var argv=horus.formargv(arguments, true);
    theForm=argv[0];
    source=horus.formfield(theForm, argv[1]);
    if (source.selectedIndex<0) return;
    dest=horus.formfield(theForm, argv[2]);
    sorter=argv[3];
    var sourceopt=source.options;
    var sourceptr=sourceopt.length;
    var shifter=[];

    while (--sourceptr>=0) {
      var s=sourceopt[sourceptr];

      if (s.selected) {
	shifter.push(s);
	sourceopt[sourceptr]=null;
      }
    }

    return horus.seladd(theForm, dest, shifter, 'replace', sorter);
  };


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=found[1];
    return val;
  };


horus.positioned=
  function ( obj ) {
    if (obj._positioned==null)
      if (window.getComputedStyle)
	obj._positioned=getComputedStyle(obj, '').position!='static';
      else {
	for (var child=obj.firstChild;
	     child && !child.offsetParent;
	     child=child.nextSibling);

	obj._positioned=child && child.offsetParent==obj;
      }

    return obj._positioned;
  };


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

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

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

    }

    return parent;
  };


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

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

    return false;
  };


horus.getposition=
  function ( obj, root, debug ) {
    obj=horus.getElement(obj);
    if (!obj) return null;
    var pos={ height: obj.offsetHeight, width: obj.offsetWidth };

    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 (!(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.getheight=
  function () {
    var theheight=0;

    var items=arguments.length==1 && arguments[0] instanceof Array
      ? arguments[0] : arguments

    for (var i=0; i<items.length; i++) {
      var item=items[i];
      var add;
      var sub;
      var thisheight;

      if (item instanceof Array) {
	add=item[1];
	sub=item[2];
	item=item[0];
      }

      if (!item) continue;

      if (item=='*')
	thisheight=horus.windowsize().height;
      else {
	item=horus.getElement(item);
	thisheight=item.offsetHeight;
      }

      if (sub) {
	if (typeof sub!='number')
	  if (typeof add!='number')
	    sub=horus.getposition(add, sub).top;
	  else
	    sub=horus.getposition(sub, item).top;

	thisheight-=sub;
      }

      if (add) {
	if (typeof add!='number') add=horus.getposition(item, add).top;
	thisheight+=add;
      }

      if (thisheight>theheight) theheight=thisheight;
    }

    return theheight;
  };


horus.afterload=
  function () {
    if (horus._pageloaded)
      horus.execlist(arguments);
    else {
      if (!horus.loadaction) horus.loadaction=[];
      for (var i=0; i<arguments.length; i++) horus.loadaction.push(arguments[i]);
    }
  };


horus.resizeaction=
  function () {
    if (!horus.resizeactions) horus.resizeactions=[];

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


horus.execlist=
  function ( thelist ) {
    if (thelist)
      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++]);
	    horus.setTimeout.apply(window, 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);

      }

  };


horus.reallydoresize=
  function () {
    horus.resizepending=null;

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

    horus.execlist(horus.resizeactions);
  };


horus.doresize=
  function () {
    if (horus.resizeactions && !horus.resizepending)
      horus.resizepending=setTimeout(horus.reallydoresize, 500);

  };


horus.sizevalue=
  function ( v, units ) {
    if (!horus.isNumber(v)) return v;
    if (!units) units='px';
    return v+units;
  };


horus.matchheight=
  function ( matcher ) {
    if (arguments.length>1) {
      var thismatch=new Array(arguments.length);
      for (var i=0; i<arguments.length; i++) thismatch[i]=arguments[i];

      if (window.heightmatch)
	window.heightmatch.push(thismatch);
      else {
	window.heightmatch=[ thismatch ];
	horus.resizeaction(horus.matchheight.call);
      }
    }

    horus.matchheight.call();
  };


horus.matchheight.match=
  function ( matcher ) {
    heightmatch.pending=false;

    if (!horus._pageloaded) {
      setTimeout(horus.matchheight.call, 500);

      if (horus.ieold) {
	heightmatch.active=false;
	return;
      }
    }

    var matcher=window.heightmatch;

    for (var setindex=0; setindex<matcher.length; setindex++) {
      var set=matcher[setindex];
      var theheight=0;
      var maxheight=0;
      var argv=[];

      for (var i=0; i<set.length; i++) {
	var item=set[i];
	var thisheight;

	if (item=='*' || item=='.') {
	  thisheight=horus.windowsize().height-1;
	  if (item=='.') maxheight=thisheight;
	} else if (typeof item=='number') {
	  thisheight=item;
	} else {
	  var adjust=0;
	  var skip=0;

	  if (item instanceof Array) {
	    if (item.length>1) adjust=item[horus.ieold && item.length>2 ? 2 : 1];
	    item=item[0];
	  }

	  if (typeof item=='string')
	    if (item=='*' || item=='.') {
	      skip=2;
	      thisheight=horus.windowsize().height+adjust-1;
	      if (item=='.') maxheight=thisheight;
	    } else {
	      if (item.charAt(0)=='-') {
		skip=1;
		item=item.substring(1);
	      }

	      item=document.getElementById(item);
	    }

	  if (skip<2) {
	    if (!item) continue;
	    item.style.height=null;
	    var data=horus.getposition(item);
	    thisheight=data.top+data.height+adjust;

	    if (skip==0) {
	      data.item=item;
	      data.adjust=adjust;
	      argv.push(data);
	    }
	  }
	}

	if (thisheight>theheight) theheight=thisheight;
      }

      if (maxheight>0) theheight=maxheight;

      for (var i=0; i<argv.length; i++) {
	var data=argv[i];
	data.item.style.height=(theheight-data.top-data.adjust)+'px';
      }
    }

    if (heightmatch.pending)
      setTimeout(horus.matchheight.match, 10);
    else
      heightmatch.active=false;

  };


horus.matchheight.call=
  function () {
    if (!window.heightmatch) return;

    if (heightmatch.active) {
      heightmatch.pending=true;
      return;
    }

    heightmatch.active=true;
    setTimeout(horus.matchheight.match, 10);
  };


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._optset=0;
horus._optreset=0;


horus.pageoptions=
  function ( opt ) {
    if (arguments.length==1 || arguments[1])
      horus._optset|=opt;
    else
      horus._optreset|=opt;

  };


horus.waittext=
  function ( thetext ) {
    window._waittext=thetext;
    horus.pageoptions(horus.HTML_WAITBOX, thetext!='');
  };


horus.pageloaded=
  function ( opt ) {
    if (!document.body) document.body=document.getElementsByTagName('body')[0];
    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) window._waitbox=true;
    if (opt&horus.HTML_SCROLL) horus.scroller();
    if (opt&horus.HTML_FOCUS) horus.focus(window);
    horus._pageloaded=true;
    horus.execlist(horus.loadaction);
    horus._popflag=true;
  };


horus.eventListener(window, 'load', horus.pageloaded);
horus.eventListener(window, 'resize', horus.doresize);
