﻿/*
 *  DOM munging stuff © 2006-2010, Horus Web Engineering Ltd
 *
 *  $Id: dom.js,v 1.96 2010-06-04 11:29:26 horus Exp $
 *
 *  in any sane world, most the following would be methods on Node or Element
 *
 *  licensed under the terms of the GNU Lesser General Public License:
 *    http://www.opensource.org/licenses/lgpl-license.php
 *
 *  needs horus.js
 *  uses tooltip.js if the "tooltip" pseudo-attribute is used
 *
 */


horus.getTags=function () { return horus.getTags._find(null, arguments, 0) };


horus.getTags.parsed={};
horus.getTags.input='input:type!hidden,textarea,select,button,a.tristate';


horus.getTags.parse=
  function ( item ) {
    if (typeof item=='object') return item;
    if (typeof item=='boolean') return null;
    if (!(typeof item=='string' && /[,:.¬<#]/.test(item))) return item;
    if (horus.getTags.parsed[item]) return horus.getTags.parsed[item];
    var match=item.split(/, */, true);

    // tag#root<idprefix.class¬class:attr
    for (var i=0; i<match.length; i++) {
      var tag=match[i];
      var point=tag.indexOf(':');
      var attributes;
      var outclass;
      var inclass;
      var prefix;
      var parent;

      if (point>=0) {
	var attr=tag.right(-point-1).split(':');
	attributes={}

	for (var a=0; a<attr.length; a++) {
	  var list=attr[a].split(/([=!])/, true);
	  var name=list.shift();
	  var values=[];

	  while (list.length) {
	    var negate=list.shift()=='!';
	    var value=horus.toObject(list.shift());
	    value.negate=negate;
	    values.push(value);
	  }

	  attributes[name]=values;
	}

	tag=tag.left(point);
      }

      point=tag.indexOf('¬');

      if (point>=0) {
	outclass=tag.right(-point-1).split('¬');
	if (outclass.length<2) outclass=outclass[0];
	tag=tag.left(point);
      }

      point=tag.indexOf('.');

      if (point>=0) {
	inclass=tag.right(-point-1).split('.');
	if (inclass.length<2) inclass=inclass[0];
	tag=tag.left(point);
      }

      point=tag.indexOf('<');

      if (point>=0) {
	prefix=tag.right(-point-1).split('<');
	for (var j=0; j<prefix.length; j++) prefix[j]=new RegExp('^'+prefix[j]);
	if (prefix.length<2) prefix=prefix[0];
	tag=tag.left(point);
      }

      point=tag.indexOf('#');

      if (point>=0) {
	parent=document.getElementById(tag.right(-point-1));
	tag=tag.left(point);
      }

      if (tag && !parent && !prefix && !inclass && !outclass && !attributes)
	match[i]=tag.toLowerCase();
      else {
	var key={};
	if (tag) key.tag=tag.toLowerCase();
	if (parent) key.parent=parent;
	if (prefix) key.prefix=prefix;
	if (inclass) key.inclass=inclass;
	if (outclass) key.outclass=outclass;
	if (attributes) key.attributes=attributes;
	match[i]=key;
      }
    }

    match.match=item;
    horus.getTags.parsed[item]=match;
    return match;
  };


horus.getTags.prefix=
  function ( prefix, match ) {
    if (horus.isNode(prefix)) prefix=prefix.id.replace(/\d+$/, '');
    prefix=horus.getTags.parse('<'+prefix);
    if (match) prefix=horus.hash.copy(horus.getTags.parse(match), prefix);
    return prefix;
  };


horus.getTags.test=
  function ( key, node ) {
    var ok=true;

    if (typeof key=='string')
      ok=key==node.tagName.toLowerCase();
    else {
      if (key.tag) ok=key.tag==node.tagName.toLowerCase();

      if (ok && key.attributes)
	for (var name in key.attributes) {
	  var values=key.attributes[name];
	  var found=0;

	  for (var i=0; i<values.length; i++) {
	    var value=values[i];

	    if (name in node)
	      if (value=='*' || node[name]==value) {
		if (value.negate) { ok=false; break }
		found=2;
	      } else if (found<2)
		if (!value.negate)
		  found=-1
		else if (!found)
		  found=1;

	  }

	  if (ok) ok=found>0;
	  if (!ok) break;
	}

      if (ok && key.inclass)
	if (key.inclass instanceof Array)
	  for (var j=0; j<key.inclass.length && ok; j++)
	    ok=horus.hasClass(node, key.inclass[j]);

	else
	  ok=horus.hasClass(node, key.inclass);

      if (ok && key.outclass)
	if (key.outclass instanceof Array)
	  for (var j=0; j<key.outclass.length && ok; j++)
	    ok=!horus.hasClass(node, key.outclass[j]);

	else
	  ok=!horus.hasClass(node, key.outclass);

      if (ok && key.prefix) {
	var id=node.id;

	if (!id)
	  ok=false;
	else if (key.prefix instanceof Array)
	  for (var j=0; j<key.prefix.length && ok; j++)
	    ok=key.prefix[j].test(id);

	else
	  ok=key.prefix.test(id);

      }
    }

    return ok;
  };


horus.getTags.check=
  function ( match, node ) {
    if (!node) return undefined;
    var ok=!match;

    if (!ok)
      if (match instanceof Array)
	for (var i=0; i<match.length && !ok; i++) ok=horus.getTags.test(match[i], node);
      else
	ok=horus.getTags.test(match, node);

    return ok;
  };


horus.getTags.walk=
  function ( match, node, found ) {
    for (node=horus.firstTag(node); node; node=horus.nextTag(node)) {
      if (horus.getTags.check(match, node)) found.push(node);
      horus.getTags.walk(match, node, found);
    }
  };


horus.getTags._match=
  function ( from, key, found ) {
    var istag=typeof key=='string';

    if (!from && (istag || key.tag && !key.parent)) {
      var nodes=document.getElementsByTagName(istag ? key : key.tag);

      for (var j=0; j<nodes.length; j++) {
	var node=nodes[j];
	if (horus.getTags.check(key, node)) found.push(node);
      }
    } else
      horus.getTags.walk(key, key.parent || from || document.body, found);

  };


horus.getTags._find=
  function ( from, argv, offset ) {
    var match=[];

    for (var a=offset; a<argv.length; a++) {
      var arg=argv[a];
      if (arg==null) continue;
      if (horus.isString(arg)) arg=horus.getTags.parse(arg);
      match.addList(arg);
    }

    var found=[];
    for (var i=0; i<match.length; i++) horus.getTags._match(from, match[i], found);
    return found;
  };


horus.getTags.find=
  function ( from ) {
    return horus.getTags._find(horus.getElement(from), arguments, 1);
  };


horus.getTags.params=
  function ( node, match, control ) {
    node=horus.getElement(node);
    if (!node) return undefined;
    var params={ node: node, match: null, count: 1, up: false };

    if (horus.isNumber(match))
      params.count=Number(match);
    else {
      var test=horus.typeOf(match, true);

      if (test=='boolean' || test=='number')
	control=match;
      else {
	params.match=horus.getTags.parse(match);
	test=horus.typeOf(control, true);
      }

      if (control!=null)
	if (test=='number')
	  params.count=Number(control);
	else if (test='boolean')
	  params.up=Boolean.parse(control);
	else if (test=='string') {
	  control=control.split(':', true);

	  while (control.length) {
	    var c=control.shift();

	    if (horus.isNumber(c))
	      params.count=Number(c);
	    else if (horus.isBoolean(c, true))
	      params.up=Boolean.parse(c);
	    else
	      throw 'illegal DOM search control parameter';

	  }
	} else
	  throw 'illegal DOM search control parameter';

    }

    return params;
  };


horus.parentTag=
  function ( node, match, count ) {
    var tag=horus.getTags.params(node, match, count);
    if (!tag) return undefined;
    if (tag.count==0 && horus.getTags.check(tag.match, tag.node)) return tag.node;

    while (tag.node) {
      tag.node=tag.node.parentNode;
      if (!tag.node.tagName) return null;
      if (horus.getTags.check(tag.match, tag.node) && --tag.count<=0) break;
    }

    return tag.node;
  };


horus.firstTag=
  function ( parent, match, count ) {
    var tag=horus.getTags.params(parent, match, count);
    if (!tag) return undefined;

    for (tag.node=tag.node.firstChild; tag.node; tag.node=tag.node.nextSibling) {
      if (tag.node.nodeType!=1) continue;
      if (horus.getTags.check(tag.match, tag.node) && --tag.count<=0) break;
    }

    return tag.node;
  };


horus.onlyChild=
  function ( node ) {
    node=horus.getElement(node);
    if (!node) return null;
    if (horus.firstTag(node.parentNode)!=node) return false;
    do node=node.nextSibling; while (node && node.nodeType!=1);
    return node==null;
  };


horus.previousTag=
  function ( node, match, count ) {
    var tag=horus.getTags.params(node, match, count);
    if (!tag) return undefined;

    do {
      do {
	if (tag.node.previousSibling || !tag.up)
	  tag.node=tag.node.previousSibling;
	else
	  tag.node=tag.node.parentNode;

      } while
	(tag.node &&
	 (tag.node.nodeType!=1 || !horus.getTags.check(tag.match, tag.node)));

    } while (tag.node && --tag.count>0);

    return tag.node;
  };


horus.nextTag=
  function ( node, match, count ) {
    var tag=horus.getTags.params(node, match, count);
    if (!tag) return undefined;

    do {
      do {
	if (tag.up)
	  while (tag.node && !tag.node.nextSibling)
	    tag.node=tag.node.parentNode;

	if (tag.node) tag.node=tag.node.nextSibling;
      } while
	(tag.node &&
	 (tag.node.nodeType!=1 || !horus.getTags.check(tag.match, tag.node)));

    } while (tag.node && --tag.count>0);

    return tag.node;
  };


horus.checkTag=
  function ( node, match ) {
    return horus.getTags.check(horus.getTags.parse(match), horus.getElement(node));
  };


horus.$fromTag=
  function ( node, up ) {
    if (typeof up!='boolean') return horus.parentTag(node, up);
    if (up) return horus.parentTag(node);
    return node;
  };


horus.$findTag=
  function ( node, argv ) {
    var argc=argv.length;
    var offset=1;
    var up=argv[offset];

    node=horus.getElement(node);

    if (typeof up=='number' || typeof up=='boolean') {
      if (up) node=horus.$fromTag(node, up);
      offset++;
    }

    while (node && offset<argc) {
      var match=argv[offset++];
      var count=typeof argv[offset]=='number' ? argv[offset++] : 1;
      node=horus.firstTag(node, match, count);
    }

    return node;
  };


// node{,  up}, (match{, count})*
horus.findTag=function ( node ) { return horus.$findTag(node, arguments) };


horus.findText=
  function ( node ) {
    node=horus.$findTag(node, arguments);
    return node ? horus.childText(node) : null;
  };


horus.$searchTag=
  function ( node, match, nomatch ) {
    for (node=horus.firstTag(node); node; node=horus.nextTag(node)) {
      if (horus.getTags.check(match, node) &&
	  !(nomatch && horus.getTags.check(nomatch, node)))
	return node;

      var found=horus.$searchTag(node, match, nomatch);
      if (found) return found;
    }

    return null;
  };


horus.searchTag=
  function ( node, up, match, nomatch ) {
    node=horus.$fromTag(horus.getElement(node), up);
    if (!node) return undefined;
    match=horus.getTags.parse(match);
    if (nomatch) nomatch=horus.getTags.parse(nomatch);
    return horus.$searchTag(node, match, nomatch);
  };


horus.copyAttr=
  function ( to, from, tag, datatype ) {
    var value=from.getAttribute(tag);

    if (datatype)
      switch (datatype) {

      case 'Boolean': value=horus.toBoolean(result); break;
      case 'numeric': value=Number(result);          break;
      case 'list':    value=value.split(',', true);  break;
      case 'date':    value=horus.toDate(value);     break;

      }

    to[tag]=value;
  };


horus.$toNode=
  function ( argv, argc ) {
    if (argc==null) argc=argv.length;
    var node='';

    if (argc==1)
      node=argv[0];
    else
      for (var i=0; i<argc; i++) {
	var item=argv[i];

	if (i%2==0) {
	  if (horus.isNode(item)) item=item.id;
	  node+=item.replace(/\d+$/, '');
	} else if (item!=null && item!='')
	  node+=horus.toId(item);

      }

    return horus.getElement(node);
  };


horus.removeNode=
  function () {
    var argc=arguments.length;
    var text=argc>1 && typeof arguments[argc-1]=='boolean' ? arguments[--argc] : false;
    var node=horus.$toNode(arguments, argc);

    if (node) {
      if (text && node.previousNode && node.previousNode.nodeType==3)
	node.parentNode.removeChild(node.previousNode);

      node.parentNode.removeChild(node);
    }

    return node;
  };


horus.removeListNode=
  function () {
    node=horus.$toNode(arguments);
    var match=horus.getTags.prefix(node);
    var className=node.className;
    var nextNode=horus.nextTag(node, match);
    var edgeNode=horus.previousTag(node, match);
    if (nextNode) edgeNode=edgeNode ? null : nextNode;
    horus.removeNode(node);

    while (nextNode) {
      var nextClass=nextNode.className;
      nextNode.className=className;
      className=nextClass;
      nextNode=horus.nextTag(nextNode, match);
    }

    return edgeNode;
  };


horus.removeContents=
  function ( parent, nbsp ) {
    parent=horus.getElement(parent);
    while (parent.firstChild) parent.removeChild(parent.firstChild);
    if (nbsp) parent.appendChild(document.createTextNode(horus.NBSP));
  };


horus.setAttribute=
  function ( node, tag, value, defer ) {
    if (horus.brokenDOM)
      horus.iefix.setAttribute(node, tag, value, defer);
    else if (typeof value=='function')
      node[tag]=value;
    else
      node.setAttribute(tag, value);

  };


horus.setId=
  function ( node, id, pattern ) {
    if (!pattern) pattern=/[0-9]+$/;

    for (var child=node.firstChild; child; child=child.nextSibling)
      horus.setId(child, id, pattern);

    if (node.id) node.id=node.id.replace(pattern, id);
    if (node.name) node.name=node.name.replace(pattern, id);
    if (node.useMap) node.useMap=node.useMap.replace(pattern, id);
  };


horus.findNode=
  function ( node, id ) {
    if (node.id && node.id==id) return node;

    for (node=node.firstChild; node; node=node.nextSibling) {
      var found=horus.findNode(node, id);
      if (found) return found;
    }
  };


Array.prototype.breakList=
  function ( argv, offset ) {
    for (var i=offset==null || offset<0 ? 0 : offset; i<argv.length; i++) {
      var text=argv[i];
      var item;

      if (horus.isString(text)) {
	item=text.split(/(\n)/, true);

	for (var j=0; j<item.length; j++)
	  this.addString(item[j]=='\n' ? horus.BR : item[j]);

      } else
	if (!horus.isArray(text))
	  this.push(text);
	else if (offset<0)
	  this.breakList(text, offset+1);
	else {
	  item=[].breakList(text, 2);
	  item.unshift(text[0], text[1]);
	  this.push(item);
	}

    }

    return this;
  };


Array.prototype.breakText=function () { return this.breakList(arguments) };
horus.breakText=function () { return [].breakList(arguments) };
horus.breakList=function ( argv, offset ) { return [].breakList(argv, offset) };


horus.makeNode=
  function ( node ) {
    if (node instanceof Array) {
      var defer=horus.brokenDOM ? [] : null;
      node=horus.$createElement(node, null, null, defer);
      if (defer) horus.iefix.deferred(defer);
      return node;
    }

    if (horus.isNode(node)) return node;
    return document.createTextNode(node);
  };


horus.$cloneNode=
  function ( node, id, parent, before ) {
    node=node.cloneNode(true);
    if (id) horus.setId(node, id);

    if (parent)
      if (before)
	parent.insertBefore(node, before);
      else
	parent.appendChild(node);

    return node;
  };


horus.cloneNode=
  function ( node, id, parent, before ) {
    if (horus.brokenDOM && parent)
      return horus.iefix.cloneNode(node, id, parent, before);
    else
      return horus.$cloneNode(node, id, parent, before);

  };


// code from Anthony Holdener http://www.alistapart.com/articles/crossbrowserscripting/
horus.importNode=
  function ( node, deep ) {
    node=horus.getElement(node);

    switch (node.nodeType) {

    case document.ELEMENT_NODE:
      var newNode=document.createElement(node.nodeName);

      if (node.attributes && node.attributes.length)
	for (var a=0; a<node.attributes.length; a++)
	  newNode.setAttribute
	    (node.attributes[a].nodeName,
	     node.getAttribute(node.attributes[a].nodeName));

      if (deep && node.childNodes && node.childNodes.length)
	for (var n=0; n<node.childNodes.length; n++)
	  newNode.appendChild(horus.importNode(node.childNodes[n], true));

      return newNode;
      break;

    case document.TEXT_NODE:
    case document.CDATA_SECTION_NODE:
    case document.COMMENT_NODE:
      return document.createTextNode(node.nodeValue);
      break;

    }
  };


horus.$appendChild=
  function ( parent, argv, offset, before, defer ) {
    var argc=argv.length;

    if (offset==null)
      offset=1;
    else if (offset<0) {
      argc+=offset;
      offset=0;
    }

    if (argc-offset==2) {
      var test=argv[argc-1];

      if (typeof test=='boolean')
	if (test && argv[offset] instanceof Array) {
	  argv=argv[offset];
	  offset=0;
	  argc=argv.length;
	} else
	  --argc;

    }
      
    var result;
    var final=false;

    for (var i=offset; i<argc; i++) {
      var node=argv[i];

      if (node instanceof Array) {
	result=horus.$createElement(node, parent, before, defer);
	final=true;
      } else {
	var save;
	var munge=false;

	if (horus.isNode(node)) {
	  save=!final;
	  munge=horus.brokenDOM && !node.parentNode;
	} else {
	  save=!result;
	  node=document.createTextNode(node);
	}

	if (before)
	  parent.insertBefore(node, before);
	else
	  parent.appendChild(node);

	if (munge) node=horus.iefix.checkNode(node);
	if (save) result=node;
      }
    }

    return result;
  };


horus.appendChild=
  function ( parent ) {
    parent=horus.getElement(parent);
    var defer=horus.brokenDOM ? [] : null;
    var node=horus.$appendChild(parent, arguments, 1, null, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.replaceContent=
  function ( parent ) {
    parent=horus.getElement(parent);
    horus.removeContents(parent);
    return horus.$appendChild(parent, arguments);
  };


horus.replaceHTML=
  function ( parent, html ) {
    parent=horus.getElement(parent);
    horus.removeContents(parent);
    var node=horus.$createElement([ 'div' ]);
    node.innerHTML=html; // sigh
    var result;
    var found=false;

    while (node.firstChild) {
      var next=parent.appendChild(node.firstChild);

      if (!found) {
	found=horus.isElement(next);
	if (found || !result) result=next;
      }
    }

    return result;
  };


horus.insertChild=
  function ( parent, node ) {
    var root=arguments.length==1;
    parent=horus.getElement(parent, root);

    if (root)
      return horus.$appendChild(document.body, [ parent ], 0, document.body.firstChild);

    var defer=horus.brokenDOM ? [] : null;
    var before=parent.firstChild;
    var node=horus.$appendChild(parent, arguments, 1, before, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.bracketChildren=
  function ( parent, before, after ) {
    parent=horus.getElement(parent);
    horus.insertChild(parent, before);
    horus.appendChild(parent, after);
  };


horus.insertBefore=
  function ( node ) {
    var before=horus.getElement(arguments[arguments.length-1]);
    var parent=before.parentNode;
    var defer=horus.brokenDOM ? [] : null;
    node=horus.$appendChild(parent, arguments, -1, before, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.appendAfter=
  function ( after, node ) {
    after=horus.getElement(after);
    var parent=after.parentNode;
    var before=after.nextSibling;
    var defer=horus.brokenDOM ? [] : null;
    node=horus.$appendChild(parent, arguments, 1, before, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.replaceNode=
  function ( oldnode, newnode ) {
    newnode=horus.makeNode(newnode);
    var parent=oldnode.parentNode;
    var munge=horus.brokenDOM && !newnode.parentNode;
    parent.replaceChild(newnode, oldnode);
    if (munge) newnode=horus.iefix.checkNode(newnode);
    return newnode;
  };


horus.$textNode=
  function ( parent, create ) {
    if (create) parent.normalize();
    for (var node=parent.firstChild; node && node.nodeType!=3; node=node.nextSibling);

    if (!node && create) {
      var text=document.createTextNode('');

      if (parent.firstChild)
	node=parent.insertBefore(text, parent.firstChild);
      else
	node=parent.appendChild(text);

    }

    return node;
  };


horus.deleteText=
  function ( parent, offset, count ) {
    parent=horus.getElement(parent);
    if (!parent) return undefined;
    var node=horus.$textNode(parent, true);

    if (node.length) {
      if (offset==null) offset=0;
      if (count==null) count=node.length;
      node.deleteData(offset, count);
    }

    return node;
  };


horus.appendText=
  function ( parent, text ) {
    parent=horus.getElement(parent);
    if (!parent) return undefined;
    var node=horus.$textNode(parent, true);
    node.appendData(text);
    return node;
  };


horus.childText=
  function ( parent ) {
    parent=horus.getElement(parent);
    if (!parent) return undefined;
    parent.normalize();
    var text='';
    var node;
    var argc=arguments.length;

    if (argc==1 || argc==2 && typeof arguments[1]=='boolean') {
      var deep=arguments[1];

      for (node=parent.firstChild; node; node=node.nextSibling)
	if (node.nodeType==3)
	  text+=node.data.replace(/[ \n\r\t]+/g, ' ');
	else if (node.tagName.toLowerCase()=='br')
	  text+='\n';
	else if (deep)
	  text+=horus.childText(node, true);

      text=text.trim().replace(/  +/g, ' ');
      if (deep) text=text.replace(/ ?\n ?/g, '\n');
    } else {
      var addtext=null;
      var i;

      if (typeof arguments[argc-1]=='boolean') {
	addtext=arguments[--argc];
	if (addtext) text=' ';
      }

      if (argc==2 && horus.isArray(arguments[1])) {
	argv=arguments[1];
	argc=argv.length;
	i=0;
      } else {
	argv=arguments;
	i=1;
      }

      while (i<argc) text+=argv[i++];
      if (addtext!=null && !addtext) text+=' ';
      text=document.createTextNode(text);

      if (!parent.firstChild || addtext)
	parent.appendChild(text);
      else if (addtext!=null && !addtext)
	parent.insertBefore(text, parent.firstChild);
      else {
	var found=[];

	for (node=parent.firstChild; node; node=node.nextSibling)
	  if (node.nodeType==3) found.push(node);

	if (!found.length)
	  parent.insertBefore(text, parent.firstChild);
	else {
	  parent.replaceChild(text, found.shift());
	  while (found.length) parent.removeChild(found.shift());
	}
      }
    }

    return text;
  };


horus.$createElement=
  function ( argv, parent, before, defer ) {
    var tagname=argv[0];
    var attributes=argv[1];
    var hasclass=false;

    try {
      var node=document.createElement(tagname);
    } catch (err) {
      horus.alert("can't create", tagname, '-', err);
    }

    if (typeof attributes=='string')
      attributes=attributes.left(1)=='.' ?
	{ classname: attributes.right(-1) } : { id: attributes };

    else if (!attributes)
      attributes={};

    for (var attr in attributes) {
      var value=attributes[attr];

      if (defer && /^(name|usemap|on.*)$/i.test(attr))
	defer.push([ node, attr, value ]);

      switch (attr.toLowerCase()) {

      case 'style':
	for (var name in value) node.style[name]=value[name];
	break;

      case 'width':
      case 'height':
	if (horus.isNumber(value)) value=value+'px';
      case 'display':
      case 'visibility':
      case 'color':
	node.style[attr]=value;
	break;

      case 'backgroundcolor':
	node.style.backgroundColor=value;
	break;

      case 'class':
      case 'classname':
	node.className=value;
	hasclass=true;
	break;

      case 'usemap':	  node.useMap=value;	  break;
      case 'colspan':	  node.colSpan=value;	  break;
      case 'rowspan':	  node.rowSpan=value;	  break;

      case 'tooltip':
	node.onmouseover=horus.tooltip.show(value);
	break;

      default:
	horus.setAttribute(node, attr, value, defer);

      }
    }

    if (tagname=='input') {
      if (!hasclass) node.className=node.type;
      if (attributes.type==null) node.type='text';
    }

    if ((tagname=='input' || tagname=='textarea' || tagname=='select') &&
	attributes.name==null)
      node.name=node.id;

    if ((tagname=='img' || tagname=='area' ||
	 tagname=='input' && attributes.type=='image') && attributes.alt==null)
      node.alt=node.title;

    if ((tagname=='a' || tagname=='area') && attributes.href==null)
      node.href='javascript:void(0)';

    if (tagname=='form') {
      if (attributes.action==null) node.action='javascript:void(0)';
      if (attributes.method==null) node.method='get';
    }

    if (parent) {
      if (before)
	parent.insertBefore(node, before);
      else
	parent.appendChild(node);

      if (horus.brokenDOM) node=horus.iefix.checkNode(node, attributes);
    }

    for (var arg=2; arg<argv.length; arg++)
      horus.appendChild(node, argv[arg]);

    if (horus.brokenDOM && node.name!=attributes.name) node.name=attributes.name;
    return node;
  };


horus.createElement=
  function ( element ) {
    var argv;

    if (element instanceof Array) {
      if (horus.brokenDOM && arguments[1]==null) return element;
      argv=element;
    } else
      argv=arguments;

    var defer=horus.brokenDOM ? [] : null;
    var node=horus.$createElement(argv, null, null, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.changeId=
  function () {
    if (arguments.length==2)
      horus.getElement(arguments[0]).id=arguments[1];
    else {
      var base=arguments[0];
      var oldid=arguments[1];
      var newid=arguments[2];

      if (base instanceof Array)
	for (var i=0; i<base.length; i++)
	  document.getElementById(base[i]+oldid).id=base[i]+newid;

      else
	document.getElementById(base+oldid).id=base+newid;

    }
  };


horus.changeName=
  function ( form ) {
    if (arguments.length==3)
      horus.formField(form, arguments[1]).name=arguments[2];
    else {
      var base=arguments[1];
      var oldid=arguments[2];
      var newid=arguments[3];

      if (base instanceof Array)
	for (var i=0; i<base.length; i++)
	  horus.formField(form, base[i]+oldid).name=base[i]+newid;

      else
	horus.formField(form, base+oldid).name=base+newid;

    }
  };


horus.changeMap=
  function ( image, map, oldid, newid ) {
    var re=new RegExp(oldid+'$');

    if (typeof image=='string') image=document.getElementById(image+oldid);
    if (image.id) image.id=image.id.replace(re, newid);
    image.useMap=image.useMap.replace(re, newid);

    if (typeof map=='string') map=document.getElementById(map+oldid);
    if (map.id) map.id=map.id.replace(re, newid);
    map.name=map.name.replace(re, newid);
  };


horus.hidePopup=
  function () {
    horus.status.clear();

    if (horus.showPopup._popup) {
      horus.showPopup._popup.style.display='none';
      horus.showPopup._popup.parentNode.style.zIndex=1;
      horus.showPopup._popup.parentNode.style.position='static';
      horus.showPopup._popup=null;
    }
  };


horus.showPopup=
  function ( popup, button ) {
    horus.hidePopup();
    horus.showPopup._popup=horus.getElement(popup);
    button.parentNode.appendChild(horus.showPopup._popup);
    button.parentNode.style.position='relative'; // blessed be the Microsoft
    button.parentNode.style.zIndex=10;
    horus.showPopup._popup.style.display='block';
  };


horus.classre=
  function ( classname ) {
    if (classname instanceof RegExp) return classname;

    if (!horus.classre.cache[classname])
      horus.classre.cache[classname]=new RegExp('(^| )'+classname+'( |$)');

    return horus.classre.cache[classname];
  };

horus.classre.cache={};


horus.classre.split=
  function ( classname, re ) {
    if (classname!=null) {
      classname=classname.trim();

      if (classname=='')
	classname=null;
      else {
	classname=classname.split(/\s+/, true);

	if (re)
	  for (var c=0; c<classname.length; c++)
	    classname[c]=horus.classre(classname[c]);

      }
    }

    return classname;
  };


horus.hasClass=
  function ( node, classname, any ) {
    node=horus.getElement(node);
    classname=horus.classre.split(classname, true);
    if (!classname) return null;
    if (!node.className) return false;

    while (classname.length)
      if (classname.shift().test(node.className)) {
	if (any) return true;
      } else if (!any)
	return false;

    return !any;
  };


horus.setClass=
  function ( node, classname ) {
    node=horus.getElement(node);
    var old=node.className;
    if (arguments.length>2) classname=arguments[classname ? 2 : 3];
    if (classname==null) classname='';
    node.className=classname;
    return old;
  };


horus.addClass=
  function ( node, classname ) {
    node=horus.getElement(node);

    if (!node.className)
      node.className=classname;
    else {
      classname=horus.classre.split(classname);

      if (classname)
	while (classname.length) {
	  var c=classname.shift();
	  if (!horus.classre(c).test(node.className)) node.className+=' '+c;
	}

    }
  };


horus.$removeClass=
  function ( node, classre ) {
    if (node.className) {
      var point=node.className.search(classre);
      if (point>=0) node.className=node.className.replace(classre, point==0 ? '' : ' ');
    }
  };


horus.removeClass=
  function ( node, classname ) {
    node=horus.getElement(node);

    if (node.className)
      if (classname instanceof RegExp)
	horus.$removeClass(node, classname);
      else {
	classname=horus.classre.split(classname, true);

	if (classname)
	  while (classname.length) horus.$removeClass(node, classname.shift());

      }

  };


horus.replaceClass=
  function ( node, oldclass, newclass ) {
    node=horus.getElement(node);
    horus.removeClass(node, oldclass);
    horus.addClass(node, newclass);
  };


horus.toggleClass=
  function ( node, classname ) {
    node=horus.getElement(node);
    var had=horus.hasClass(node, classname);

    if (had)
      horus.removeClass(node, classname);
    else
      horus.addClass(node, classname);

    return !had;
  };


horus.swapClass=
  function ( node, class1, class2, class3 ) {
    node=horus.getElement(node);

    if (typeof class1=='boolean')
      if (class1) {
	if (class3) horus.removeClass(node, class3);
	if (class2) horus.addClass(node, class2);
      } else {
	if (class2) horus.removeClass(node, class2);
	if (class3) horus.addClass(node, class3);
      }
    else if (horus.hasClass(node, class1))
      horus.replaceClass(node, class1, class2);
    else if (horus.hasClass(node, class2))
      horus.replaceClass(node, class2, class1);
    else if (class3!=null)
      horus.addClass(node, class3);

    return node.className;
  };


horus.scrollTop=
  function ( node, pos ) {
    var scroll=null;

    if (node)
      if (pos!=null) {
	scroll=pos<0 ? 0 : pos;
	node.scrollTop=scroll;
      } else
	scroll=node.scrollTop;

    return scroll;
  };


horus.getSize=
  function ( node, resized ) {
    node=horus.getElement(node);

    if (resized || node.$deltaWidth==null) {
      horus.matchHeight.lock();

      var height=node.offsetHeight;
      var restore=node.style.height;
      node.style.height=height+'px';
      node.$deltaHeight=2*(node.offsetHeight-height);
      node.style.height=restore;

      var width=node.offsetWidth;
      restore=node.style.width;
      node.style.width=width+'px';
      node.$deltaWidth=2*(node.offsetWidth-width);
      node.style.width=restore;

      horus.matchHeight.unlock();
    }

    return { outerHeight: node.offsetHeight,
	     innerHeight: node.offsetHeight-node.$deltaHeight,
	     deltaHeight: node.$deltaHeight,
	     outerWidth: node.offsetWidth, 
	     innerWidth: node.offsetWidth-node.$deltaWidth,
	     deltaWidth: node.$deltaWidth };

  };


horus.setHeight=
  function ( node, height, resized ) {
    node=horus.getElement(node);
    node.style.height=(height-horus.getSize(node, resized).deltaHeight)+'px';
  };


horus.adjustHeight=
  function ( node, adjustment, resized ) {
    node=horus.getElement(node);
    node.style.height=(horus.getSize(node, resized).innerHeight+adjustment)+'px';
  };


horus.setWidth=
  function ( node, width, resized ) {
    node=horus.getElement(node);
    node.style.width=(width-horus.getSize(node, resized).deltaWidth)+'px';
  };


horus.adjustWidth=
  function ( node, adjustment, resized ) {
    node=horus.getElement(node);
    node.style.width=(horus.getSize(node, resized).innerWidth+adjustment)+'px';
  };


horus.opacity=
  function ( node, opacity ) {
    if (opacity<0)
      opacity=0;
    else if (opacity>1)
      opacity=1;

    if (node instanceof Array)
      for (var i=0; i<node.length; i++)
	horus.opacity.set(horus.getElement(node[i]).style, opacity);

    else
      horus.opacity.set(horus.getElement(node).style, opacity);

    return opacity;
  };


horus.opacity.set=
  function ( style, opacity ) {
    style.opacity=opacity;

    if (horus.iewin) {
      if (horus.ie>=8)
        style['-ms-filter']=
          'progid:DXImageTransform.Microsoft.Alpha(Opacity='+(opacity*100)+')';

      style.filter='alpha(opacity='+(opacity*100)+')';
    } else
      if (horus.gecko)
        style['-moz-opacity']=opacity;
      else if (horus.khtml)
        style['-khtml-opacity']=opacity;

  };


horus.showBox=
  function ( box, ref, xoffset, yoffset ) {
    var refpos=horus.getPosition(ref);
    box=horus.getElement(box);
    if (!xoffset) xoffset=20;
    if (!yoffset) yoffset=10;
    box.style.left=(refpos.left+xoffset)+'px';
    box.style.top=(refpos.top+yoffset)+'px';
    var winpos=horus.windowPos();
    horus.visible(box, true);
    var boxpos=horus.getPosition(box);
    xoffset=(boxpos.left+boxpos.width)-winpos.right+(gecko ? 25 : 0);
    if (xoffset>0) box.style.left=(boxpos.left-xoffset)+'px';
    yoffset=(boxpostop+boxpos.height)-winpos.bottom;
    if (yoffset>0) box.style.top=(boxpos.top-yoffset)+'px';
    return false;
  };


horus.script.loadIf(horus.brokenDOM, 'ie-dom-fixes');
horus.script.loaded('dom');
