﻿/*
 *  DOM munging stuff © 2006-2012, Horus Web Engineering Ltd
 *
 *  $Id: dom.js,v 1.134 2012-01-26 15:16:56 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();

	  if (list.isEmpty())
	    attributes[name]='*';
	  else {
	    var values=[];

	    while (list.length) {
	      var op=list.shift();
	      var value=list.shift();
	      var op1=op.left(1);

	      if (op1=='~' || op1=='<') {
		op=op.right(-1);
		if (op1=='<') value='^'+value;
		value=new RegExp(value);
	      } else
		value=horus.toObject(value);

	      value.$negate=op=='!';
	      values.push(value);
	    }

	    attributes[name]=values.length==1 ? values[0] : 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) {
      match=horus.getTags.parse(match);
      match=match instanceof Array ? match[0] : { tag: match };
      prefix=horus.hash.copy(match, prefix[0]);
    }

    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 attr=undefined;

	  if (name in node) {
	    attr=node[name];

	    if (typeof attr=='function')
	      attr=attr.toString().replace(/^.*?\{\s*(.*?)\s*\}$/, '$1');

	  }

	  var found=0;

	  if (values instanceof Array) {
	    for (var i=0; i<values.length; i++) {
	      var val=values[i];

	      if (val instanceof RegExp ?
		  val.test(attr) :
		  val=='*' && attr!=undefined || attr==val) {
		if (val.$negate) { ok=false; break }
		found=2;
	      } else if (found<2)
		if (!val.$negate)
		  found=-1;
		else if (!found)
		  found=1;

	    }

	    if (ok) ok=found>0;
	  } else {
	    found=(values instanceof RegExp ?
		   values.test(attr) :
		   values=='*' && attr!=undefined || attr==values);

	    if (values.$negate) found=!found;
	    if (!found) ok=false;
	  }

	  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=!horus.hasValue(key.prefix);
	else if (!(key.prefix instanceof Array))
	  ok=key.prefix.test(id);
	else if (!key.prefix.isEmpty()) {
	  ok=false;

	  for (var j=0; j<key.prefix.length; j++)
	    if (key.prefix[j].test(id)) {
	      ok=true;
	      break;
	    }

	}
      }
    }

    return ok;
  };


horus.getTags.check=
  function ( match, node ) {
    if (!node) return undefined;
    if (node.nodeType!=1) return false;
    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.up && 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.toNode=
  function () {
    var argc=arguments.length;
    if (argc==2 && (arguments[1]==null || arguments[1]=='')) argc=1;
    return horus.$toNode(arguments, argc);
  };


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 () {
    return horus.removeListNode.prefixed(horus.$toNode(arguments));
  };


horus.removeListNode.prefixed=
  function ( node, prefix ) {
    node=horus.getElement(node);
    var match=horus.getTags.prefix(prefix ? 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' || tag.indexOf('$')>=0)
      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, substitute ) {
    if (substitute==null)
      substitute=deep && deep instanceof horus.hash && !deep.isEmpty();

    switch (node.nodeType) {

    case document.ELEMENT_NODE:
      var newNode=horus.$createElementNS(node.nodeName);

      if (node.attributes && node.attributes.length)
	for (var a=0; a<node.attributes.length; a++) {
	  var attr=node.attributes[a].nodeName;
	  var value=node.getAttribute(attr);
	  if (substitute) value=horus.xmlhttp.substitute(value, substitute);
	  newNode.setAttribute(attr, value);
	}

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

      return newNode;
      break;

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

    }
  };


horus.appendImport=
  function ( parent, node, substitute, children ) {
    var result;

    if (typeof substitute=='boolean') {
      children=substitute;
      substitute=null;
    }

    parent=parent ? horus.getElement(parent) : document.body;

    if (horus.isArray(node)) {
      for (var i=0; i<node.length; i++) {
	var r=horus.appendImport(parent, node[i], substitute, children);
	if (!result) result=r;
      }
    } else if (children) {
      for (node=node.firstChild; node; node=node.nextSibling) {
	var r=horus.appendImport(parent, node, substitute);
	if (!result) result=r;
      }
    } else if (horus.ieold) {
      var placeholder=horus.appendChild(parent, [ 'div' ]);
      result=placeholder.previousSibling;
      var content=node.outerHTML;
      if (substitute) content=horus.xmlhttp.substitute(content, substitute);
      placeholder.outerHTML=content;
      result=result ? result.nextSibling : parent.firstChild;
    } else
      result=horus.appendChild(parent, horus.importNode(node, true, substitute));

    return result;
  };


horus.replaceImport=
  function ( parent, node, substitute, children ) {
    parent=horus.getElement(parent);
    horus.removeContents(parent);
    return horus.appendImport.apply(arguments);
  };


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 (horus.isArray(node)) {
	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 {
	  if (node==null || node=='') continue;
	  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.appendChildren=
  function ( parent, argv, offset ) {
    parent=parent ? horus.getElement(parent) : document.body;
    var defer=horus.brokenDOM ? [] : null;
    var node=horus.$appendChild(parent, argv, offset==null ? 0 : offset, null, defer);
    if (defer) horus.iefix.deferred(defer);
    return node;
  };


horus.appendChild=
  function ( parent ) {
    return horus.appendChildren(parent, arguments, 1);
  };


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


horus.replaceContentWith=
  function ( parent, argv ) {
    parent=horus.getElement(parent);
    if (!parent) return undefined;
    horus.removeContents(parent);
    var node;

    if (horus.isArray(argv)) {
      var defer=horus.brokenDOM ? [] : null;
      node=horus.$appendChild(parent, argv, 0, null, defer);
      if (defer) horus.iefix.deferred(defer);
    } else if (argv!=null)
      node=horus.$appendChild(parent, arguments);

    return node;
  };


horus.replaceHTML=
  function ( parent, html ) {
    if (parent) {
      parent=horus.getElement(parent);
      horus.removeContents(parent);
    } else
      parent=horus.$createElement([ 'div' ]);

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

      if ('$truncated' in parent) delete parent.$truncated;
    }

    return text;
  };


horus.truncatedText=
  function ( parent, deep ) {
    parent=horus.getElement(parent);

    if (!('$truncated' in parent)) {
      var offsetWidth=parent.offsetWidth;
      var offsetHeight=parent.offsetHeight;
      var overflow=parent.style.overflow;
      var width=parent.style.width;
      var height=parent.style.height;
      parent.style.overflow='wrap';
      parent.style.width='auto';
      parent.style.height='auto';

      parent.$truncated=
	parent.offsetWidth>offsetWidth || parent.offsetHeight>offsetHeight;

      parent.style.height=height;
      parent.style.width=width;
      parent.style.overflow=overflow;

      if (horus.truncatedText.$truncated)
	horus.truncatedText.$truncated.push(parent);
      else {
	horus.truncatedText.$truncated=[ parent ];
	horus.onResize(horus.truncatedText.$reset);
      }
    }

    return parent.$truncated ? horus.childText(parent, Boolean(deep)) : '';
  };


horus.truncatedText.$reset=
  function () {
    for (var i=0; i<horus.truncatedText.$truncated.length; i++)
      delete horus.truncatedText.$truncated[i].$truncated;

  };


horus.styleValue=
  function ( tag, value ) {
    if (value!=null) {
      if (horus.typeOf(value)=='object') value=value[tag];

      if (value && /color/i.test(tag) && /^[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value))
	value='#'+value;

    }

    return value;
  };


horus.contrasting=
  function ( colour, black, white ) {
    if (colour.left(1)=='#') colour=colour.right(-1);
    colour=parseInt(colour, 16);
    var total=0;

    while (colour>0) {
      total+=colour&255;
      colour>>>=8;
    }

    if (total>382)
      colour=black==null ? 'black' : black;
    else
      colour=black==null ? 'white' : white;

    return colour;
  };


horus.$createElementNS=
  function ( tagname ) {
    try {
      return document.createElementNS ?
        document.createElementNS("http://www.w3.org/1999/xhtml", tagname) :
	document.createElement(tagname);

    } catch (err) {
      throw "can't create "+tagname+' - '+err;
    }
  };


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

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

    else if (attributes instanceof Array)
      attributes={ classname: 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]=horus.styleValue(name, value);
	break;

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

      case 'backgroundcolor':
	if (value!=null) node.style.backgroundColor=horus.styleValue(attr, value);
	break;

      case 'class':
      case 'classname':
	if (value) {
	  node.className=value instanceof Array ? value.clean().join(' ') : value;
	  hasclass=true;
	}

	break;

      case 'coords':
	if (value instanceof Array) value=value.join(',');
	node.coords=value;
	break;

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

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

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

      }
    }

    if (tagname=='input') {
      if (attributes.type==null) node.type='text';

      if (attributes.value==null)
	if (node.type=='checkbox')
	  node.value=true;
	else if (node.type=='text' || node.type=='hidden')
	  node.value='';

      if (!hasclass) node.className=node.type;
    }

    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=='area' && attributes.shape==null) {
      value=attributes.coords;
      if (!(value instanceof Array)) value=value.split(/[ ,]+/);
      node.shape=value.length==3 ? 'circle' : value.length==4 ? 'rect' : 'poly';
    }

    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);
      if (horus.$d1) parent.appendChild(document.createTextNode('\n'));
    }

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


Function.prototype.asAction=
  function () {
    var self=this;
    var argv=arguments;
    return function () { return self.apply(argv) };
  };


Function.prototype.asEvent=
  function () {
    var self=this;
    var tail=arguments;

    return function () {
      var argv=[ arguments ];
      argv.push.apply(argv, tail);
      return self.apply(argv);
    };
  };


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.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) {
      if (classname instanceof Array) {
	classname=classname.clean();
	if (classname.isEmpty()) classname=null;
      } else {
	classname=classname.trim();

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

      }

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

    }

    return classname;
  };


horus.compareClass=
  function ( a, b ) {
    if (horus.isNode(a)) a=a.className;
    if (horus.isNode(b)) b=b.className;
    a=a.split(/\s+/, true).sort(horus.compare);
    b=b.split(/\s+/, true).sort(horus.compare);
    var aa=true;
    var bb=true;

    while (a.length && b.length && (aa || bb))
      if (a[0]==b[0]) {
	a.shift();
	b.shift();
      } else if (a[0]<b[0]) {
	a.shift();
	aa=false;
      } else {
	b.shift();
	bb=false;
      }

    if (aa && a.length) aa=false;
    if (bb && b.length) bb=false;
    return aa && bb ? 0 : aa ? -1 : bb ? 1 : null;
  };


horus.hasClass=
  function ( node, classname, any ) {
    node=horus.getElement(node, true);
    if (!node) return undefined;
    if (classname.left(1)=='.') classname=classname.right(-1);
    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, true);
    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, true);

    if (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, true);

    if (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, true);
    horus.removeClass(node, oldclass);
    horus.addClass(node, newclass);
  };


horus.toggleClass=
  function ( node, classname ) {
    node=horus.getElement(node, true);
    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, true);
    var oldClass=node.className;

    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 horus.compareClass(node.className, oldClass)!=0;
  };


horus.exchangeClass=
  function ( node1, node2, keep ) {
    node1=horus.getElement(node1, true);
    node2=horus.getElement(node2, true);
    var c=node1.className; node1.className=node2.className; node2.className=c;

    if (keep) {
      keep=horus.classre.split(keep);

      while (keep.length) {
	c=keep.shift();
	var c1=horus.hasClass(node1, c);
	var c2=horus.hasClass(node2, c);

	if (c1!=c2)
	  if (c1) {
	    horus.removeClass(node1, c);
	    horus.addClass(node2, c);
	  } else {
	    horus.removeClass(node2, c);
	    horus.addClass(node1, c);
	  }

      }
    }
  };


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;

  };


if (horus.firefox>2) {
  horus.fixTables=
    function () {
      var tables=horus.getTags('table.fixed');

      for (var i=0; i<tables.length; i++) {
	var table=tables[i];
	var width=table.offsetWidth;
	var cols=horus.getTags.find(table, 'col:width~^\\d+$,colgroup:width~^\\d+$');

	for (var j=0; j<cols.length; j++) {
	  var col=cols[j];
	  col.setAttribute('width', Math.round(Number(col.width)/width*100)+'%');
	}
      }
    };

  horus.onLoad(horus.fixTables);
}

horus.script.loaded('dom');

