/* ----------------------------------------------------------------------------

                                  __       ___          ___      
                     __          /\ \__  / ___\        /\_ \     
     _____     ___  /\_\     ___ \ \  _\/\ \__/  __  __\//\ \    
    /\  __ \  / __ \\/\ \   / _  \\ \ \/\ \  __\/\ \/\ \ \ \ \   
    \ \ \_\ \/\ \_\ \\ \ \ /\ \/\ \\ \ \_\ \ \_/\ \ \_\ \ \_\ \_ 
     \ \  __/\ \____/ \ \_\\ \_\ \_\\ \__\\ \_\  \ \____/ /\____\
      \ \ \/  \/___/   \/_/ \/_/\/_/ \/__/ \/_/   \/___/  \/____/
       \ \_\ 
        \/_/                                    S O L U T I O N S


    PAX: Pointful AjaX and event library

	pax.js Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details
	
	Notices thanks and credits
	--------------------------
	
	.	PAX uses John Resig's Sizzle CSS Selector engine with permission:
	
			http://sizzlejs.com
	
	.	The date formatting library in this script are based on the awesome script 
		by Baron Schwartz, originally published here:
	
			http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
	
	.	The XML parser in the data library is based on this script:
	
			http://www.openjs.com/scripts/xml_parser/xml2array.js
			
	.	Thanks goes to Jeremy Keith for his presentation and workshop at WD06 South, it
		was very inspirational
		
	
		
---------------------------------------------------------------------------- */
/*!
* Sizzle CSS Selector Engine - v1.0
* Copyright 2009, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
 
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
  done = 0,
  toString = Object.prototype.toString,
  arraySplice = Array.prototype.splice,
  arrayPush = Array.prototype.push,
  arraySort = Array.prototype.sort;
 
var Sizzle = function(selector, context, results, seed) {
  results = results || [];
  var origContext = context = context || document;
 
  if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
    return [];
  }
  
  if ( !selector || typeof selector !== "string" ) {
    return results;
  }
 
  var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
  
  // Reset the position of the chunker regexp (start from head)
  chunker.lastIndex = 0;
  
  while ( (m = chunker.exec(selector)) !== null ) {
    parts.push( m[1] );
    
    if ( m[2] ) {
      extra = RegExp.rightContext;
      break;
    }
  }
 
  if ( parts.length > 1 && origPOS.exec( selector ) ) {
    if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
      set = posProcess( parts[0] + parts[1], context );
    } else {
      set = Expr.relative[ parts[0] ] ?
        [ context ] :
        Sizzle( parts.shift(), context );
 
      while ( parts.length ) {
        selector = parts.shift();
 
        if ( Expr.relative[ selector ] )
          selector += parts.shift();
 
        set = posProcess( selector, set );
      }
    }
  } else {
    // Take a shortcut and set the context if the root selector is an ID
    // (but not if it'll be faster if the inner selector is an ID)
    if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
        Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
      var ret = Sizzle.find( parts.shift(), context, contextXML );
      context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
    }
 
    if ( context ) {
      var ret = seed ?
        { expr: parts.pop(), set: makeArray(seed) } :
        Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
      set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
 
      if ( parts.length > 0 ) {
        checkSet = makeArray(set);
      } else {
        prune = false;
      }
 
      while ( parts.length ) {
        var cur = parts.pop(), pop = cur;
 
        if ( !Expr.relative[ cur ] ) {
          cur = "";
        } else {
          pop = parts.pop();
        }
 
        if ( pop == null ) {
          pop = context;
        }
 
        Expr.relative[ cur ]( checkSet, pop, contextXML );
      }
    } else {
      checkSet = parts = [];
    }
  }
 
  if ( !checkSet ) {
    checkSet = set;
  }
 
  if ( !checkSet ) {
    throw "Syntax error, unrecognized expression: " + (cur || selector);
  }
 
  if ( toString.call(checkSet) === "[object Array]" ) {
    if ( !prune ) {
      arrayPush.apply( results, checkSet );
    } else if ( context && context.nodeType === 1 ) {
      for ( var i = 0; checkSet[i] != null; i++ ) {
        if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
          arrayPush.call( results, set[i] );
        }
      }
    } else {
      for ( var i = 0; checkSet[i] != null; i++ ) {
        if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
          arrayPush.call( results, set[i] );
        }
      }
    }
  } else {
    makeArray( checkSet, results );
  }
 
  if ( extra ) {
    Sizzle( extra, origContext, results, seed );
    Sizzle.uniqueSort( results );
  }
 
  return results;
};
 
Sizzle.uniqueSort = function(results){
  if ( sortOrder ) {
    hasDuplicate = false;
    arraySort.call(results, sortOrder);
 
    if ( hasDuplicate ) {
      for ( var i = 1; i < results.length; i++ ) {
        if ( results[i] === results[i-1] ) {
          arraySplice.call(results, i--, 1);
        }
      }
    }
  }
};
 
Sizzle.matches = function(expr, set){
  return Sizzle(expr, null, null, set);
};
 
Sizzle.find = function(expr, context, isXML){
  var set, match;
 
  if ( !expr ) {
    return [];
  }
 
  for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
    var type = Expr.order[i], match;
    
    if ( (match = Expr.match[ type ].exec( expr )) ) {
      var left = RegExp.leftContext;
 
      if ( left.substr( left.length - 1 ) !== "\\" ) {
        match[1] = (match[1] || "").replace(/\\/g, "");
        set = Expr.find[ type ]( match, context, isXML );
        if ( set != null ) {
          expr = expr.replace( Expr.match[ type ], "" );
          break;
        }
      }
    }
  }
 
  if ( !set ) {
    set = context.getElementsByTagName("*");
  }
 
  return {set: set, expr: expr};
};
 
Sizzle.filter = function(expr, set, inplace, not){
  var old = expr, result = [], curLoop = set, match, anyFound,
    isXMLFilter = set && set[0] && isXML(set[0]);
 
  while ( expr && set.length ) {
    for ( var type in Expr.filter ) {
      if ( (match = Expr.match[ type ].exec( expr )) != null ) {
        var filter = Expr.filter[ type ], found, item;
        anyFound = false;
 
        if ( curLoop == result ) {
          result = [];
        }
 
        if ( Expr.preFilter[ type ] ) {
          match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
 
          if ( !match ) {
            anyFound = found = true;
          } else if ( match === true ) {
            continue;
          }
        }
 
        if ( match ) {
          for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
            if ( item ) {
              found = filter( item, match, i, curLoop );
              var pass = not ^ !!found;
 
              if ( inplace && found != null ) {
                if ( pass ) {
                  anyFound = true;
                } else {
                  curLoop[i] = false;
                }
              } else if ( pass ) {
                result.push( item );
                anyFound = true;
              }
            }
          }
        }
 
        if ( found !== undefined ) {
          if ( !inplace ) {
            curLoop = result;
          }
 
          expr = expr.replace( Expr.match[ type ], "" );
 
          if ( !anyFound ) {
            return [];
          }
 
          break;
        }
      }
    }
 
    // Improper expression
    if ( expr == old ) {
      if ( anyFound == null ) {
        throw "Syntax error, unrecognized expression: " + expr;
      } else {
        break;
      }
    }
 
    old = expr;
  }
 
  return curLoop;
};
 
var Expr = Sizzle.selectors = {
  order: [ "ID", "NAME", "TAG" ],
  match: {
    ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
    CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
    NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
    ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
    TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
    CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
    POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
    PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
  },
  attrMap: {
    "class": "className",
    "for": "htmlFor"
  },
  attrHandle: {
    href: function(elem){
      return elem.getAttribute("href");
    }
  },
  relative: {
    "+": function(checkSet, part, isXML){
      var isPartStr = typeof part === "string",
        isTag = isPartStr && !/\W/.test(part),
        isPartStrNotTag = isPartStr && !isTag;
 
      if ( isTag && !isXML ) {
        part = part.toUpperCase();
      }
 
      for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
        if ( (elem = checkSet[i]) ) {
          while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
 
          checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
            elem || false :
            elem === part;
        }
      }
 
      if ( isPartStrNotTag ) {
        Sizzle.filter( part, checkSet, true );
      }
    },
    ">": function(checkSet, part, isXML){
      var isPartStr = typeof part === "string";
 
      if ( isPartStr && !/\W/.test(part) ) {
        part = isXML ? part : part.toUpperCase();
 
        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
          var elem = checkSet[i];
          if ( elem ) {
            var parent = elem.parentNode;
            checkSet[i] = parent.nodeName === part ? parent : false;
          }
        }
      } else {
        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
          var elem = checkSet[i];
          if ( elem ) {
            checkSet[i] = isPartStr ?
              elem.parentNode :
              elem.parentNode === part;
          }
        }
 
        if ( isPartStr ) {
          Sizzle.filter( part, checkSet, true );
        }
      }
    },
    "": function(checkSet, part, isXML){
      var doneName = done++, checkFn = dirCheck;
 
      if ( !part.match(/\W/) ) {
        var nodeCheck = part = isXML ? part : part.toUpperCase();
        checkFn = dirNodeCheck;
      }
 
      checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
    },
    "~": function(checkSet, part, isXML){
      var doneName = done++, checkFn = dirCheck;
 
      if ( typeof part === "string" && !part.match(/\W/) ) {
        var nodeCheck = part = isXML ? part : part.toUpperCase();
        checkFn = dirNodeCheck;
      }
 
      checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
    }
  },
  find: {
    ID: function(match, context, isXML){
      if ( typeof context.getElementById !== "undefined" && !isXML ) {
        var m = context.getElementById(match[1]);
        return m ? [m] : [];
      }
    },
    NAME: function(match, context, isXML){
      if ( typeof context.getElementsByName !== "undefined" ) {
        var ret = [], results = context.getElementsByName(match[1]);
 
        for ( var i = 0, l = results.length; i < l; i++ ) {
          if ( results[i].getAttribute("name") === match[1] ) {
            ret.push( results[i] );
          }
        }
 
        return ret.length === 0 ? null : ret;
      }
    },
    TAG: function(match, context){
      return context.getElementsByTagName(match[1]);
    }
  },
  preFilter: {
    CLASS: function(match, curLoop, inplace, result, not, isXML){
      match = " " + match[1].replace(/\\/g, "") + " ";
 
      if ( isXML ) {
        return match;
      }
 
      for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
        if ( elem ) {
          if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
            if ( !inplace )
              result.push( elem );
          } else if ( inplace ) {
            curLoop[i] = false;
          }
        }
      }
 
      return false;
    },
    ID: function(match){
      return match[1].replace(/\\/g, "");
    },
    TAG: function(match, curLoop){
      for ( var i = 0; curLoop[i] === false; i++ ){}
      return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
    },
    CHILD: function(match){
      if ( match[1] == "nth" ) {
        // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
        var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
          match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
          !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
 
        // calculate the numbers (first)n+(last) including if they are negative
        match[2] = (test[1] + (test[2] || 1)) - 0;
        match[3] = test[3] - 0;
      }
 
      // TODO: Move to normal caching system
      match[0] = done++;
 
      return match;
    },
    ATTR: function(match, curLoop, inplace, result, not, isXML){
      var name = match[1].replace(/\\/g, "");
      
      if ( !isXML && Expr.attrMap[name] ) {
        match[1] = Expr.attrMap[name];
      }
 
      if ( match[2] === "~=" ) {
        match[4] = " " + match[4] + " ";
      }
 
      return match;
    },
    PSEUDO: function(match, curLoop, inplace, result, not){
      if ( match[1] === "not" ) {
        // If we're dealing with a complex expression, or a simple one
        if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
          match[3] = Sizzle(match[3], null, null, curLoop);
        } else {
          var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
          if ( !inplace ) {
            result.push.apply( result, ret );
          }
          return false;
        }
      } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
        return true;
      }
      
      return match;
    },
    POS: function(match){
      match.unshift( true );
      return match;
    }
  },
  filters: {
    enabled: function(elem){
      return elem.disabled === false && elem.type !== "hidden";
    },
    disabled: function(elem){
      return elem.disabled === true;
    },
    checked: function(elem){
      return elem.checked === true;
    },
    selected: function(elem){
      // Accessing this property makes selected-by-default
      // options in Safari work properly
      elem.parentNode.selectedIndex;
      return elem.selected === true;
    },
    parent: function(elem){
      return !!elem.firstChild;
    },
    empty: function(elem){
      return !elem.firstChild;
    },
    has: function(elem, i, match){
      return !!Sizzle( match[3], elem ).length;
    },
    header: function(elem){
      return /h\d/i.test( elem.nodeName );
    },
    text: function(elem){
      return "text" === elem.type;
    },
    radio: function(elem){
      return "radio" === elem.type;
    },
    checkbox: function(elem){
      return "checkbox" === elem.type;
    },
    file: function(elem){
      return "file" === elem.type;
    },
    password: function(elem){
      return "password" === elem.type;
    },
    submit: function(elem){
      return "submit" === elem.type;
    },
    image: function(elem){
      return "image" === elem.type;
    },
    reset: function(elem){
      return "reset" === elem.type;
    },
    button: function(elem){
      return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
    },
    input: function(elem){
      return /input|select|textarea|button/i.test(elem.nodeName);
    }
  },
  setFilters: {
    first: function(elem, i){
      return i === 0;
    },
    last: function(elem, i, match, array){
      return i === array.length - 1;
    },
    even: function(elem, i){
      return i % 2 === 0;
    },
    odd: function(elem, i){
      return i % 2 === 1;
    },
    lt: function(elem, i, match){
      return i < match[3] - 0;
    },
    gt: function(elem, i, match){
      return i > match[3] - 0;
    },
    nth: function(elem, i, match){
      return match[3] - 0 == i;
    },
    eq: function(elem, i, match){
      return match[3] - 0 == i;
    }
  },
  filter: {
    PSEUDO: function(elem, match, i, array){
      var name = match[1], filter = Expr.filters[ name ];
 
      if ( filter ) {
        return filter( elem, i, match, array );
      } else if ( name === "contains" ) {
        return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
      } else if ( name === "not" ) {
        var not = match[3];
 
        for ( var i = 0, l = not.length; i < l; i++ ) {
          if ( not[i] === elem ) {
            return false;
          }
        }
 
        return true;
      }
    },
    CHILD: function(elem, match){
      var type = match[1], node = elem;
      switch (type) {
        case 'only':
        case 'first':
          while (node = node.previousSibling) {
            if ( node.nodeType === 1 ) return false;
          }
          if ( type == 'first') return true;
          node = elem;
        case 'last':
          while (node = node.nextSibling) {
            if ( node.nodeType === 1 ) return false;
          }
          return true;
        case 'nth':
          var first = match[2], last = match[3];
 
          if ( first == 1 && last == 0 ) {
            return true;
          }
          
          var doneName = match[0],
            parent = elem.parentNode;
  
          if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
            var count = 0;
            for ( node = parent.firstChild; node; node = node.nextSibling ) {
              if ( node.nodeType === 1 ) {
                node.nodeIndex = ++count;
              }
            }
            parent.sizcache = doneName;
          }
          
          var diff = elem.nodeIndex - last;
          if ( first == 0 ) {
            return diff == 0;
          } else {
            return ( diff % first == 0 && diff / first >= 0 );
          }
      }
    },
    ID: function(elem, match){
      return elem.nodeType === 1 && elem.getAttribute("id") === match;
    },
    TAG: function(elem, match){
      return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
    },
    CLASS: function(elem, match){
      return (" " + (elem.className || elem.getAttribute("class")) + " ")
        .indexOf( match ) > -1;
    },
    ATTR: function(elem, match){
      var name = match[1],
        result = Expr.attrHandle[ name ] ?
          Expr.attrHandle[ name ]( elem ) :
          elem[ name ] != null ?
            elem[ name ] :
            elem.getAttribute( name ),
        value = result + "",
        type = match[2],
        check = match[4];
 
      return result == null ?
        type === "!=" :
        type === "=" ?
        value === check :
        type === "*=" ?
        value.indexOf(check) >= 0 :
        type === "~=" ?
        (" " + value + " ").indexOf(check) >= 0 :
        !check ?
        value && result !== false :
        type === "!=" ?
        value != check :
        type === "^=" ?
        value.indexOf(check) === 0 :
        type === "$=" ?
        value.substr(value.length - check.length) === check :
        type === "|=" ?
        value === check || value.substr(0, check.length + 1) === check + "-" :
        false;
    },
    POS: function(elem, match, i, array){
      var name = match[2], filter = Expr.setFilters[ name ];
 
      if ( filter ) {
        return filter( elem, i, match, array );
      }
    }
  }
};
 
var origPOS = Expr.match.POS;
 
for ( var type in Expr.match ) {
  Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}
 
var makeArray = function(array, results) {
  array = Array.prototype.slice.call( array );
 
  if ( results ) {
    arrayPush.apply( results, array );
    return results;
  }
  
  return array;
};
 
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
try {
  Array.prototype.slice.call( document.documentElement.childNodes );
 
// Provide a fallback method if it does not work
} catch(e){
  makeArray = function(array, results) {
    var ret = results || [];
 
    if ( toString.call(array) === "[object Array]" ) {
      Array.prototype.push.apply( ret, array );
    } else {
      if ( typeof array.length === "number" ) {
        for ( var i = 0, l = array.length; i < l; i++ ) {
          ret.push( array[i] );
        }
      } else {
        for ( var i = 0; array[i]; i++ ) {
          ret.push( array[i] );
        }
      }
    }
 
    return ret;
  };
}
 
var sortOrder;
 
if ( document.documentElement.compareDocumentPosition ) {
  sortOrder = function( a, b ) {
    var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
    if ( ret === 0 ) {
      hasDuplicate = true;
    }
    return ret;
  };
} else if ( "sourceIndex" in document.documentElement ) {
  sortOrder = function( a, b ) {
    var ret = a.sourceIndex - b.sourceIndex;
    if ( ret === 0 ) {
      hasDuplicate = true;
    }
    return ret;
  };
} else if ( document.createRange ) {
  sortOrder = function( a, b ) {
    var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
    aRange.selectNode(a);
    aRange.collapse(true);
    bRange.selectNode(b);
    bRange.collapse(true);
    var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
    if ( ret === 0 ) {
      hasDuplicate = true;
    }
    return ret;
  };
}
 
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
  // We're going to inject a fake input element with a specified name
  var form = document.createElement("div"),
    id = "script" + (new Date).getTime();
  form.innerHTML = "<a name='" + id + "'/>";
 
  // Inject it into the root element, check its status, and remove it quickly
  var root = document.documentElement;
  root.insertBefore( form, root.firstChild );
 
  // The workaround has to do additional checks after a getElementById
  // Which slows things down for other browsers (hence the branching)
  if ( !!document.getElementById( id ) ) {
    Expr.find.ID = function(match, context, isXML){
      if ( typeof context.getElementById !== "undefined" && !isXML ) {
        var m = context.getElementById(match[1]);
        return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
      }
    };
 
    Expr.filter.ID = function(elem, match){
      var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
      return elem.nodeType === 1 && node && node.nodeValue === match;
    };
  }
 
  root.removeChild( form );
})();
 
(function(){
  // Check to see if the browser returns only elements
  // when doing getElementsByTagName("*")
 
  // Create a fake element
  var div = document.createElement("div");
  div.appendChild( document.createComment("") );
 
  // Make sure no comments are found
  if ( div.getElementsByTagName("*").length > 0 ) {
    Expr.find.TAG = function(match, context){
      var results = context.getElementsByTagName(match[1]);
 
      // Filter out possible comments
      if ( match[1] === "*" ) {
        var tmp = [];
 
        for ( var i = 0; results[i]; i++ ) {
          if ( results[i].nodeType === 1 ) {
            tmp.push( results[i] );
          }
        }
 
        results = tmp;
      }
 
      return results;
    };
  }
 
  // Check to see if an attribute returns normalized href attributes
  div.innerHTML = "<a href='#'></a>";
  if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
      div.firstChild.getAttribute("href") !== "#" ) {
    Expr.attrHandle.href = function(elem){
      return elem.getAttribute("href", 2);
    };
  }
})();
 
if ( document.querySelectorAll ) (function(){
  var oldSizzle = Sizzle, div = document.createElement("div");
  div.innerHTML = "<p class='TEST'></p>";
 
  // Safari can't handle uppercase or unicode characters when
  // in quirks mode.
  if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
    return;
  }
  
  Sizzle = function(query, context, extra, seed){
    context = context || document;
 
    // Only use querySelectorAll on non-XML documents
    // (ID selectors don't work in non-HTML documents)
    if ( !seed && context.nodeType === 9 && !isXML(context) ) {
      try {
        return makeArray( context.querySelectorAll(query), extra );
      } catch(e){}
    }
    
    return oldSizzle(query, context, extra, seed);
  };
 
  for ( var prop in oldSizzle ) {
    Sizzle[ prop ] = oldSizzle[ prop ];
  }
})();
 
if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
  var div = document.createElement("div");
  div.innerHTML = "<div class='test e'></div><div class='test'></div>";
 
  // Opera can't find a second classname (in 9.6)
  if ( div.getElementsByClassName("e").length === 0 )
    return;
 
  // Safari caches class attributes, doesn't catch changes (in 3.2)
  div.lastChild.className = "e";
 
  if ( div.getElementsByClassName("e").length === 1 )
    return;
 
  Expr.order.splice(1, 0, "CLASS");
  Expr.find.CLASS = function(match, context, isXML) {
    if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
      return context.getElementsByClassName(match[1]);
    }
  };
})();
 
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  var sibDir = dir == "previousSibling" && !isXML;
  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    var elem = checkSet[i];
    if ( elem ) {
      if ( sibDir && elem.nodeType === 1 ){
        elem.sizcache = doneName;
        elem.sizset = i;
      }
      elem = elem[dir];
      var match = false;
 
      while ( elem ) {
        if ( elem.sizcache === doneName ) {
          match = checkSet[elem.sizset];
          break;
        }
 
        if ( elem.nodeType === 1 && !isXML ){
          elem.sizcache = doneName;
          elem.sizset = i;
        }
 
        if ( elem.nodeName === cur ) {
          match = elem;
          break;
        }
 
        elem = elem[dir];
      }
 
      checkSet[i] = match;
    }
  }
}
 
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  var sibDir = dir == "previousSibling" && !isXML;
  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    var elem = checkSet[i];
    if ( elem ) {
      if ( sibDir && elem.nodeType === 1 ) {
        elem.sizcache = doneName;
        elem.sizset = i;
      }
      elem = elem[dir];
      var match = false;
 
      while ( elem ) {
        if ( elem.sizcache === doneName ) {
          match = checkSet[elem.sizset];
          break;
        }
 
        if ( elem.nodeType === 1 ) {
          if ( !isXML ) {
            elem.sizcache = doneName;
            elem.sizset = i;
          }
          if ( typeof cur !== "string" ) {
            if ( elem === cur ) {
              match = true;
              break;
            }
 
          } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
            match = elem;
            break;
          }
        }
 
        elem = elem[dir];
      }
 
      checkSet[i] = match;
    }
  }
}
 
var contains = document.compareDocumentPosition ? function(a, b){
  return a.compareDocumentPosition(b) & 16;
} : function(a, b){
  return a !== b && (a.contains ? a.contains(b) : true);
};
 
var isXML = function(elem){
  return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
    !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
};
 
var posProcess = function(selector, context){
  var tmpSet = [], later = "", match,
    root = context.nodeType ? [context] : context;
 
  // Position selectors must be done after the filter
  // And so must :not(positional) so we move all PSEUDOs to the end
  while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
    later += match[0];
    selector = selector.replace( Expr.match.PSEUDO, "" );
  }
 
  selector = Expr.relative[selector] ? selector + "*" : selector;
 
  for ( var i = 0, l = root.length; i < l; i++ ) {
    Sizzle( selector, root[i], tmpSet );
  }
 
  return Sizzle.filter( later, tmpSet );
};
 
// EXPOSE
 
window.Sizzle = Sizzle;
 
})();
/*
	Script: pax
		This is the main PAX library, encapsulating xmlHttpRequest, event handeling, and so on.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


/*	Property: pax
	Main library OBJ	*/
var pax = pax || {};


/*	Method: pax.$
	Simple alias to document.getElementById
	
	Parameters:
		id - The id of the DOM element

	Example:
		(start code)
			<div id='myElement'>This is my Elements innerHTML</div>
			[:.
				var ele = pax.$('myElement');
				alert( ele.innerHTML );
			:]
		(end)
		Assigns a pointer to *ele*, and the alerts the contents.
*/
pax.$ = function( id ) {
	return (typeof( id ) == 'string' )? document.getElementById( id ): id;
};

var $ = $ || pax.$;	//	Assign $ to our function, unless it already exists - play nicely with other libraries



/*	--- Start Query stuff ---	*/

/*	Behave like jQuery.

	NOTE: 	The point of this is NOT to build a jQuery clone, rather it is to provide 
			the most often used jQuery functionality. We override the sizzle functionality 
			to return a chainable object from pax.query.
	
	TODO: Find out what the most often used jQuery functions are. Assume for now:
	
	[done]	. Chainable events
	[done]	. find(...)
	. DOM manipulation, eg: css
	. Effects
	
	We don't want / need full jQuery - people will include and use jQuery for that :o)
*/

var jQuery = jQuery || null;

if( jQuery && jQuery.prototype ) {
	//	If we detect jQuery, pax.query should = jQuery.
	pax.query = function(){};
	pax.query = pax.query.prototype = jQuery;
} else {
	//	Create a mini set of jQuery-like functionality, apologies to J.Resig for
	//	creating bastardised functions that work similarly to his library :oP
	
	//	Setup Sizzle selector engine
	pax.sizzle = Sizzle;

	pax.query = function( queryStr, scope ) {
		return new pax.query.prototype.init( queryStr );
	};

	//	Create the query function for chaining
	pax.query.prototype = {
		init: function( queryStr, scope ) {
			this.results = pax.sizzle( queryStr, scope );
			return this;
		},
		length: 0,
		size: function() { return this.length; },
		get: function( index ) {
			return (index && index != 0)? this.result: this.result[index];
		},
		find: function( queryStr ) {
			var found = [];
			for( var i = 0; i < this.results.length; i++ ) {
				var matches = pax.sizzle.find( queryStr, this.results[i] )['set'] || [];
				for( var m = 0; m < matches.length; m++ ) {
					found.push( matches[m] );
				}
			}
			this.results = found;
			return this;
		},
		val: function( value ) {
			//	TODO: Use pax.form.fieldValue
			return pax.form.getFieldValue( results );
		},
		attr: function() {
			//	Use pax.util.attrib, which needs some refactoring...
		},
		createBindFunc: function( eventName ) {
			return function( func ) {
				for( var i = 0; i < this.results.length; i++ ) {
					pax.event.bind( this.results[i], eventName, func );
				}
				return this;
			};
		},
		show: function( speed, callback ) {
			//	TODO: use the pax.fx fadein or perhaps reveal function.
			return this;
		}
	};

	//	Prototypify (expose) the init function
	pax.query.prototype.init.prototype = pax.query.prototype;

	//	Create event handlers for chainablilty
	var paxQueryEvents = [
		'blur', 'focus', 'load', 'resize', 'scroll', 'unload', 'click', 'dblclick', 
		'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'change', 
		'select', 'submit', 'keydown', 'keypress', 'keyup', 'error' 
	];

	for( var i = 0; i < paxQueryEvents.length; i++ ) {
		var eventName = paxQueryEvents[i];
		var myFunc = pax.query.prototype.createBindFunc( eventName );
		pax.query[eventName] = pax.query.prototype[eventName] = myFunc;
	};

}

/*	--- End Query stuff ---	*/



/*	Property: pax.docRoot
	String to prefix all server calls with, useful for ensuring security is not broken easily */
pax.docRoot = '';
/*	Property: pax.requestQueue
	Array of requests.	*/
pax.requestQueue = [];
/*	Property: pax.showStatusSpinner
	Boolean to choose if we should display the status spinner	*/
pax.showStatusSpinner = true;
/*	Property: pax.statusSpinnerContainer
	The default container (DOM Element) to put the statusbox spinner into.	*/
pax.statusSpinnerContainer = 'StatusBox';
/*	Property: pax.bindQueue
	List of functions bound to objects. {obj:object, event:event, func:function}	*/
pax.bindQueue = [];
/*	Property: pax.statusSpinnerClass
	The default CSS class to use for the spinner	*/
pax.statusSpinnerClass = 'narrowDark';
/*	Property: pax.idCounter
	Unique ID counter for generating temporary DOM IDs	*/
pax.idCounter = 0;

/*	Property: pax.showDebug
	Outputs debug to console in certain instances, if true	*/
pax.showDebug = false;

pax.event = pax.event || {};							// Events - Browser events library


/*	Simple workaround for browsers without firebug
	See http://www.getfirebug.com/
*/
if( !( 'console' in window ) || !( 'firebug' in console ) ) {
	var names = [
		'log', 'debug', 'info', 'warn', 'error', 'assert', 'dir', 'dirxml',
		'group', 'groupEnd', 'time', 'timeEnd', 'count', 'trace', 'profile', 'profileEnd'
	];

	window.console = {};
	for( var i = 0; i < names.length; ++i ) {
		window.console[names[i]] = function(){};
	}
		
	window.console.log = function( message ) {
		var debugBox = pax.$( 'console_output' );
		if( debugBox ) {
			debugBox.value = message + '\n' + debugBox.value;
		}
		window.status = message;
	};
}


//	Error function; all functions and widgets should use this to report CRITICAL errors.
//	Obviously we don't always want to alert the error, so this *could* be overridden before using PAX in production
pax.criticalError = function( err ) {
	alert( err );
};


/*	Method: pax.post
	Send a POST request via the XMLHttpRequest object. Note that you do not need to specify all parameters

	Parameters:
		url - URL to send a request to.
		post - POST string to include in the call, use <pax.postString> to encode an object containing key / value pairs
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into
		spinnerClass - Class to use on the request spinner.

	Note that a callback function needs 3 parameters:
		xml	- If the response is in XML format, this will contain the response, otherwise it will be empty
		txt - If the response is in Text format, this will contain the response, otherwise it will be empty
		url - what URL the response came from

	Example:
		(start code)
			<div id='pax.post.example1'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.post.example1').innerHTML = "The name retreived from the server is: " + response.data[0].name;
				};
				pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 2 } ), mycallBack, 'Post example', 'pax.post.example1' );
			:]
		(end)

		This will send a request, with id = 2, action = 'editAction', showing the default spinner image via the spinner queue.
		It should be noted that the php script has a 2 second delay, so that you actually see the spinner.
		
	Example:
		(start code)
			<div id='pax.post.example2'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.post.example2').innerHTML = "The country retreived from the server is: " + response.data[0].country;
				};
				pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 1 } ), mycallBack, 'Post example', 'pax.post.example2', 'load_snake_blue.gif' );
			:]
		(end)

		This will send a request, with id = 1, action = 'editAction', showing a custom spinner image via the spinner queue.
		It should be noted that the php script has a 2 second delay, so that you actually see the spinner.
*/
pax.post = function( url, post, callBack, callDesc, spinnerBox, spinnerClass ) {
	post = ( typeof( post ) == 'string' )? post : pax.postString( post );
	var rqObj = pax.setCallBack( url, callBack, callDesc, spinnerBox, spinnerClass );
	rqObj.ro.open( 'POST', pax.docRoot + url, true );
	rqObj.ro.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	rqObj.ro.send( post );
	
	pax.showStatus( spinnerClass );
	
	return rqObj;
};


/*	Method: pax.get
	Send a GET request via the XMLHttpRequest object. Note that you do not need to specify all parameters

	Parameters:
		url - URL to send a request to.
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into
		spinnerClass - Class to use on request spinner box.

	Note that a callback function needs 3 parameters:
		xml	- If the response is in XML format, this will contain the response, otherwise it will be empty
		txt - If the response is in Text format, this will contain the response, otherwise it will be empty
		url - what URL the response came from

	Example:
		(start code)
			<fieldset><legend>Response from pax.get</legend>
				<div id='pax.get.example1'></div>
			</fieldset>
			[:.
				var url = '/pax/pax.get.example1.txt';
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.get.example1').innerHTML = txt;
				};
				
				pax.get( url, callBack, 'get example', false, false );
			:]
		(end)
		
	Note:
		You can add a timestamp at the end of the URL, to make it unique, to avoid caching issues in certain browsers

*/
pax.get = function( url, callBack, callDesc, spinnerBox, spinnerClass ) {
	var rqObj = pax.setCallBack( url, callBack, callDesc, spinnerBox, spinnerClass );
	rqObj.ro.open( 'GET', pax.docRoot + url, true );
	rqObj.ro.send( null );
	
	pax.showStatus( spinnerClass );
	
	return rqObj;
};


/*	Method: pax.getId
	Returns the current unique ID
	
	Parameters:
		none
		
	Example:
		(start code)
		(end)
*/
pax.getId = function() {
	return '_pax_' + pax.idCounter;
};


/*	Method: pax.getNextId
	Increments the unique ID counter, and returns the ID
	
	Parameters:
		none
		
	Example:
		(start code)
		(end)
*/
pax.getNextId = function() {
	pax.idCounter += 1;
	return pax.getId();
};


/*	Method: pax.event.bind
	Add an event to an OBJ
	
	Parameters:
		object - DOM Object to attach the event / function to
		event - The event we're listening for. Note that we don't add the 'on' part
		func - Function to run when event fires

	Example:
		(start code)
			<div id="pax.event.bind.example1">Click me!</div>
			[:.
				pax.event.bind( pax.$('pax.event.bind.example1'), 'click', function() { alert('Thanks for clicking!') } );
			:]
		(end)

		Alerts when the div's onclick function fires.
		
	Notes:
		- To avoid memory leaks, the bind function automatically adds an <pax.event.unbindAll> function call to window.onunload
		- It is best practice to NOT to assign local closure variables to an outside reference.
		
	If you use:
		
			var el = document.getElementById('exampleWindowCloseButton');
			pax.event.bind( el, 'click', function() {...
			
	Then the memory leak will occur. If you use:
		
			pax.event.bind( pax.$('exampleWindowCloseButton'), 'click', function() {...
			
	Then there is no memory leak, as pax.unbindAll will clear the binding before we unload the window.

*/
pax.event.bind = function( obj, event, func ) {
	if( typeof( func ) == 'undefined' || typeof( event ) == 'undefined' || typeof( obj ) == 'undefined' || obj == null )return false;
	
	if( obj.attachEvent ) {
		obj['e' + event + func] = func;
		obj[event + func] = function(){ obj['e' + event + func]( window.event ); };
		obj.attachEvent( 'on' + event, obj[event + func] );
	} else obj.addEventListener( event, func, false );
	//	Check if the binding exists in the queue before pushing it.
	if( ! pax.event.hasBinding( obj, event, func ) ) {
		pax.bindQueue.push( { obj: obj, event: event, func: func } );
		var boundObj = pax.bindQueue[ pax.bindQueue.length -1 ];
	}
	
	if( pax.bindQueue.length == 1 )pax.event.bind( window, 'unload', pax.event.unbindAll );
	
	return boundObj;
};

//	Same as bind event, but binds a list of events to the same function
pax.event.bindEventList = function( obj, event, func ) {
	var eventList = ( typeof( event ) == typeof( [] ) )? event: [event];
	var boundObjs = [];
	for( var i = 0; i < eventList.length; i++ ) {
		boundObjs.push( pax.event.bind( obj, eventList[i], func ) );
	}
	return boundObjs;
};


/*	Method: pax.event.bindOne
	Binds the event once only, ie: clears any other bound functions on the event first.
*/
pax.event.bindOne = function( obj, event, func ) {
	if( pax.util.getType( event ) == 'array' ) {
		for( var i in event ) {
			if( pax.event.hasBinding( obj, event[i] ) ) {
				pax.event.unbind( obj, event[i] );
			}
		}
		return pax.event.bindEventList( obj, event, func );
	} else {
		if( pax.event.hasBinding( obj, event ) ) {
			pax.event.unbind( obj, event );
		}
		return pax.event.bind( obj, event, func );
	}
};


/*	Method: pax.event.hasBinding
	Returns true if an object has a certain event bound to the given function. This uses the <pax.bindQueue>
	
	Parameters:
		object - DOM Object we're inspecting
		event - The event we're testing for. Note that we don't add the 'on' part
		func - Function that we're testing for

	Example:
		(start code)
			<div id="pax.event.hasBinding.example1"></div>			
			[:.
				var myFunc = function() {
					alert('Thanks for clicking!');
				};
				var box = pax.$('pax.event.hasBinding.example1');
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
				box.innerHTML += 'adding binding...<bR>';
				pax.event.bind( box, 'click', myFunc );
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
				box.innerHTML += 'removing binding...<bR>';
				pax.event.unbind( box, 'click', myFunc );
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
			:]
		(end)

		
*/
pax.event.hasBinding = function( obj, event, func ) {
	var result = false;
	for( var x = 0; x < pax.bindQueue.length; x++ ) {
		if( typeof( func ) != 'undefined' ) {
			if( ( ( pax.bindQueue[x].obj == obj ) && ( pax.bindQueue[x].event == event ) && ( pax.bindQueue[x].func == func ) ) ) {
				result = true;
			}
		} else {
			if( ( ( pax.bindQueue[x].obj == obj ) && ( pax.bindQueue[x].event == event ) ) ) {
				result = true;
			}
		}
	}
	return result;
};


/*	Method: pax.event.unbind
	Remove an event from an OBJ
	
	Parameters:
		object - DOM Object to remove the event from
		event - The event we assigned originally. Note that we don't add the 'on' part
		func - Function that was assigned to run when event fires

	Example:
		(start code)
			<div id="pax.event.unbind.example1">Click me!</div>
			[:.
				var cluck = function() {
					pax.event.unbind( pax.$('pax.event.unbind.example1'), 'click', cluck );
					alert('Thanks for clicking! Now click OK, and try again. It wont work the 2nd time, as we have unbound the function.');
				};
				pax.event.bind( pax.$('pax.event.unbind.example1'), 'click', cluck );
			:]
		(end)
		Removes the previously bound function event.

	Example:
		(start code)
			<div id="pax.event.unbind.example2">Click me!</div><input type='button' id="pax.event.unbind.example2.button" value='Unbind'>
			[:.
				var cluck1 = function() { console.log('1'); };
				var cluck2 = function() { console.log('2'); };
				pax.event.bind( pax.$('pax.event.unbind.example2'), 'click', cluck1 );
				var unbindCluck = function() {
					pax.event.unbind( pax.$('pax.event.unbind.example2'), 'click' );
					alert('Thanks for clicking! Now click OK, and try again. It wont work the 2nd time, as we have unbound the function.');
				};
				pax.event.bind( pax.$('pax.event.unbind.example2'), 'click', cluck2 );
				pax.event.bind( pax.$('pax.event.unbind.example2.button'), 'click', unbindCluck );
			:]
		(end)
		Removes all previously bound click events.
*/
pax.event.unbind = function( obj, event, func ) {
	if( func ) {
		if( obj.removeEventListener ) {
			obj.removeEventListener( event, func, false );
		} else if( obj.detachEvent ) {
			obj.detachEvent( 'on' + event, obj[event + func] );
			obj[event + func] = null;
		}
	   
		//	Remove item from queue
		var newBindQueue = [];
		for( var x = 0; x < pax.bindQueue.length; x++ ) {
			if( ! ( ( pax.bindQueue[x].obj === obj ) && ( pax.bindQueue[x].event === event ) && ( pax.bindQueue[x].func === func ) ) ) {
				newBindQueue.push( pax.bindQueue[x] );
			}
		}
		pax.bindQueue = newBindQueue;
	} else {
		//	Assume we're detatching ALL functions for the given event on the obj
		for( var i = 0; i < pax.bindQueue.length; i++ ) {
			if( event ) {
				if( pax.bindQueue[i].obj == obj && pax.bindQueue[i].event == event ) {
					pax.event.unbind( pax.bindQueue[i].obj, pax.bindQueue[i].event, pax.bindQueue[i].func );
				}
			} else {
				if( pax.bindQueue[i].obj == obj ) {
					pax.event.unbind( pax.bindQueue[i].obj, pax.bindQueue[i].event, pax.bindQueue[i].func );
				}
			}
		}
	}
};


/*	Method: pax.event.unbindAll
	Remove all registered events from all OBJs. This method is automatiacally called on window unload.
	
	Note:
		There is no example, as it would break the example window's functionality. It removes all previously bound function events.
*/
pax.event.unbindAll = function( unbindObj ) {
	if( typeof( unbindObj ) != 'undefined' ) {
		for( var i = 0; i < pax.bindQueue.length; i++ ) {
			with( pax.bindQueue[i] ) {
				if( obj == unbindObj )pax.event.unbind( obj, event, func );
			}
		}
	} else {
		for( var i = 0; i < pax.bindQueue.length; i++ ) {
			with( pax.bindQueue[i] ) {
				pax.event.unbind( obj, event, func );
			}
		}
		pax.bindQueue = [];
	}
};



/*	Method: pax.event.bindKeyDown
	Binds a key down event to an object
	
	Parameters:
		object - DOM Object to bind the keydown event to
		func - Function that runs when the key fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyDown.keyInput"> Key code: <input type="text" id="pax.event.bindKeyDown.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyDown.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyDown( pax.$('pax.event.bindKeyDown.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key in the first text box
*/
pax.event.bindKeyDown = function( obj, func ) {
	// This may fail! - should check for safari differently.
	if( pax.isSafari )return pax.event.bind( obj, 'keypress', func );
	else return pax.event.bind( obj, 'keydown', func );
};


/*	Method: pax.event.bindKeyUp
	Binds a key up event to an object
	
	Parameters:
		object - DOM Object to bind the keyup event to
		func - Function that runs when the event fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyUp.keyInput"> Key code: <input type="text" id="pax.event.bindKeyUp.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyUp.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyUp( pax.$('pax.event.bindKeyUp.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key, then let go of it, in the first text box
*/
pax.event.bindKeyUp = function( obj, func ) {
	if( pax.isSafari )return pax.event.bind( obj, 'keypress', func );
	else return pax.event.bind( obj, 'keyup', func );
};


/*	Method: pax.event.bindKeyPress
	Binds a key press event to an object. The reason we have this method is that it will work in *all* browsers
	
	Parameters:
		object - DOM Object to bind the keypress event to
		func - Function that runs when the event fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyPress.keyInput"> 
			Key code: <input type="text" id="pax.event.bindKeyPress.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyPress.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyPress( pax.$('pax.event.bindKeyPress.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key in the first text box
*/
pax.event.bindKeyPress = function( obj, action ) {
	pax.event.bind( obj, 'keypress', action );
};
  

/*	Method: pax.scope
	Brings the scope of an object into the current scope, with the optional given name.
	If no name is specified, then it is assumed an object with 'name' -> value is passed,
	and each variable will be available as 'name'. The global variable is simply a copy, and is not linked to the local variable
	
	Parameters:
		obj - object to put into global name space
		name - optional name to use for the object; if no name is given, the object will be traversed, and the items in it will be globalised.

	Example:
		(start code)
			Type before global scope: <div id='pax.scope.example1.type' style='border: 1px solid red'></div>
			Type after global scope: <div id='pax.scope.example1.typeAfter' style='border: 1px solid green'></div>
			[:.
				var globalise = function () {
					var localVariable = "local";
					pax.scope( localVariable, 'gVar' );
				};
				pax.$('pax.scope.example1.type').innerHTML = typeof(gVar) + '<b' + 'r>';
				globalise();
				pax.$('pax.scope.example1.typeAfter').innerHTML = typeof(gVar) + '<b' + 'r>';
			:]
		(end)
		This example brings a variable from the scope of the function, into the current scope.

	Example:
		(start code)
			Log: <div id='pax.scope.example1.log' style='border: 1px solid black'></div>
			[:.
				var myFunc = {
					init: function ( value ) {
						this.variable = value;
					},
					scopalise: function() {
						pax.scope( this.variable, 'gvar2' );
					},
					log: function( header) {
						var t = pax.$('pax.scope.example1.log');
						t.innerHTML += '[' + header + ']<b' + 'r> local type: <b>' + typeof(this.variable) + '</b> value: <b>' + 
							this.variable + '</b><b' + 'r>global type: <b>' + typeof(gvar2) + '</b>';
						if( typeof(gvar2) != 'undefined' )t.innerHTML += ' value: <b>' + gvar2 + '</b>';
						t.innerHTML += '<hr>';
					}
				};
				myFunc.init( 'I\'m a local' );
				myFunc.log( 'before scopalise' );
				myFunc.scopalise();
				myFunc.log( 'after scopalise' );
				gvar2 = "I\'m a global";
				myFunc.log( 'change scopalised variable' );
			:]
		(end)
		This example brings a variable from the scope of the function, into the current scope, and shows that it is just a copy, not the actual variable.
		
		Note: This function is REALLY expensive due to eval!
*/
pax.scope = function( obj, name ) {
	var myObj = obj;
	
	if( pax.util.getType( obj ) != 'object' ) {
		if( typeof( name ) != 'string' ) return false;	//	must specify a name, if you don't specify an object, otherwise you won't know how to reference it.
		myObj = { name: obj };
	} else {
		if( typeof( name ) != 'string' )name = 'global';
	}

	myObj[name] = obj;
	
	window['__' + name + '__scope'] = myObj;
	
	//	This may not work in opera or safari!
	for( var v in myObj ) {
		var evalLine = ["var ", v, " = (window['__", name, "__scope'])? window['__", name, "__scope'].", v, ": null"].join('');
		if( window.execScript ) {
			try {
				window.execScript( evalLine );
			}
			catch( e ) {
				//	If we cannot execScript it 
				window.eval( evalLine );
			}
		} else if( window.eval ) {
			window.eval( evalLine );
		}
		else eval( evalLine );
	}
	
	//	Cleanup the window variable here...
	window['__' + name + '__scope'] = null;
};


//	Prevent propagation of an event
pax.event.preventpropagate = function(e) {
	if( typeof( e.stopPropagation ) != 'undefined' )e.stopPropagation();
	else e.cancelBubble = true;
};


/*	Method: pax.unJSON
		Converts a JSON string to an object. This method is only here because it is best practice to do it this way.

	Parameters:
		jsonStr - the string to convert.

	Example:
		(start code)
			<div id='pax.unJSON.example1'></div>
			[:.
				var myFruitString = "{	'apples': ['Granny Smith', 'Red delicious'], ";
				myFruitString += "		'oranges': ['Valencia', 'Blood'], ";
				myFruitString += "		'pears': 'green' }";
				var myFruits = pax.unJSON( myFruitString );
				pax.$('pax.unJSON.example1').innerHTML = pax.util.toString(myFruits);
			:]
		(end)
		Will return an object with this shape:
		
		{ 
			'apples': [
				'Granny Smith',
				'Red delicious'
			],
			'oranges': [
				'Valencia',
				'Blood'
			],
			'pears': 'green'
		}

*/
pax.unJSON = function( jsonStr ) {
	return eval( '(' + jsonStr + ')' );
};


/*	Method: pax.JSON
		Converts an object into a JSON string.

	Parameters:
		obj - the object to convert.

	Example:
		(start code)
			<div id='pax.JSON.example1'></div>
			[:.
				var myFruitObj = { 
					'apples': ['Granny Smith', 'Red delicious'], 
					'oranges': ['Valencia', 'Blood'],
					'pears': 'green' 
				};
				var myFruitString = pax.JSON( myFruitObj );
				pax.$('pax.JSON.example1').innerHTML = myFruitString;
			:]
		(end)
		Will display the object in JSON string format, with strings URI encoded
*/
pax.JSON = function( obj, prev ) {
	prev = prev || [];
	//	This will show '*circular ref*', when we have string values repeated.
	//	TODO: create a JSONP-like obj instead.
	if( pax.util.hasValue( obj, prev ) && pax.util.getType( obj ) == 'object' ) {
		return '"*circular ref*: ' + obj + '"';
	} else {
		var result = [];
		prev.push( obj );
		var type = pax.util.getType(obj);
		switch( type ) {
			case 'string':
				return '"' + encodeURIComponent( obj.replace( new RegExp('(["\\\\])', 'g'), '\\$1') ).replace( /%20/g, '+' ) + '"';
			case 'array':
				for( var i = 0; i < obj.length; i++ )result.push( pax.JSON( obj[i], prev ) );
				return '[' + result.join(',') + ']';
			case 'object':
				for (var property in obj)result.push( '"' + property + '":' + pax.JSON( obj[property], prev ) );
				return '{' + result.join(',') + '}';
		}
	}
	return String( obj );
};


/*	Method: pax.postString
	URL encodes an oject into a post string to be used in <pax.post>.
	
	Parameters:
		param - an object containing key / value pairs to be encoded

	Example:
		(start code)
			Before: <div id='pax.postString.example1.before'></div>
			After: <div id='pax.postString.example1.after'></div>
			[:.
				var postObj = { id: 23, action: 'update' };
				var myPost = pax.postString( postObj );
				pax.$('pax.postString.example1.before').innerHTML = pax.util.inspect(postObj);
				pax.$('pax.postString.example1.after').innerHTML = myPost;
			:]
		(end)

		When this is run, myPost becomes '&id=23&action=update'
*/
pax.postString = function( param ) {
	var url = "";
	var count = 0;
	
	for( var p in param ) {
		//	We use the built in URI encoder here...
		url += "&" + p + "=" + encodeURIComponent( param[p] ).replace( /%20/g, '+' );
	}

	return url;
};




/*	Method: pax.postParams
	URL decodes a post string back into a set of parameters
	
	Parameters:
		string - a string to be decoded into parameters
*/
pax.postParams = function( string ) {
	var queryString = {};
	var parameters = string.split('&');
	for( var i = 0; i < parameters.length; i++ ) {
		var pos = parameters[i].indexOf('=');
		if( pos > -1 ) {
			var paramname = parameters[i].substring( 0, pos );
			var paramval = parameters[i].substring( pos + 1 );
			queryString[paramname] = unescape( paramval.replace(/\+/g,' ') );
		} else {
			//special value when there is a querystring parameter with no value
			queryString[parameters[i]] = "";
		}
	}		
	return queryString;
};


/*	Method: pax.defaultArgs
	Overrides default arguments in the given object only if they exist in the given argument
	
	Parameters:
		obj - an object containing key / value pairs to be overridden
		args - an object containing optional key / value pairs to override the contents of obj

	Example:
		(start code)
			Before: <div id='pax.defaultArgs.example1.before'></div>
			After: <div id='pax.defaultArgs.example1.after'></div>
			[:.
				var config = { name: 'Bob Smith', email: 'bob@smith.com', action: 'update' };
				pax.$('pax.defaultArgs.example1.before').innerHTML = pax.util.toString( config );
				pax.defaultArgs( config, { action: 'insert' } );
				pax.$('pax.defaultArgs.example1.after').innerHTML = pax.util.toString( config );
			:]
		(end)
		Changes config to contain the action 'insert'
*/
pax.defaultArgs = function( obj, args ) {
	//	Loop on supplied args
	for( var a in args ) {
		for( var o in obj ) {
			//	Match on default obj keys, and override with args
			if( o == a )obj[o] = args[a];
		}
	}
	
	return obj;
};


/*	Method: pax.defAddArgs
	Overrides default arguments in the given object, and adds any other attributes in the argument
	
	Parameters:
		obj - an object containing key / value pairs to be overridden
		args - an object containing optional key / value pairs to override the contents of obj

	Example:
		(start code)
			Before: <div id='pax.defaultArgs.example1.before'></div>
			After: <div id='pax.defaultArgs.example1.after'></div>
			[:.
				var config = { name: 'Bob Smith', email: 'bob@smith.com', action: 'update' };
				pax.$('pax.defaultArgs.example1.before').innerHTML = pax.util.toString( config );
				pax.defaultArgs( config, { action: 'insert' } );
				pax.$('pax.defaultArgs.example1.after').innerHTML = pax.util.toString( config );
			:]
		(end)
		Changes config to contain the action 'insert'
*/
pax.defAddArgs = function( obj, args ) {
	//	Loop on supplied args
	for( var a in args ) {
		obj[a] = args[a];
	}
	
	return obj;
};





/*	Private Method: pax.getRequestObject
	Creates a XMLHttpRequest object. In the context of the PAX library, this is a private method, although you could use it to create a request object yourself.
	
	Parameters:
		none

	Example:
		(start code)
			<fieldset><legend>Response from Request Object</legend>
				<div id='pax.getRequestObject.example1'></div>
			</fieldset>
			[:.
				var myRO = pax.getRequestObject();
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.getRequestObject.example1').innerHTML = txt;
				};
				
				myRO.onreadystatechange = function() {
					if( myRO.readyState == 4 ) {
						callBack( myRO.responseXML, myRO.responseText, '' );
					}
				};
	
				myRO.open( 'GET', '/pax/pax.getRO.example1.txt', true );
				myRO.send( null );
				
			:]
		(end)

		Creates a request object, and uses it to load some text from the server. You don't need to do this, you should use wither <pax.get> or <pax.post>
*/
pax.getRequestObject = function() {
	var ro;
	
	/*
	if( typeof XMLHttpRequest != undefined ) {
		ro = new XMLHttpRequest();
	} else {
		try {
			ro = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch( e ) {
			//ro = new XMLHttpRequest();
			//	Fallback to IE 4?
		}
	}
	*/
	
	try {
		ro = new ActiveXObject("Microsoft.XMLHTTP");
	}
	catch( e ) {
		//ro = new XMLHttpRequest();
		//	Fallback to IE 4?
		if (typeof XMLHttpRequest != undefined) {
			ro = new XMLHttpRequest();
		}
		else {
			// We are SOL.
		}
	}
	
	return ro;
};


/*	Private Method: pax.removeRequestQueueItem
	Removes the specified request object from the request queue. This is a private method.
	
	Parameters:
		robj - Request object to remove

*/
pax.removeRequestQueueItem = function( robj ) {
	var newRequests = [];
	for( var x = 0; x < pax.requestQueue.length; x++ ) {
		if( ! ( robj === pax.requestQueue[x].ro ) ) {
			newRequests.push( pax.requestQueue[x] );
		}
	}
	pax.requestQueue = newRequests;
};


/*	Private Method: pax.getRequestQueueItem
	Retreives the specified request object from the request queue. This is a private method.
	
	Parameters:
		robj - Request object to find

*/
pax.getRequestQueueItem = function( robj ) {
	for( var x = 0; x < pax.requestQueue.length; x++ ) {
		if( robj === pax.requestQueue[x].ro ) {
			return pax.requestQueue[x];
		}
	}
	return null;
};


/*	Private Method: pax.showStatus
	Display the status for each request in the queue, using a spinner image: assumes a container with id of <pax.statusSpinnerContainer>, which will be created if it doesn't exist.
	This is a private method, although you could use it to display an image for each request in the queue.
	
	Parameters:
		spinnerClass - Image to use
*/
pax.showStatus = function( spinnerClass ) {

	spinnerClass = (spinnerClass)? spinnerClass: pax.statusSpinnerClass;

	if( pax.showStatusSpinner ) {
		var sb = document.getElementById( pax.statusSpinnerContainer );

		// Insert the status box, if it doesn't already exist
		//	Note: this will appear at the end of the body, that may not be the best place...
		if( sb == null ) {
			var div = document.createElement('DIV');
			div.id = pax.statusSpinnerContainer;
			div.style.position = 'absolute';
			document['body'].appendChild( div );
			sb = document.getElementById( div.id )
		}
		
		sb.innerHTML = '';
		pax.util.addClassName( sb, spinnerClass );
		for( var re in pax.requestQueue ) {
			if( typeof pax.requestQueue[re].spinnerBox != 'undefined' ) {
				sb = document.getElementById( pax.requestQueue[re].spinnerBox );
				if( sb )pax.util.addClassName( sb, spinnerClass );
			}
		}
	}
};


/*	Method: pax.hideStatus
	Hides the status spinner
	
	Parameters:
		none

*/
pax.hideStatus = function() {
	if( pax.showStatusSpinner ) {
		var sb = document.getElementById( pax.statusSpinnerContainer );
		if( sb ) {
			var spinnerClass = pax.statusSpinnerClass;
			pax.util.removeClassName( sb, spinnerClass );
			
			for( var re in pax.requestQueue ) {
				spinnerClass = (pax.requestQueue[re].spinnerClass)? pax.requestQueue[re].spinnerClass: pax.statusSpinnerClass;
				if( typeof pax.requestQueue[re].spinnerBox != 'undefined' )sb = document.getElementById( pax.requestQueue[re].spinnerBox );
				if( sb )pax.util.removeClassName( sb, spinnerClass );
			}
		}
	}
};


/*	Method: pax.setCallBack
	Set the onreadystatechange to a callback function, which calls the given function. Also shows hides the status spinner as required.

	Parameters:
		url - URL to send a request to.
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into, false = don't use
		spinnerClass - CSS Class to use for the request spinner, false = don't use

	Example:
		This method is only useful if you're overriding one of the request methods, such as <pax.get> or <pax.post>
		(start code)
			<fieldset><legend>Response from Request Object</legend>
				<div id='pax.setCallBack.example1'></div>
			</fieldset>
			[:.
				var url = '/pax/pax.getRO.example1.txt';
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.setCallBack.example1').innerHTML = txt;
				};
				
				var myRObj = pax.setCallBack( url, callBack, 'setCallBack example', false, false );
				myRObj.ro.open( 'GET', url, true );
				myRObj.ro.send( null );
			:]
		(end)

		This will send a request, and receive a response via using the setCallBack method.
*/
pax.setCallBack = function( url, callBack, callDesc, spinnerBox, spinnerClass ) {
	spinnerBox = ( typeof spinnerBox != 'undefined' )? spinnerBox: pax.statusSpinnerContainer;
	spinnerClass = ( typeof spinnerClass != 'undefined' )? spinnerClass: pax.statusSpinnerClass;
	
	var robj = pax.getRequestObject();

	var onReadyStateChange = function() {
		if( robj.readyState == 4 ) {
			var resXML = robj.responseXML;
			var resTXT = robj.responseText;
			pax.cancel( robj );
			callBack( resXML, resTXT, url);
		}
		//	We can add functions to handle other states here...
	};
	
	var roInterval = setInterval( onReadyStateChange, 12 );
	
	//	IE requires that we wrap the XMLHttpRequest in an object, as it doesn't allow expandos on the request object.
	var rqObj = { 
		ro: robj, 
		url: url, 
		callBack: callBack, 
		callDesc: callDesc, 
		spinnerBox: spinnerBox, 
		spinnerClass: spinnerClass,
		timerInterval: roInterval		
	};

	pax.requestQueue.push( rqObj );

	return rqObj;
};



/*	Method: pax.cancel
	Cancel the pax call, and destroy request object
	
	Parameters:
		robj - Request object to cancel call from

	Example:
		(start code)
			<div id='pax.cancel.example1'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.cancel.example1').innerHTML = "The name retreived from the server is: " + response.data[0].name;
				};
				var myRO = pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 2 } ), mycallBack, 'Post example', 'pax.cancel.example1' );
				
				window.setTimeout( function() { pax.cancel( myRO.ro ); pax.$('pax.cancel.example1').innerHTML = 'Cancelled request'; }, 1000 );
			:]
		(end)

		This example creates a call that takes 2000ms, then cancels the call after 1000ms
*/
pax.cancel = function( robj ) {
	var requestQueueItem = pax.getRequestQueueItem( robj );
	if( requestQueueItem ) {
		var timerInterval = pax.getRequestQueueItem( robj ).timerInterval;

		pax.hideStatus();
		pax.removeRequestQueueItem( robj );
		
		robj.abort();
		
		//	Destroy obj (so we don't leak memory)
		robj.onreadystatechange = function(){};
		if( timerInterval ) {
			clearInterval( timerInterval );
			timerInterval = null;
		}
	}
	robj = null;
};
/* ----------------------------------------------------------------------------

	pax.util.js Copyright (C) 2004, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.util
		This is the utility part of the PAX library, it has handy methods to do various tasks
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/
/*	
	WIP: This is being integrated with the PAX base library.
*/

var pax = pax || {};
pax.util = pax.util || {};

/*	Property: pax.util.genElementID
		Global Gnerated element unique id	*/
pax.util.genElementID = 0;

//	Sets attribute(s) for a given element
//	This won't work in certain instances for IE6 down as some attributes cannot be set in IE.
//	The only known hack is to set the attributes as the element is being generated, though see
//	the collection manager for a possible better hack.
pax.util.attrib = function( element, attribs ) {
	for( var attrib in attribs ) {
		var value = attribs[attrib];
		element[attrib] = value;
		if (element.setAttribute)element.setAttribute(attrib, value);
	}
	
	return element;
};

//	Creates an element, note: name defaults to id
pax.util.genElement = function( type, args ) {
	args.id = (typeof(args.id) != 'undefined') ? args.id : pax.getNextId();
	args.name = (typeof(args.name) != 'undefined') ? args.name : args.id;
	
	if (pax.isIe6Down) {
		//	Create using a string for Ie6 and down
		//	TODO: check if it is an element with start and end tag, or just a singular tag
		var eleStr = '<' + type + ' ';
		for( var i in args ) {
			eleStr += i + '="' + args[i].split('"').join('\\"') + '" ';
		}
		eleStr += '/>';
		
		var ele = document.createElement( eleStr );
	}
	else {
		var ele = document.createElement(type);
		pax.util.attrib(ele, args);
	}
	
	return ele;
};

//	Removes an element
pax.util.removeElement = function( ele ) {
	if( ele.tagName != 'BODY' ) {
		if( pax.isIe ) {
			var destroy = document.createElement('div');
			destroy.appendChild( ele );
			destroy.innerHTML = '';
		} else {
			if( ele && ele.parentNode ) {
				ele.parentNode.removeChild( ele );
			}
		}
	}
};


/*	Method: pax.util.getAgent
	Private method, detects browser string, you should detect browser capability instead, handy in some instances. This is run on startup by default
	
	Parameters:
		none

	Example:
		(start code)
			<div>Is IE: [:= pax.isIe :]</div>
			<div>Is Safari: [:= pax.isSafari :]</div>
			<div>Is Gecko: [:= pax.isGecko :]</div>
		(end)
		Will show the detected status of each browser type, we don't call the detection method, as it is automatically run when this library is loaded.

	This is automatically run when this library is loaded.
*/
pax.util.getAgent = function() {
	pax.isIe = false;
	pax.isIe6Down = false;
	pax.isOpera = false;
	pax.isSafari = false;
	pax.isGecko = false;
	
	if( navigator.userAgent.indexOf("Safari") > 0 ) {
		pax.isSafari = true;
	} else if( navigator.product == "Gecko" ) {
		pax.isGecko = true;
	} else if( window.opera ){
		pax.isOpera = true;
	} else {
		pax.isIe = true;
		var version = (navigator.userAgent.toLowerCase().match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1];
		pax.isIe6Down = (version == '6.0');
	}
};

//	Run the detection on include of this file.
pax.util.getAgent();

if( pax.isIe ) {
	//	Hack to eliminate background image flicker in IE. http://mister-pixel.com/#Content__state=is_that_simple
    document.execCommand( "BackgroundImageCache", false, true );
};


/*	Method: pax.util.copyObj
	Returns a new object with the attribues of the given object, optionally removing duplicates
	
	Parameters:
		obj - The object to copy
		allowDupes - Optionally remove duplicate items

	Example:
		(start code)
			<div id='pax.util.copyObj.example1'></div>
			[:.
				var basket1 = [ 'apple', 'banana', 'pear' ];
				var basket2 = pax.util.copyObj( basket1 );
				basket2[basket2.length] = 'lime';
				pax.$('pax.util.copyObj.example1').innerHTML = 'basket1: ' + pax.util.toString( basket1 ) + '<hr>basket2: ' + pax.util.toString( basket2 );
			:]
		(end)
		This will copy basket1 into basket2, and then add a lime to basket2
		
	Example:
		(start code)
			<div id='pax.util.copyObj.example2'></div>
			[:.
				var basket1 = [ 'apple', 'banana', 'pear', { chocolate: 'dark Belgian', strawberries: 'Queensland' } ];
				var basket2 = pax.util.copyObj( basket1 );
				var wineCooler = { ice: 'cold', bottles: ['Yalumba chardonnay', 'Penfolds five'], corkscrew: { automatic: 'powercorker2000', manual: 'waiters friend' } };
				basket2[basket2.length] = wineCooler;
				pax.$('pax.util.copyObj.example2').innerHTML = 'basket1: ' + pax.util.toString( basket1 ) + '<hr>basket2: ' + pax.util.toString( basket2 );
			:]
		(end)
		This will copy basket1 into basket2, and then add a complex wineCooler object to basket2
		
	Example:
		(start code)
			<div id='pax.util.copyObj.example3'></div>
			[:.
				var basket1 = [ 'apple', 'banana', 'pear', { chocolate: 'dark Belgian', strawberries: 'Queensland' } ];
				var basket2 = pax.util.copyObj( basket1 );
				var wineCooler = { basketOne: basket1, ice: 'cold', bottles: ['Yalumba chardonnay', 'Penfolds five'], corkscrew: { automatic: 'powercorker2000', manual: 'waiters friend' } };
				basket2[basket2.length] = wineCooler;
				pax.$('pax.util.copyObj.example3').innerHTML = 'basket1: ' + pax.util.toString( basket1 ) + '<hr>basket2: ' + pax.util.toString( basket2 );
			:]
		(end)
		This will copy basket1 into basket2, and then add a complex wineCooler object to basket2, which already contains basket1, thus creating a circular reference
*/
pax.util.copyObj = function( obj, allowDupes ) {
	allowDupes = ( typeof( allowDupes ) != 'undefined' )?allowDupes : true;
	var newObj = ( pax.util.getType( obj ) == 'array' )?[] : {};

	if( pax.util.getType( obj ) == 'array' ) {
		for( var a in obj ) {
			if( allowDupes )newObj[newObj.length] = obj[a];
			else {
				if( ! pax.util.arrayHasValue( obj[a], newObj ) ) {
					newObj[newObj.length] = obj[a];
				}
			}
		}
	}
	else {
		for( var a in obj )newObj[a] = obj[a];
	}

	return newObj;
};


/*	Method: pax.util.joinObj
	Returns an object with the attribues of both obj1 and obj2. Assumes return object type is to be the same as obj1
	
	Parameters:
		obj1 - The first object to join
		obj2 - The second object to join

	Example:
		(start code)
			<div id='pax.util.joinObj.example1'></div>
			[:.
				var basket1 = [ 'apple', 'banana', 'pear' ];
				var basket2 = [ 'orange', 'lemon', 'lime' ];
				var fruitBasket = pax.util.joinObj( basket1, basket2 );
				pax.$('pax.util.joinObj.example1').innerHTML = pax.util.toString( fruitBasket );
			:]
		(end)
		This will assign [ 'apple', 'banana', 'pear', 'orange', 'lemon', 'lime' ] to fruitBasket;
		
	Example:
		TODO: This example crashes in IE
		(start code)
			<div id='pax.util.joinObj.example2'></div>
			[:.
				var checkBoxMap = {
					'pax.cache.template.js': [ 'pax.js', 'pax.cache.js', 'pax.template.js' ],
					'pax.template.js': [ 'pax.js', 'pax.cache.js', 'pax.cache.template.js', 'pax.template.modifier.js' ],
					'pax.template.modifier.js': [ 'pax.template.js' ],
					'pax.widget.js': [ 'pax.js' ],
					'pax.validate.js': [ 'pax.js', 'pax.util.js' ],
					'pax.fx.js': [ 'pax.js', 'pax.util.js' ]
				};
				
				for( var c in checkBoxMap ) {
					var newDeps = [];
					for( var dep in checkBoxMap[c] ) {
						if( pax.util.hasKey( checkBoxMap[c][dep], checkBoxMap ) ) {
							newDeps = pax.util.joinObj( checkBoxMap[c], checkBoxMap[checkBoxMap[c][dep]], false );
						} else {
							newDeps[newDeps.length] = checkBoxMap[c][dep];
						}
					}
					if( newDeps != [] )checkBoxMap[c] = newDeps;
				}
				
				pax.$('pax.util.joinObj.example2').innerHTML = pax.util.toString( checkBoxMap ).split(',').join(',<b'+'r>');
				
			:]
		(end)
*/
/*
	TODO: This and pax.defaultArgs do almost the same thing, so combine them.
*/
pax.util.joinObj = function( obj1, obj2, allowDupes ) {
	allowDupes = ( typeof( allowDupes ) != 'undefined' )? allowDupes : true;
	var newObj = pax.util.copyObj( obj1, allowDupes );

	if( pax.util.getType( obj1 ) == 'array' ) {
	//	if( typeof( obj1 ) == typeof( [] ) ) {
		for( var a in obj2 ) {
			if( allowDupes )newObj[newObj.length] = obj2[a];
			else {
				if( ! pax.util.arrayHasValue( obj2[a], newObj ) ) {
					newObj[newObj.length] = obj2[a];
				}
			}
		}
	}
	else {
		for( var a in obj2 )newObj[a] = obj2[a];
	}
	return newObj;
};


/*	Method: pax.util.getType
	Returns the type of object that matches the element passed in.

	Parameters:
		obj - the object to inspect.

	Returns:
		'element' - if obj is a DOM element node
		'textnode' - if obj is a DOM text node
		'whitespace' - if obj is a DOM whitespace node
		'array' - if obj is an array
		'object' - if obj is an object
		'string' - if obj is a string
		'number' - if obj is a number
		'boolean' - if obj is a boolean
		'function' - if obj is a function
		false - (boolean) if the object is not defined or none of the above.
		
	Example:
		(start code)
			<div id='pax.util.getType.example1'></div>
			[:.
				var o = pax.$('pax.util.getType.example1');
				o.innerHTML += "DOM element node: " + pax.util.getType( pax.$('pax.util.getType.example1') ) + '<'+'br>';
				o.innerHTML += "Array: " + pax.util.getType( [] ) + '<'+'br>';
				o.innerHTML += "Object: " + pax.util.getType( {} ) + '<'+'br>';
				o.innerHTML += "String: " + pax.util.getType( '' ) + '<'+'br>';
				o.innerHTML += "Number: " + pax.util.getType( 1 ) + '<'+'br>';
				o.innerHTML += "Boolean: " + pax.util.getType( true ) + '<'+'br>';
				o.innerHTML += "Function: " + pax.util.getType( function(){} ) + '<'+'br>';
				o.innerHTML += "Not defined(false): " + pax.util.getType(  ) + '<'+'br>';
			:]
				'textnode' - if obj is a DOM text node
				'whitespace' - if obj is a DOM whitespace node
		(end)
		This will Show the different type strings that getType can return.
		
*/
pax.util.getType = function( obj ) {
	var type = typeof( obj );
	if( type == 'undefined' || obj === null )return false;
	if( type == 'object' ) {
		if( obj.push )return 'array';
		if( obj.htmlElement )return 'element';
		if( obj.nodeName ) {
			switch (obj.nodeType){
				case 1: return 'element';
				case 3: return (obj.nodeValue && obj.nodeValue.test && obj.nodeValue.test(/\S/)) ? 'textnode' : 'whitespace';
			}
		}
	}
	return type;
};


/*	Method: pax.util.toString
	Returns a string representation of the object passed in.

	Warning:
	Do NOT pass in objects that are self referencing

	Parameters:
		obj - the object to inspect.
		
	Example:
		(start code)
			<div id="pax.util.toString.example1"></div>
			[:.
				var config =  {
					url: '/pax/myEditController.php',
					offset: 4, 
					columns: [
						'notes',
						{
							offset: 3,
							foo: function( text ) { alert( text ) }
						}
					],
					test: function(a,b){return a > b;}
				};
				config.self = config;
				config.columns[1]['me'] = config;
				pax.$( 'pax.util.toString.example1' ).innerHTML = pax.util.toString( config );
			:]
		(end)
		This will show an example with two circular references.
		
	Example:
		(start code)
			<div id="pax.util.toString.example2"></div>
			[:.
				pax.$( 'pax.util.toString.example2' ).innerHTML = pax.util.toString( pax.util.toString );
			:]
		(end)
		This will show the <pax.util.toString> function ;o)
		
	Note that this was inspired by mootools
*/
pax.util.toString = function( obj, prev ) {
	prev = prev || [];
	//	This will show '*circular ref*', when we have string values repeated.
	if( pax.util.hasValue( obj, prev ) && pax.util.getType( obj ) == 'object' ) {
		return '"*circular ref*: ' + obj + '"';
	} else {
		var result = [];
		prev.push( obj );
		var type = pax.util.getType(obj);
		switch( type ) {
			case 'string':
				return '"' + obj.replace( new RegExp('(["\\\\])', 'g'), '\\$1') + '"';
			case 'array':
				for( var i = 0; i < obj.length; i++ )result.push( pax.util.toString( obj[i], prev ) );
				return '[' + result.join(',') + ']';
			case 'object':
				for (var property in obj)result.push( '"' + property + '":' + pax.util.toString( obj[property], prev ) );
				return '{' + result.join(',') + '}';
		}
	}
	return String( obj );
};


/*	Method: pax.util.pprint
	Returns a pretty printed string representation of the object passed in.

	Warning:
	Do NOT pass in objects that are self referencing

	Parameters:
		obj - the object to inspect.
		
	Example:
		(start code)
			<div id="pax.util.pprint.example1"></div>
			[:.
				var config =  {
					url: '/pax/myEditController.php',
					offset: 4, 
					columns: [
						'notes',
						{
							offset: 3,
							foo: function( text ) { alert( text ) }
						}
					],
					test: function(a,b){return a > b;}
				};
				config.self = config;
				config.columns[1]['me'] = config;
				pax.$( 'pax.util.pprint.example1' ).innerHTML = '<pre>' + pax.util.pprint( config ) + '</pre>';
			:]
		(end)
		This will show an example with two circular references.
		
	Example:
		(start code)
			<div id="pax.util.pprint.example2"></div>
			[:.
				pax.$( 'pax.util.pprint.example2' ).innerHTML = '<pre>' + pax.util.pprint( pax.util.pprint ) + '</pre>';
			:]
		(end)
		This will show the <pax.util.pprint> function ;o)
		
	TODO: This is very similar to the toString function, except for the indent; we should combine these...
*/
pax.util.pprint = function( obj, prev, indent ) {
	if( typeof(indent) == 'undefined' )indent = 0;
	prev = prev || [];
	if( pax.util.hasValue( obj, prev ) && pax.util.getType( obj ) == 'object' ) {
		return '"*circular ref*: ' + obj + '"';
	} else {
		var result = [];
		prev.push( obj );
		var type = pax.util.getType(obj);
		
		var indentText = '';
		for( var x = 0; x < indent; x++ )indentText += '\t';
		
		switch( type ) {
			case 'string':
				return ' "' + obj.replace( new RegExp('(["\\\\])', 'g'), '\\$1') + '"';
			case 'array':
				for( var i = 0; i < obj.length; i++ )result.push( indentText + pax.util.pprint( obj[i], prev, indent + 1 ) );
				return indentText + '[\n' + result.join(',') + indentText + '\n]';
			case 'object':
				for (var property in obj)result.push( indentText + '"' + property + '":' + pax.util.pprint( obj[property], prev, indent + 1 ) );
				return '{\n' + result.join(',\n') + '\n' + indentText + '}';
		}
	}
	return String( obj );
};


/*	Method: pax.util.hasKey
	Returns the key, if it finds a key from a dict or array
	
	Parameters:
		key - The key to look for
		obj - The dictionary or array to look in

	Example:
		(start code)
			<div id="pax.util.hasKey.example1"></div>
			[:.
				var fruitBasket = { orange: 3, lemon: 2, lime: 0 };
				pax.$('pax.util.hasKey.example1').innerHTML = "Is there a key of 'line' in the fruit basket?: " + pax.util.hasKey( 'lime', fruitBasket );
			:]
		(end)
		This will show true

	Example:
		(start code)
			<div id="pax.util.hasKey.example2"></div>
			[:.
				var fruitBasket = { orange: 3, lemon: 2, lime: 0 };
				pax.$('pax.util.hasKey.example2').innerHTML = "Is there a 'kiwi' in the fruit basket?: " + pax.util.hasKey( 'kiwi', fruitBasket );
			:]
		(end)
		This will show false
*/
pax.util.hasKey = function( key, obj ) {
	//	Note that the VAR keyword in front of item is necessary if using this in a template, or we get an error: 'Object doesn't support this action'.
	for( var i in obj ) {
		if( key == i )return i;
	}
	return false;
};

/*	Counts number of items in an object
*/
pax.util.numItems = function( obj ) {
	var count = 0;
	for( var i in obj ) {
		count += 1;
	}
	return count;
};


/*	Method: pax.util.hasValue
	Returns the value, if it finds a value in an object or dictionary
	
	Parameters:
		key - The key to look for
		dict - The object or dictionary to look in

	Example:
		(start code)
			<div id="pax.util.hasValue.example1"></div>
			[:.
				var fruitBasket = { orange: 3, lemon: 2, lime: 0 };
				pax.$('pax.util.hasValue.example1').innerHTML = "Does the fruit basket have a value of 2?: " + pax.util.hasValue( 2, fruitBasket );
			:]
		(end)
		This will show true
		
	Example:
		(start code)
			<div id="pax.util.hasValue.example2"></div>
			[:.
				var fruitBasket = { orange: 3, lemon: 2, lime: 0 };
				pax.$('pax.util.hasValue.example2').innerHTML = "Does the fruit basket have a value of 7?: " + pax.util.hasValue( 7, fruitBasket );
			:]
		(end)
		This will show false
*/
pax.util.hasValue = function( value, dict ) {
	//	Note that the VAR keyword in front of item is necessary if using this in a template, or we get an error: 'Object doesn't support this action'.
	for( var i in dict ) {
		if( value == dict[i] )return i;
	}
	return false;
};


/*	Method: pax.util.arrayHasValue
	Returns the index, if it finds a value in an array
	
	Parameters:
		key - The key to look for
		arr - The array to look in

	Example:
		(start code)
			<div id="pax.util.arrayHasValue.example1"></div>
			[:.
				var fruitBasket = [ 'orange', 'lemon', 'lime' ];
				pax.$('pax.util.arrayHasValue.example1').innerHTML = "Is there a 'lemon' in the fruit basket?: " + pax.util.arrayHasValue( 'lemon', fruitBasket );
			:]
		(end)
		This will show true
		
	Example:
		(start code)
			<div id="pax.util.arrayHasValue.example2"></div>
			[:.
				var fruitBasket = [ 'orange', 'lemon', 'lime' ];
				pax.$('pax.util.arrayHasValue.example2').innerHTML = "Is there a 'passionfruit' in the fruit basket?: " + pax.util.arrayHasValue( 'passionfruit', fruitBasket );
			:]
		(end)
		This will show false
		
*/
pax.util.arrayHasValue = function( value, arr ) {
	//	Note that the VAR keyword in front of item is necessary if using this in a template, or we get an error: 'Object doesn't support this action'.
	for( var i = 0; i < arr.length; i++ ) {
		if( value == arr[i] )return i;
	}
	return false;
};


/*	Method: pax.util.getTarget
	Returns the target of an event
*/
pax.util.getTarget = function( e ) {
	if( pax.isIe )return e.target || window.event.srcElement;
	else return e.target;
};

/*	Method: pax.util.getPosition
	Locates position and dimensions of an object
	
	Parameters:
		element - The DOM element to find the position / dimensions of
		excludeParent - Don't take the parent offset into account, to be used if adding an element absolutely positioned inside the same parent element

	Returns:
		A dictionary with x, y, width, height of element. Does not work for the window.

	Example:
		(start code)
			Excluding parent node offsets (as we're positioning a box inside the same parent) - red box should appear at SW corner
			<div id="pax.util.getPosition.example1.divA" style="border: solid 1px #c0c0c0; position: absolute; top: 50px; left: 40px;">Pos - </div>
			<div id="pax.util.getPosition.example1.divB" style='background:red;position:absolute;width:10px; height:10px;'></div>
			[:.
				var myDiv = pax.$('pax.util.getPosition.example1.divA');
				var pos = pax.util.getPosition( myDiv, false );
				myDiv.innerHTML += 'x: ' + pos.x + ' y: ' + pos.y + ' width: ' + pos.width + ' height: ' + pos.height;
				var pos = pax.util.getPosition( myDiv, false );
				var myArrow = pax.$('pax.util.getPosition.example1.divB');
				pax.util.setPosition( myArrow, { x: pos.x + pos.width, y: pos.y + pos.height } );
			:]
		(end)
		pos will be an object that contains {x: int, y: int, width: int, height: int}

	Example:
		(start code)
			Including parent nodes (notice the large y value)
			<div style="border: solid 1px #c0c0c0" id="pax.util.getPosition.example2">Pos - </div>
			[:.
				var myDiv = pax.$('pax.util.getPosition.example2');
				var pos = pax.util.getPosition( myDiv );
				myDiv.innerHTML += 'x: ' + pos.x + ' y: ' + pos.y + ' width: ' + pos.width + ' height: ' + pos.height;
			:]
		(end)
		pos will be an object that contains {x: int, y: int, width: int, height: int}

	Example:
		(start code)
			<span style="border: solid 1px #c0c0c0" id="pax.util.getPosition.example3">Pos - </span>
			[:.
				var mySpan = pax.$('pax.util.getPosition.example3');
				var pos = pax.util.getPosition( mySpan );
				mySpan.innerHTML += 'x: ' + pos.x + ' y: ' + pos.y + ' width: ' + pos.width + ' height: ' + pos.height;
			:]
		(end)
		pos will be an object that contains {x: int, y: int, width: int, height: int}

	Example:
		(start code)
			<span style="border: solid 1px #c0c0c0; position: absolute; top: 50px; left: 40px;" id="pax.util.getPosition.example4">Pos - </span>
			[:.
				var mySpan = pax.$('pax.util.getPosition.example4');
				var pos = pax.util.getPosition( mySpan );
				mySpan.innerHTML += 'x: ' + pos.x + ' y: ' + pos.y + ' width: ' + pos.width + ' height: ' + pos.height;
			:]
		(end)
		pos will be an object that contains {x: int, y: int, width: int, height: int}

	Example:
		(start code)
			<span style="border: solid 1px #c0c0c0; position: absolute; top: 50px; left: 40px;" id="pax.util.getPosition.example5">Pos - </span>
			[:.
				var newSpan = document.createElement( 'SPAN' );
				newSpan.innerHTML = 'Span';
				newSpan.id = 'pax.util.getPosition.example5.span';
				newSpan.style.position = 'absolute';
				newSpan.style.left = '100px';
				newSpan.style.top = '200px';
				document.body.appendChild( newSpan );
				
				var mySpan = pax.$('pax.util.getPosition.example5');
				var myNewSpan = pax.$('pax.util.getPosition.example5.span');
				var pos = pax.util.getPosition( myNewSpan );
				mySpan.innerHTML += 'x: ' + pos.x + ' y: ' + pos.y + ' width: ' + pos.width + ' height: ' + pos.height;
				
				document.body.removeChild( newSpan );
			:]
		(end)
		pos will be an object that contains {x: int, y: int, width: int, height: int}, of the dynamically created newSpan element
*/
pax.util.getPosition = function( element, excludeParent ) {
	excludeParent = ( typeof( excludeParent ) != 'undefined' && excludeParent == false );
	var offsetLeft = 0;
	var offsetTop = 0;

	// Divs can be affected by scroll offsets - this is needed in datagrid.
	// var is_div = /^div$/i.test( element.tagName );
	if( /^div$/i.test( element.tagName ) ) {
		if( element.scrollLeft )offsetLeft = element.scrollLeft;
		if( element.scrollTop )offsetTop = element.scrollTop;

		// We should add the scroll offset too in some instances...?
		// Maybe only if something is absolutely positioned?
		// offsetLeft += ( document.body.scrollLeft )? document.body.scrollLeft: ( window.pageXOffset )? window.pageXOffset : 0;
		// offsetTop += ( document.body.scrollTop )? document.body.scrollTop: ( window.pageYOffset )? window.pageYOffset: 0;
	}

	var pos = {
		x: element.offsetLeft - offsetLeft, 
		y: element.offsetTop - offsetTop, 
		width: element.offsetWidth,
		height: element.offsetHeight
	};
	
	if( ! excludeParent ) {
		if( element.offsetParent ) {
			var tmp = pax.util.getPosition( element.offsetParent );
			pos.x += tmp.x;
			pos.y += tmp.y;
		}
	}
	
	return pos;
};



/*	Method: pax.util.setPosition
	Sets position and dimensions of an object
	
	Parameters:
		element - The DOM element to set the position / dimensions of
		pos - Object as returned from <pax.util.getPosition>

	Example:
		(start code)
			<div id='pax.util.getPosition.example1.divA' style="border: solid 1px #c0c0c0; background: blue; position: absolute; top: 80px; left: 40px;">Click me to cover me with the red div!</div>
			<div id='pax.util.getPosition.example1.divB' style='background:red;position:absolute; left: 10px; top: 40px;'>I'm the red div!</div>
			[:.
				pax.event.bind( pax.$('pax.util.getPosition.example1.divA'), 'click', function() {
					var pos = pax.util.getPosition( pax.$('pax.util.getPosition.example1.divA'), false );
					pax.util.setPosition( pax.$('pax.util.getPosition.example1.divB'), pos );
				} );
			:]
		(end)

*/
pax.util.setPosition = function( element, pos ) {
	if(typeof( pos.x ) == 'number')element.style.left = pos.x + 'px';
	if(typeof( pos.y ) == 'number')element.style.top = pos.y + 'px';
	if(typeof( pos.width ) == 'number')element.style.width = pos.width + 'px';
	if(typeof( pos.height ) == 'number')element.style.height = pos.height + 'px';
};


/*	Method: pax.util.getWindowDimensions
	Returns a dictionary with width, height of the window.
	
	Parameters:
		none

	Returns:
		A dictionary with width, height of the window.

	Example:
		(start code)
			<div id="pax.util.getWindowDimensions.example1"></div>
			[:.
				var myDiv = pax.$('pax.util.getWindowDimensions.example1');
				var winSize = pax.util.getWindowDimensions();
				myDiv.innerHTML += 'width: ' + winSize.width + ' height: ' + winSize.height + ' scrollLeft: ' + winSize.scrollLeft + ' scrollTop: ' + winSize.scrollTop;
			:]
		(end)
		winSize will be an object that contains { width: int, height: int, scrollLeft: int, scrollTop: int };
*/
pax.util.getWindowDimensions = function( tWindow ) {
	tWindow = ( typeof(tWindow) != 'undefined' )? tWindow: window;
	var myWidth = 0, myHeight = 0;
	if( typeof( window.innerWidth ) == 'number' ) {    // Non IE
		myWidth = window.innerWidth;
		myHeight = window.innerHeight;
		//    It seems that non-IE browsers INCLUDE the scrollbar in the width. We can't calculate that, so estimate it to be 19px
		myWidth -= 19;
	} else if( tWindow.document.documentElement && ( tWindow.document.documentElement.clientWidth || tWindow.document.documentElement.clientHeight ) ) {
		//	IE 6+ in 'standards compliant mode'
		myWidth = tWindow.document.documentElement.clientWidth;
		myHeight = tWindow.document.documentElement.clientHeight;
	} else if( tWindow.document.body && (	tWindow.document.body.clientWidth || tWindow.document.body.clientHeight ) ) {    //IE 4 compatible
		myWidth = tWindow.document.body.clientWidth;
		myHeight = tWindow.document.body.clientHeight;
	}

	var docBody = ( tWindow.document.compatMode && tWindow.document.compatMode != "BackCompat" )?	tWindow.document.documentElement : tWindow.document.body;
	var leftOffset = tWindow.document.all? docBody.scrollLeft : tWindow.pageXOffset;
	var topOffset  = tWindow.document.all? docBody.scrollTop :	tWindow.pageYOffset;
	
	return { width: myWidth, height: myHeight, scrollLeft: leftOffset, scrollTop: topOffset };
};


/*	Method: pax.util.getElementsByClassName
	Return all elements by a given tag name and class name.
	
	Parameters:
		element - The DOM element to search
		elementType -  Type of element to look for, you can use "*" to return all element types
		className - The class name of the elements to return

	Returns:
		An array with the required DOM elements

	Example:
		(start code)
			<div id='pax.getElementsByClassName.example1'></div>
			[:.
				var myElement = pax.util.getElementsByClassName( document, 'div', 'CFunction' );
				pax.$('pax.getElementsByClassName.example1').innerHTML = 'There are ' + myElement.length + ' divs with CFunction as class name.';
			:]
		(end)
		myElement will now be an array with all div elements with a class name of 'CFunction' from the document
*/
pax.util.getElementsByClassName = function( element, elementType, className ) {
	var allElements = (elementType == "*" && element.all)? element.all : element.getElementsByTagName(elementType);
	var classElements = new Array();
	className = className.replace(/\-/g, "\\-");
	var findClass = new RegExp("(^|\\s)" + className + "(\\s|$)");
	for( var i=0; i<allElements.length; i++) {
		var myElement = allElements[i];
		if( findClass.test( myElement.className ) )classElements[classElements.length] = myElement;
	}
	return classElements;
};


/*	Method: pax.util.addClassName
	Adds a class name to an element
	
	Parameters:
		element - The DOM element to add a class name to
		className - The class name you want to add

	Example:
		(start code)
			<div id="pax.util.addClassName.example1"></div>
			[:.
				var newSpan = document.createElement( 'SPAN' );
				newSpan.innerHTML = 'Span';
				newSpan.id = 'pax.util.addClassName.example1.span';
				newSpan.style.position = 'absolute';
				newSpan.style.left = '100px';
				newSpan.style.top = '200px';
				document.body.appendChild( newSpan );
				
				var myNewSpan = pax.$('pax.util.addClassName.example1.span');
				pax.util.addClassName( myNewSpan, 'classOne' );
				pax.util.addClassName( myNewSpan, 'classTwo' );
				pax.$('pax.util.addClassName.example1').innerHTML += 'Class names: [' + myNewSpan.className + ']';
				
				document.body.removeChild( newSpan );
			:]
		(end)
		This will add the class names 'classOne' and 'classTwo' to the newSpan element
*/
pax.util.addClassName = function( element, className ) {
	pax.util.removeClassName( element, className );
	element.className += ( ( element.className.length > 0 )? ' ': '' ) + className;
};


/*	Method: pax.util.hasClassName
	Checks for a class name in an element
	
	Parameters:
		element - The DOM element to check
		className - The class name you want to check for

	Example:
		(start code)
			<div id="pax.util.hasClassName.example1" class="classOne classTwo classThree"></div>
			[:.
				pax.$('pax.util.hasClassName.example1').innerHTML = 'hasClassName (classOne): [' + pax.util.hasClassName( pax.$('pax.util.hasClassName.example1'), 'classOne' ) + ']';
				pax.$('pax.util.hasClassName.example1').innerHTML += ' hasClassName (classFour): [' + pax.util.hasClassName( pax.$('pax.util.hasClassName.example1'), 'classFour' ) + ']';
			:]
		(end)
		This will add the class name 'classOne' to the newSpan element, then remove it.
*/
pax.util.hasClassName = function( element, className ) {
	return (!element || !element.className)? false: (new RegExp("\\b"+className+"\\b")).test(element.className);
};



/*	Method: pax.util.removeClassName
	Removes a class name from an element
	
	Parameters:
		element - The DOM element to remove a class name from
		className - The class name you want to remove

	Example:
		(start code)
			<div id="pax.util.removeClassName.example1"></div>
			[:.
				var newSpan = document.createElement( 'SPAN' );
				newSpan.innerHTML = 'Span';
				newSpan.id = 'pax.util.removeClassName.example1.span';
				newSpan.style.position = 'absolute';
				newSpan.style.left = '100px';
				newSpan.style.top = '200px';
				document.body.appendChild( newSpan );
				
				var myNewSpan = pax.$('pax.util.removeClassName.example1.span');
				pax.util.addClassName( myNewSpan, 'classOne' );
				pax.$('pax.util.removeClassName.example1').innerHTML += 'Before removeClassName: [' + myNewSpan.className + '], ';
				pax.util.removeClassName( myNewSpan, 'classOne' );
				pax.$('pax.util.removeClassName.example1').innerHTML += 'After removeClassName: [' + myNewSpan.className + ']';
				
				document.body.removeChild( newSpan );
			:]
		(end)
		This will add the class name 'classOne' to the newSpan element, then remove it.
*/
pax.util.removeClassName = function( element, className ) {
	if( element ) {
		//	TODO: Fix this - doesn't work when there is no whitespace after the class name???
		
		var myClass = new RegExp( ( className + "\s?" ), "i" );
		element.className = element.className.replace(new RegExp("^" + className + "\\b\\s*|\\s*\\b" + className + "\\b",'g'),'');
		//	element.className = element.className.replace(new RegExp("^" + myClass + "\\b\\s*|\\s*\\b" + myClass + "\\b",'g'),'');
		//	element.className = element.className.split( className ).join('');
	}
};

/*	Method: pax.util.swapClassName
	Removes one class name, and adds another, to an element
	
	Parameters:
		element - The DOM element to swap a class names on
		className1 - The class name you want to remove, this can be a list of class names
		className2 - The class name you want to add, this can be a list of class names

	Example:
		(start code)
			<div id="pax.util.swapClassName.example1"></div>
			[:.
				var myDiv = pax.$('pax.util.swapClassName.example1');
				pax.util.addClassName( myDiv, 'classOne' );
				myDiv.innerHTML += 'Before swapClassName: [' + myDiv.className + '], ';
				pax.util.swapClassName( myDiv, 'classOne', 'classTwo' );
				myDiv.innerHTML += 'After swapClassName: [' + myDiv.className + ']';
			:]
		(end)
		This will add the class name 'classOne' to the newSpan element, then swap it with 'classTwo'.

	Example:
		(start code)
			<div id="pax.util.swapClassName.example2"></div>
			[:.
				var myDiv = pax.$('pax.util.swapClassName.example2');
				pax.util.addClassName( myDiv, 'classOne' );
				pax.util.addClassName( myDiv, 'classTwo' );
				myDiv.innerHTML += 'Before swapClassName: [' + myDiv.className + '], ';
				pax.util.swapClassName( myDiv, ['classOne', 'classTwo'], ['classThree', 'classFour'] );
				myDiv.innerHTML += 'After swapClassName: [' + myDiv.className + ']';
			:]
		(end)
		This will add the class names 'classOne' and 'classTwo' to the element, then swap it with 'classThree' and 'classFour'.
*/
pax.util.swapClassName = function( element, className1, className2 ) {
	if( typeof( className1 ) != 'string' ) {
		//	Assume a list, and remove all
		for( var i = 0; i < className1.length; i++ )pax.util.removeClassName( element, className1[i] );
	} else pax.util.removeClassName( element, className1 );
	if( typeof( className2 ) != 'string' ) {
		//	Assume a list, and add all
		for( var i = 0; i < className2.length; i++ )pax.util.addClassName( element, className2[i] );
	} else pax.util.addClassName( element, className2 );
};



/*	Method: pax.util.getStyle
	Returns a given property from an element. A property is computed if it is not explicitly set, and may differ from browser to browser.
	
	WIP: Make this more consistent for every browser.
	
		- Really, you don't say - next time add specific instances!
	
	Parameters:
		element - The DOM element to get the style from
		property - The style property you want to get

	Example:
		(start code)
			<div id="pax.util.getStyle.example1"></div>
			[:.
				var fontSize = pax.util.getStyle( pax.$('pax.util.getStyle.example1'), 'font-size' );
				pax.$('pax.util.getStyle.example1').innerHTML += 'Font size: ' + fontSize;
				var myColour = pax.util.getStyle( pax.$('pax.util.getStyle.example1'), 'color' );
				pax.$('pax.util.getStyle.example1').innerHTML += ' Colour: ' + myColour;
			:]
		(end)
		This will display the font size, and color CSS properties of the given element
*/
pax.util.getStyle = function( element, property ) {
	var myStyle = "";
	if( typeof( element ) == 'undefined' || ! element )return null;
	if( document.defaultView && document.defaultView.getComputedStyle ) {
		if( property )myStyle = document.defaultView.getComputedStyle(element, "").getPropertyValue(property);
		else {
			myStyle = {};
			objStyle = document.defaultView.getComputedStyle(element, "");
			for( var i in objStyle ) {
				if( pax.util.getType( objStyle[i] ) != 'function' && i != 'length' ) {
					myStyle[i] = objStyle.getPropertyValue(i);
				}
			}
		}
	} else if( element.currentStyle ) {
		if( property ) {
			property = property.replace(/\-(\w)/g, function (strMatch, p1) { return p1.toUpperCase(); } );
			myStyle = element.currentStyle[property];
		} else {
			myStyle = element.currentStyle;
		}
	}
	return myStyle;
};



// WIP: Make this work correctly in IE, and move to pax.css...
pax.util.setStyle = function( element, properties ) {

	if( typeof( element ) == 'undefined' || ! element )return null;
	//if( document.defaultView && document.defaultView.getComputedStyle ) {
		//var compstyle = document.defaultView.getComputedStyle(element, "");
		//console.log( pax.util.toString( compstyle ) );
		for( var i in properties ) {
			var property = i.toLowerCase();
			if( property != 'csstext' && property != 'parentrule' ) {
				//	compstyle.setProperty( i, properties[i], '' );
				
				if( property == 'opacity' ) {
					pax.css.opacity( element, properties[i] );
				} else if( property == 'float' ) {
					element.style['styleFloat'] = properties[i];
					if( element.style.setAttribute )element.style.setAttribute('styleFloat', properties[i]);
				} else {
					element.style[i] = properties[i];
					if( element.style.setAttribute )element.style.setAttribute(i, properties[i]);
				}
			}
		}
		
		
	//} else if( element.currentStyle ) {
		//	TODO: IE 
	//}
	
};
/* ----------------------------------------------------------------------------

	pax.data.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.data
		This class allows various ways of parsing of data
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
*/

/*
	TODO: utalise jspon (www.jspon.org) to allow ORM manipulation.

*/

var pax = pax || {};
pax.data = pax.data || {};
pax.data.parse = pax.data.parse || {};

/*	Method: pax.data.parse.data.xmlDOM
	Parses an XML document into a Document Object Model object.
	
	Parameters:
		xmlDocument - an XML document to parse
		
	Note:
		* Attributes are accesed via '_attribute'
		* Values are accesed via '_value'

	Example:
		(start code)
			<a href='../people.xml'>people.xml</a><hr>
			<textarea id='pax.data.parse.xmlDOM.example1' cols=100 rows=16></textarea>
			
			[:.
				function parseXML( xml, txt, url ) {
					pax.$('pax.data.parse.xmlDOM.example1').value = pax.util.pprint( pax.data.parse.xmlDOM( xml ) );
				}
				pax.get( '../people.xml', parseXML );
			:]

		(end)
		This example retreives an XML file, parses it into an object, and pretty prints the result in a textarea.
*/
//	Note that some parts come from http://www.openjs.com/scripts/xml_parser/xml2array.js
pax.data.parse.xmlDOM = function( xmlDocument ) {
	var whitespace = new RegExp(/[^\s]/);	//	RegExp to find whitespace

	//	Process the xml data
	function xml2obj( xmlDoc, parent ) {
		var obj = {}, parentName = "";
		parent = parent || {};

		//	Child node
		var nodeValue = xmlDoc.nodeValue;
		if( xmlDoc.parentNode && xmlDoc.parentNode.nodeName && nodeValue && whitespace.test( nodeValue ) ) {
			obj[xmlDoc.parentNode.nodeName] = {};
			obj[xmlDoc.parentNode.nodeName]._value = nodeValue;
		}

		//	Parent node
		if( xmlDoc.nodeName && xmlDoc.nodeName.charAt(0) != "#" && xmlDoc.childNodes.length > 1 ) {
			parentName = xmlDoc.nodeName;
		}
		
		if( xmlDoc.childNodes.length == 1 ) {
			obj = xml2obj( xmlDoc.childNodes[0], parent );
		} else {	// Iterate on child nodes
			for( var i = 0; i < xmlDoc.childNodes.length; i++ ) {
				var temp = xml2obj( xmlDoc.childNodes[i], parent );
				if( temp ) {
					//	temp is an object like this: { key: value }, and here we get just the the key
					for( var key in temp ) { break;	};
					
					//	Repeated element
					if( obj[key] ) {
						//	We make a numerically indexed object here
						if( ! parent[key] ) {
							parent[key] = 0;
							obj[key] = { 0: obj[key] };
						}
						parent[key] += 1;
						obj[key][parent[key]] = temp[key]; //Members of of a numeric array
					} else {
						parent[key] = 0;
						obj[key] = temp[key];
						//	get the attributes
						if( xmlDoc.childNodes && xmlDoc.childNodes[i] && xmlDoc.childNodes[i].attributes ) {
							for( var j = 0; j < xmlDoc.childNodes[i].attributes.length; j++ ) {
								var nname = xmlDoc.childNodes[i].attributes[j].nodeName;
								if( nname ) {
									obj[key]['_attribute'] = obj[key]['_attribute'] || {};
									obj[key]['_attribute'][nname] = xmlDoc.childNodes[i].attributes[j].nodeValue;
								}
							}
						}
					}
				}
			}
		}

		if( parentName && obj ) {
			var temp = obj;
			obj = {};
			obj[parentName] = temp;
		}
		
		return obj;
	}

	return xml2obj( xmlDocument, 0 );
};
/* ----------------------------------------------------------------------------

	pax.cache.js Copyright (C) 2004, 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.cache
		This class caches objects in the DOM
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/
/*
	TODO: 19-08-04 - We could in theory use the window name to store things in, as a session variable? Investigate this, as it would acrry between requests.
*/

var pax = pax || {};
pax.cache = pax.cache || {};

pax.cache.obj = {};						//	OBJ holder - key : obj
pax.cache.objExpire = {};				//	Expiry holder - key : [ entry time, expiry in seconds ]
pax.cache.defaultExpire = 600;			//	600 seconds = 10 minutes, as default expiry

/*	Method: pax.cache.isCached
	Returns true if an object is cached, (false otherwise), and expires and removes obj as required.
	
	Parameters:
		key - The key used when the object was cached.

	Returns:
		Boolean - true if object is cached, (false otherwise), and expires and removes obj as required.

	Example:
		(start code)
			<div id='pax.cache.isCached.example1'></div>
			[:.
				pax.cache.set( 'myObjKey', { box: 'Shoes' } );
				if( pax.cache.isCached( 'myObjKey' ) ) {
					var contents = "";
					for( var item in pax.cache.get('myObjKey') ) {
						contents += item + " with " + pax.cache.get('myObjKey')[item] + "\n";
					}
					pax.$('pax.cache.isCached.example1').innerHTML = "my object is a: " + contents;
				}
			:]
		(end)
		
		*isCached* returns true if anything has been cached under that key, and it has not expired.
		If it has expired, the object is removed from the cache, and false will be returned.

	Example:
		(start code)
			<div id='pax.cache.isCached.example2'></div>
			[:.
				var myFunc = function() {
					pax.$('pax.cache.isCached.example2').innerHTML = "This is from a cached function!";
				};
				pax.cache.set( 'myObjKey', myFunc );
				if( pax.cache.isCached( 'myObjKey' ) ) {
					pax.cache.get('myObjKey')();
				};
			:]
		(end)
		
		This example populates the DIV, using a cached function.
*/
pax.cache.isCached = function( key ) {
	for( var obj in pax.cache.obj ) {
		if( obj == key ) {
			var now = new Date();
			if( pax.cache.objExpire[key][1] != 0 ) {
				var expireTime = pax.cache.objExpire[key][0] + ( pax.cache.objExpire[key][1] * 1000 );
				if( expireTime < now.getTime() ) {
					pax.cache.remove( key );		// Destroy obj as it has expired.				
					return false;
				}
				else {
					return true;
				}
			}
			else return true;
		}
	}
	return false;
};

/*	Method: pax.cache.set
	Set an object in the cache.
	
	Parameters:
		key - The key to use for caching the object.
		value - The object or value to be cached.
		expire - Time in seconds for the value or object to be valid. Set to 0 to never expire the object (or omit expire)

	Example:
		(start code)
			[:_
				var myFruits = { 
					'apples': [
						'Granny Smith',
						'Red delicious'
					],
					'oranges': [
						'Valencia',
						'Blood'
					],
					'pears': 'green'
				};
				pax.cache.set( 'myObjKey', myFruits, 0 );
				var cacheObj = pax.util.toString( pax.cache.get('myObjKey') );
			:]
			The fruit object contains: [:= cacheObj :]
		(end)
		
		This will cache the myFruits object "forever".
*/
pax.cache.set = function( key, value, expire ) {

	//	Ensure we have a valid expiry time
	expire = (typeof expire != 'undefined')? expire : pax.cache.defaultExpire;
	
	var now = new Date();
	pax.cache.objExpire[key] = [ now.getTime(), expire ];
	pax.cache.obj[key] = value;
};

/*	Method: pax.cache.get
	Retreives an object from the cache, returns null if obj is not cached
	
	Parameters:
		key - The key to use for caching the object.

	Example:
		(start code)
			[:_
				var myFruits = { 
					'apples': [
						'Granny Smith',
						'Red delicious'
					],
					'oranges': [
						'Valencia',
						'Blood'
					],
					'pears': 'green'
				};
				pax.cache.set( 'myObjKey', myFruits, 0 );
				var myObj = pax.cache.get('myObjKey');
				var appleList = pax.util.toString( myObj.apples );
			:]
			The fruit object contains the following apples: [:= appleList :]
		(end)
		
		This will populate myFruits with object cached under 'myObjKey', or null if the key was not found.
*/
pax.cache.get = function( key ) {
	return ( pax.cache.isCached( key ) )? pax.cache.obj[key] : null;
};

/*	Method: pax.cache.remove
	Deletes an object from the cache, returns false if obj is not cached
	
	Parameters:
		key - The key to use for caching the object.

	Example:
		(start code)
			[:_
				var myFruits = { 
					'apples': [
						'Granny Smith',
						'Red delicious'
					],
					'oranges': [
						'Valencia',
						'Blood'
					],
					'pears': 'green'
				};
				pax.cache.set( 'myObjKey', myFruits, 0 );
				var objContents = pax.util.toString( pax.cache.get('myObjKey') );
			:]
			The fruit object contains the following: [:= objContents :]
			[:
				pax.cache.remove( 'myObjKey' );
				var objContents2 = pax.util.toString( pax.cache.get('myObjKey') );
			:]
			<hr>
			After removal, the fruit object contains the following: [:= objContents2 :]
		(end)
		
		This will remove the object cached under 'myObjKey', or run the error function otherwise.
*/
pax.cache.remove = function( key ) {
	for( var obj in pax.cache.obj ) {
		if( obj == key ) {
			delete pax.cache.obj[key];
			return true;
		}
	}
	return false;
};


/* ----------------------------------------------------------------------------

	pax.cache.template.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.cache.template
		This class caches templates in the DOM, and provides convenient methods to manage them.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.cache = pax.cache || {};
pax.cache.template = pax.cache.template || {};
pax.cache.template.target = document.getElementById('output');		// Default target container. Ie: you need a container with the id of output.


/*	Method: pax.cache.template.loadTemplate
	Retreives a template from a given URL, and caches it. When it is done, it runs the specified call back method
	
	Parameters:
		templateUrl - The URL to load a template from
		postObj - The POST data object, if null or false, we use GET instead. Use {} for no parameters in the POST
		callBack - A method to run when the template has been cached. Hint: the callBack is passed back the URL, which is also the cache key.

	Example:
		(start code)
			<div id="pax.cache.template.loadTemplate.example1"></div>
			[:.
				function hasLoaded( key ) {
					var myData = [
						{	'firstName': 'Bill', 'lastName': 'Washman'	},
						{	'firstName': 'Julia', 'lastName': 'Copalotalus'	},
						{	'firstName': 'Isabella', 'lastName': 'Carnivoree'	}
					];
						
					pax.template.render( pax.cache.get( key ), myData, pax.$('pax.cache.template.loadTemplate.example1') );
					pax.cache.remove( key );
				}
				pax.cache.template.loadTemplate( '/pax/pax.cache.template.loadTemplate.example1.tpl', false, hasLoaded );
			:]
		(end)

		Loads the template into the cache, and then renders it from there, using <pax.template.render>.

	Example:
		(start code)
			<div id="pax.cache.template.loadTemplate.example2"></div>
			[:.
				function hasLoaded( key ) {
					var myData = [
						{	'firstName': 'Bill', 'lastName': 'Washman'	},
						{	'firstName': 'Julia', 'lastName': 'Copalotalus'	},
						{	'firstName': 'Isabella', 'lastName': 'Carnivoree'	}
					];
						
					pax.template.render( pax.cache.get( key ), myData, pax.$('pax.cache.template.loadTemplate.example2') );
					pax.cache.remove( key );
				}
				pax.cache.template.loadTemplate( '/pax/pax.cache.template.loadTemplate.example1.tpl', {}, hasLoaded );
			:]
		(end)

		Loads the template into the cache, (via post with no parameters), and then renders it from there, using <pax.template.render>.
*/
pax.cache.template.loadTemplate = function( templateUrl, postObj, callBack ) {
	if( ! pax.cache.isCached( templateUrl ) ) {
		var callBackFunc = function( xml, txt, url ) {
			pax.cache.set( templateUrl, txt );
			callBack( templateUrl );
		};
		if( postObj )pax.post( templateUrl, postObj, callBackFunc, 'Load POST Template: [' + templateUrl + ']', 'StatusBox' );
		else pax.get( templateUrl, callBackFunc, 'Load GET Template: [' + templateUrl + ']', 'StatusBox' );
	}
	else callBack( templateUrl );
};


/*	Method: pax.cache.template.loadData
	Retreives data from a given URL. When it is done, it runs the specified call back method
	
	Parameters:
		dataUrl - The URL to load data from
		postObj - The POST data object, if null or false, we use GET instead. Use {} for no parameters in the POST
		callBack - A method to run when the template has been cached. Hint: the callBack is passed back the URL, which is also the cache key.

	Example:
		(start code)
			The data loaded is:
			<div id="pax.cache.template.loadData.example1"></div>
			[:.
				function hasLoaded( txt ) {
					pax.$('pax.cache.template.loadData.example1').innerHTML = txt;
				}
				pax.cache.template.loadData( '/pax/pax.cache.template.loadTemplate.example1.data', false, hasLoaded );
			:]
		(end)

		Loads data. Note that data is never cached.
*/
pax.cache.template.loadData = function( dataUrl, postObj, callBack ) {
	var callBackFunc = function( xml, txt, url ) {
		callBack( txt );
	};

	if( postObj )pax.post( dataUrl, postObj, callBackFunc, 'Load POST data: [' + dataUrl + ']', 'StatusBox' );
	else pax.get( dataUrl, callBackFunc, 'Load GET data: [' + dataUrl + ']', 'StatusBox' );
};


/*	Method: pax.cache.template.loadAndRender
	Retreives a template from a given URL, caches it, and then renders it.
	
	Parameters:
		target - the DOM element to render the template and data into.
		templateUrl - The URL to load a template via a GET
		dataUrl - The URL to load data from via a GET
		dataPostObj - If we want to use POST to retreive the data, specify a dictionary of post key -> value pairs here. If you want to use GET, omit this parameter.

	Example:
		(start code)
			<div id="pax.cache.template.loadAndRender.example1"></div>
			[:.
				pax.cache.template.loadAndRender( pax.$('pax.cache.template.loadAndRender.example1'), '/pax/pax.cache.template.loadTemplate.example1.tpl', '/pax/pax.cache.template.loadTemplate.example1.data' );
			:]
		(end)

		Loads the template, then the data, and renders it into the target.
*/
pax.cache.template.loadAndRender = function( target, templateUrl, dataUrl, dataPostObj ) {
	var callBackRenderFunc = function( json ) {
		var data = pax.unJSON( json );
		pax.template.render( pax.cache.get( templateUrl ), data, target );
	};
	
	var dataRenderFunc = function( data ) {
		pax.template.render( pax.cache.get( templateUrl ), data, target );
	};
	
	var callBackDataLoadFunc = function( key ) {
		if( typeof(dataUrl) == 'object' ) {
			dataRenderFunc( dataUrl );
		} else {
			if( dataUrl )pax.cache.template.loadData( dataUrl, dataPostObj, callBackRenderFunc );
			else callBackRenderFunc( "{}" );
		}
	};
	
	pax.cache.template.loadTemplate( templateUrl, false, callBackDataLoadFunc );
};
/* ----------------------------------------------------------------------------

	pax.form.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.form
		This PAX library contains methods to manipulate and access forms.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.form = pax.form || {};

/*	Property: pax.form.ajaxSubmitFieldName
	Field name that is added to any form that PAX submits via ajax
*/
pax.form.ajaxSubmitFieldName = 'paxAjaxSubmit';

/*	Property: pax.form.submitViaIframeFieldName
	Field name that is added to any form that PAX submits using the "iframe trick"
*/
pax.form.submitViaIframeFieldName = 'paxIframeSubmit';

/*	Method: pax.form.useAjaxSubmit
	This makes a form submit via ajax, or if there are file uploads, via a hidden iframe

	Parameters:
		form - an ID or DOM element for the form we are submitting
		args - An object that contains the following:
			* callBack - Optional function to run when the submit is complete
			* url - Optionally specify a different URL to submit the form to
			* submitButton - the forms associated submit button, note that this must be bound to use ajax
*/
pax.form.useAjaxSubmit = function( form, args ) {
	args = args || {};

	//	Disable normal form submit
	pax.event.bind( form, 'submit', function(e) {
		if( e.preventDefault )e.preventDefault();	//	Most browsers
		else e.returnValue = false;					//	IE
		return false;								//	Fallback?
	} );
	
	//	Bind submit button to use ajax
	pax.event.bind( args.submitButton, 'click', function( e ) {

		//	Prevent the form from submitting the "normal" way - we must always return false as well
		if( e.preventDefault )e.preventDefault();	//	Most browsers
		else e.returnValue = false;					//	IE
			
		//	If the form has validations, (and is not valid), return false.
		if( ! pax.validate.isFormValid( form.name ) )return false;
	
		var allFields = pax.validate.allFields( form.name, true );
		var values = {};
		var hasFileUploads = false;
		for( var i = 0; i < allFields.length; i++ ) {
			var field = allFields[i];
			//	Check to see if we have file field(s), in which case we must 
			//	submit via an iframe, instead of ajax
			if( field.type == "file" ) {
				hasFileUploads = true;
				break;
			} else {
				values[field.name] = pax.form.getFieldValue( field );
			}
		}
		
		if( hasFileUploads ) {
			//	If we have file field(s), we submit via a hidden iframe
			var iFrameArgs = args;
			iFrameArgs['form'] = form;
			var url = args.url || null;
			pax.form.submitViaIframe( iFrameArgs, url );
		} else {
			//	Tell the server we submitted via ajax.
			values[pax.form.ajaxSubmitFieldName] = true;
			var targetUrl = ( args.url )? args.url: form.action;
			pax.post( targetUrl, pax.postString( values ), function( xml, txt, url ) {
				if( args.callBack )args.callBack( xml, txt, url );
			} );
		}
		
		//	Always return false, so we don't submit the form
		return false;
	} );
};


/*	Method: pax.form.submitViaIframe
	Submits a form, using a hidden iframe; this allows uploading of files, where as ajax submission doesn't

	Parameters:
		args - An object that contain the following:
			* form - an ID or DOM element for the form we are submitting
			* callBack - Optional function to run when the submit is complete
			* preserveEncoding - Optional boolean to specify that we don't want the encoding changed to 'multipart/form-data'
		url - Optionally specify a different URL to submit the form to
		
*/
pax.form.submitViaIframe = function( args, url ) {
	args = args || {};

	var id = pax.getNextId();
	
	//	Create a hidden iFrame
	var frame = document.createElement('iframe');
	frame.id = frame.name = id;
	frame.style.display = 'none';
	document.body.appendChild( frame );
	if( pax.isIe )document.frames[id].name = id;
	
	//	Get the form, and set the target to the iFrame id, plus correct encoding for file uploads
	var form = pax.$( args.form );
	if( url )form.action = url;
	form.target = id;
	form.method = 'POST';
	if( ! args.preserveEncoding )form.enctype = form.encoding = 'multipart/form-data';

	//	Add a parameter that shows we are submitting via "ajax"
	var ajaxSubmitField = pax.util.genElement( 'input', { id: pax.form.ajaxSubmitFieldName } );
	ajaxSubmitField.type = 'hidden';
	ajaxSubmitField.value = true;
	form.appendChild( ajaxSubmitField );
	
	//	Add a parameter that shows we are submitting using an iframe
	var iframeSubmitField = pax.util.genElement( 'input', { id: pax.form.submitViaIframeFieldName } );
	iframeSubmitField.type = 'hidden';
	iframeSubmitField.value = true;
	form.appendChild( iframeSubmitField );
	
	//	Bind a function to handle the frame on load.
	pax.event.bind( frame, 'load', function() {
		var result = '';
		var frameDocument = ( pax.isIe )? frame.contentWindow.document: ( frame.contentDocument || window.frames[id].document );
		if( frameDocument && frameDocument.body )result = frameDocument.body.innerHTML;
		
		//	Unbind the frame load event, and remove the frame
		setTimeout( function() {
			pax.event.unbind( frame, 'load' );
			pax.hideStatus();
			pax.util.removeElement( frame ); 
		}, 200 );
		
		//	Run the callback
		//	Note: 	We cannot support "real" JSON responses in the iFrame, (ie: "Content-type: application/x-json"),
		//			as this causes the browser to think we're downloading a file.
		if( args.callBack )args.callBack( null, result, form.action );
	} );

	pax.showStatus();
	form.submit();
};

//	Adds option(s) to target select
pax.form.addOption = function( options, target ) {
	//	Support multiple options
	if( pax.util.getType( options ) != 'array' )options = [options];
	for( var i = 0; i < options.length; i++ ) {
		//	Create a new option here
		target.options[target.options.length] = new Option(options[i].text, options[i].value, false, false);
	}
};

//	Remove an option
pax.form.removeOption = function( options, target ) {
	//	Support multiple options
	if( pax.util.getType( options ) != 'array' )options = [options];
	for (var i = 0; i < options.length; i++) {
		for (var t = 0; t < target.options.length; t++) {
			if( target.options[t] == options[i] )target.options[t] = null;
		}
	}
};


/*	Method: getSelectedOptions
	Gets the selected option(s) from a select box
	
	Parameters:
		selectBox - The select box we're getting the option(s) from

	Example:
		(start code)

			<select name='fruit' id='fruit'>
				<option value="47">Apples</option>
				<option value="49">Oranges</option>
				<option value="67">Pears</option>
			</select>
			[:.
				//pax.form.setSelected( pax.$('fruit'), 'Oranges' );
			:]
		(end)
		This would 

*/
pax.form.getSelectedOptions = function( selectBox ) {
	var result = [];
	for( var i = 0; i < selectBox.options.length; i++ ) {
		if( selectBox.options[i].selected )result.push( selectBox.options[i] );
	}
	return result;
};

//	Returns the options from a select box
pax.form.getOptions = function( selectBox ) {
	var result = [];
	for( var i = 0; i < selectBox.options.length; i++ ) {
		result.push( selectBox.options[i] );
	}
	return result;
};


/*	Method: setSelected
	Sets the selected index for a select box, by text instead of value
	
	Parameters:
		selectBox - The select box we're setting the value for
		text - which value to set the select box index for

	Example:
		(start code)
			The select box's selected index should be 49, which has a value of 'Oranges': 
			<select name='fruit' id='fruit'>
				<option value="47">Apples</option>
				<option value="49">Oranges</option>
				<option value="67">Pears</option>
			</select>
			[:.
				pax.form.setSelected( pax.$('fruit'), 'Oranges' );
			:]
		(end)
		This would set the select box's selected index value to 49, which has text 'Oranges'

*/
pax.form.setSelected = function( selectBox, text ) {
	if (text == null && selectBox.multiple) {
		for (var i = 0; i < selectBox.options.length; i++) {
			selectBox.options[i].selected = 'selected';
		}
	} else {
		for (var i = 0; i < selectBox.options.length; i++) {
			if (selectBox.multiple) {
				if (text == selectBox.options[i].text)selectBox.options[i].selected = 'selected';
			} else {
				if (text == selectBox.options[i].text) selectBox.selectedIndex = i;
			}
		}
	}
};


/*	Method: setChecked
	Sets the selected index for a check box, by value
	
	Parameters:
		checkBox - The check box we're setting the value for
		value - which value to set the check box index for

	Example:
		Assuming you have a check box set like this:
		(start code)
			<form name='shoppinglist' id='shoppinglist'>
				<input type='checkbox' name='fruit' value='Apple'>Apple
				<input type='checkbox' name='fruit' value='Orange'>Orange
				<input type='checkbox' name='fruit' value='Pear'>Pear
			</form>
			[:.
				pax.form.setChecked( document.forms.shoppinglist.fruit, 'Orange' );
			:]
		(end)
		This would set the check box's selected index to 2, which has a value of 'Orange'

*/
pax.form.setChecked = function( checkBox, value ) {
	for( var i = 0; i < checkBox.length; i++ ) {
		if( value == checkBox[i].value )checkBox[i].checked = true;
	}
};


//	Retreives the value of a field, regardless of type.
pax.form.getFieldValue = function( field ) {

	//	For radio sets, we may get a list, we need to pick the checked one.
	if( pax.util.getType( field ) == 'object' && !field.nodeName ) {
		for( var i = 0; i < field.length; i++ ) {
			console.log( field[i] );
			if( field[i].checked )return field[i].value;
		}
		return null;
	}

	//	Check field type
	var ft = ( typeof field.nodeName != 'undefined' )? field.nodeName.toLowerCase(): '';
	if( ft == 'input' || ft == 'textarea' )return field.value;
	//	TODO: allow multiple selected options!
	if( ft == 'select' )return field.options[field.selectedIndex].value;
};

//	Select a certain part of the text in a specific text field.
pax.form.selectRange = function( field, start, length ) {
	if( pax.isIe ) {
		var myRange = field.createTextRange();
		myRange.moveStart( "character", start );
		myRange.moveEnd( "character", length );
		myRange.select();
	} else {
		field.setSelectionRange( start, length );
	}
};


/*	Method: checkboxDependents
	This function relates check boxes to eachother, and warns or forces checking, when a Dependency (parent) is being unchecked, and can optionally uncheck Dependents (child) if a parent is unchecked.
	
	Parameters:
		formName - Name of the form the fields exist in
		checkBoxMap - Dictionary that contains the check boxes
		args - An optional object that optionally contain the following:
			* autoCheck - weather to automatically check dependencies (parent) if a dependent (child) is checked. Default is true
			* autoUncheck - weather to automatically uncheck dependents (child) if a dependent (parent) is unchecked. Default is false
			* showHint - weather to show a hint next to a dependent (parent), as it is attempted to be unchecked. Default is true
			* callBack - function to run when a dependency (parent) checkbox is unchecked, parameter is the field.
			
	Note:
		After instanciation, each parent field contains two lists as a properties:
		* childDepend - A list of checkboxes that depend on this field
		* parentDepend - A list of checkboxes that this field depends on
		Also, the reason we have autoCheck and autoUncheck is that you may want to just show a warning, in which case you should disable both. 
		Enabling both autoCheck and autoUncheck is not supported.
		
	TODO: 
		* Currently you need both name and id to access the fields, it shouldn't be like that.
		
	Example:
		(start code)
			<form name='pax.form.checkboxDependents.example3'>
				<ul>
					<li><input type='checkbox' name='box1' id='box1'>box1</li>
					<li><input type='checkbox' name='box2' id='box2'>box2</li>
					<li><input type='checkbox' name='box3' id='box3'>box3</li>
				</ul>
			</form>
			[:.
				var checkBoxMap = {
					'box1':	[],
					'box2':	[ 'box1' ],
					'box3':	[ 'box2' ]
				};
				pax.form.checkboxDependents( 'pax.form.checkboxDependents.example3', checkBoxMap );
			:]
		(end)
		This example uses the default behaviour, it auto checks parent checkboxes, and shows a warning.
		
	Example:
		(start code)
			<form name='pax.form.checkboxDependents.example2'>
				<ul>
					<li><input type='checkbox' name='box1' id='box1'>box1</li>
					<li><input type='checkbox' name='box2' id='box2'>box2</li>
					<li><input type='checkbox' name='box3' id='box3'>box3</li>
				</ul>
			</form>
			[:.
				var checkBoxMap = {
					'box1':	[],
					'box2':	[ 'box1' ],
					'box3':	[ 'box2' ]
				};
				pax.form.checkboxDependents( 'pax.form.checkboxDependents.example2', checkBoxMap, { autoCheck: false, autoUncheck: true } );
			:]
		(end)
		This example auto unchecks the Dependenct (child) boxes. Ie if you uncheck box1, and box2 and box3 are dependents (child), they will be unchecked.
		
	Example:
		(start code)
			<form name='pax.form.checkboxDependents.example1'>
				<ul>
					<li><input type='checkbox' name='pax.js' id='pax.js' >pax.js</li>
					<li><input type='checkbox' name='pax.load.js' id='pax.load.js' >pax.load.js</li>
					<li><input type='checkbox' name='pax.util.js' id='pax.util.js' >pax.util.js</li>
					<li><input type='checkbox' name='pax.cache.js' id='pax.cache.js' >pax.cache.js</li>
					<li><input type='checkbox' name='pax.cache.template.js' id='pax.cache.template.js' >pax.cache.template.js</li>
					<li><input type='checkbox' name='pax.form.js' id='pax.form.js' >pax.form.js</li>
					<li><input type='checkbox' name='pax.fx.js' id='pax.fx.js' >pax.fx.js</li>
					<li><input type='checkbox' name='pax.validate.js' id='pax.validate.js' >pax.validate.js</li>
					<li><input type='checkbox' name='pax.template.js' id='pax.template.js' >pax.template.js</li>
					<li><input type='checkbox' name='pax.template.modifier.js' id='pax.template.modifier.js' >pax.template.modifier.js</li>
					<li><input type='checkbox' name='pax.widget.js' id='pax.widget.js' >pax.widget.js</li>
					<li><input type='checkbox' name='pax.widget.inputbox.js' id='pax.widget.inputbox.js' >pax.widget.inputbox.js</li>
					<li><input type='checkbox' name='pax.window.js' id='pax.window.js' >pax.window.js</li>
				</ul>
			</form>
			[:.
				var checkBoxMap = {
					'pax.cache.template.js': 	[ 'pax.js', 'pax.cache.js', 'pax.template.js' ],
					'pax.template.js': 			[ 'pax.js', 'pax.cache.js', 'pax.cache.template.js', 'pax.template.modifier.js' ],
					'pax.template.modifier.js': [ 'pax.template.js' ],
					'pax.widget.js': 			[ 'pax.js', 'pax.util.js' ],
					'pax.validate.js': 			[ 'pax.js', 'pax.util.js' ],
					'pax.fx.js': 				[ 'pax.js', 'pax.util.js' ]
				};
				pax.form.checkboxDependents( 'pax.form.checkboxDependents.example1', checkBoxMap );
			:]
		(end)
		This example auto unchecks the dependent (child) boxes. Ie if you uncheck a box that has Dependents, it will uncheck the Dependents. The example has circular references, ie one or more boxes that depend on eachother.
		
	Example:
		(start code)
			<form name='pax.form.checkboxDependents.example4'>
				<ul>
					<li><input type='checkbox' name='box1' id='box1'>box1</li>
					<li><input type='checkbox' name='box2' id='box2'>box2</li>
					<li><input type='checkbox' name='box3' id='box3'>box3</li>
				</ul>
			</form>
			[:.
				var callBack = function( field ) {
					var message = '';
					for( var i = 0; i < field.parentDepend.length; i++ ) {
						message += 'Dependent: ' + field.parentDepend[i];
					}
					message += '<b'+'r>' + field.childDepend;
					pax.box.showOnRight( 'fieldMessage', message, 'hintMessage', field, 5 );
				};
				
				var checkBoxMap = {
					'box1':	[],
					'box2':	[ 'box1' ],
					'box3':	[ 'box2' ]
				};
				pax.form.checkboxDependents( 'pax.form.checkboxDependents.example4', checkBoxMap, { autoUncheck: true, showHint: false, callBack: callBack } );
			:]
		(end)
		This example auto unchecks the dependent (child) boxes. Ie if you uncheck a box that has Dependents, it will uncheck the Dependents. The example has also tuned off the hint box.
		
*/
pax.form.checkboxDependents = function( formName, checkBoxMap, args ) {
	args = ( typeof( args ) != 'undefined' )? args: {};
	var autoCheck = ( typeof( args.autoCheck ) != 'undefined' )?args.autoCheck : true;
	var autoUncheck = ( typeof( args.autoUncheck ) != 'undefined' )?args.autoUncheck : false;
	var showHint = ( typeof( args.showHint ) != 'undefined' )?args.showHint : true;
	var callBack = ( typeof( args.callBack ) != 'undefined' )?args.callBack : null;
	
	//	"Normalise" the checkBoxMap. Ie: resolve cyclic Dependencies by consolidating each cyclic Dependency into one list per entry
	for( var c in checkBoxMap ) {
		var newDeps = [];
		for( var dep in checkBoxMap[c] ) {
			if( pax.util.hasKey( checkBoxMap[c][dep], checkBoxMap ) ) {
				newDeps = pax.util.joinObj( checkBoxMap[c], checkBoxMap[checkBoxMap[c][dep]], false );
			} else {
				newDeps[newDeps.length] = checkBoxMap[c][dep];
			}
		}
		if( newDeps != [] )checkBoxMap[c] = newDeps;
	}
	
	//	Bind the click events
	for( var c in checkBoxMap ) {
		pax.$(c).childDepend = checkBoxMap[c];
		
		//	Dependency (parent) checkboxes
		pax.event.bind( pax.$(c), 'click', function() {
			if( this.checked ) {
				var myFields = pax.validate.allFields( formName, true );
				for( var f = 0; f < myFields.length; f++ ) {
					field = myFields[f];
					if( field.type == 'checkbox' ) {
						var checkIt = false;
						for( var i = 0; i < this.childDepend.length; i++ ) {
							if( field.name == this.childDepend[i] )checkIt = true;
						}
						if( checkIt ) {
							//	Automatically check parent dependent if a child is checked.
							if( autoCheck )field.checked = true;
						}
					}
				}
			}
		} );
		

		//	Warning function to check each child from the parent.
		var warningFunc = function() {
			var canUncheck = true;
			var deps = "";
			for( var dep in this.parentDepend ) {
				var myDep = this.parentDepend[dep];
				if( pax.$(myDep).checked && pax.$(myDep) != this ) {
					canUncheck = false;
					deps += myDep + " ";
					if( ! this.checked ) {
						if( autoUncheck )pax.$(myDep).checked = false;
					}
				}
			}
			if( !canUncheck ) {
				if( !this.checked ) {
					//	Forces parent to stay checked
					if( autoCheck )this.checked = true;
					if( showHint )pax.box.showOnRight( 'Dependents', "This field should not be unchecked, as it is required by: " + deps, 'hintMessage', this, 5 );
					if( callBack )callBack( this );
				}
			}
		};

		//	Dependents (child) boxes
		for( var d in checkBoxMap[c] ) {
			var myBox = pax.$( checkBoxMap[c][d] );
			if( typeof( myBox.parentDepend ) == 'undefined' )myBox.parentDepend = [c];
			else if( ! pax.util.arrayHasValue( c, myBox.parentDepend ) )myBox.parentDepend.push( c );

			//	Bind warning function to each parent, to check each child
			pax.event.bind( myBox, 'click', warningFunc );
		}
	}
};

/* ----------------------------------------------------------------------------

	pax.fx.js Copyright (C) 2005, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.fx
		This class allows various effects and graphical attribues of CSS classes to be manipulated, and multiple effects to be applied to one or more DOM elements;
		it utalises tweening, which allows good control over animations and effects.
		
	Note: You should use <pax.fx.init> for all FX functions!!!
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
*/

var pax = pax || {};

pax.fx = pax.fx || {};			// Fx methods
pax.fx.dict = {};				// Dictionary for tracking effect items
pax.fx.effectInterval = null;	// Interval for running effects

pax.css = pax.css || {};		// CSS methods

pax.fx.tween = {};				// FX Tweening lib
pax.fx.tween.util = {};			// FX Tweening utility lib

pax.fx.dragDict = {};			// Dictionary for tracking dragable elements
pax.fx.dropDict = {};			// Dictionary for tracking drop target elements


/*	Method: pax.fx.init
	Initialises an effect, and adds it to the efefcts dictionary.
	
	Parameters:
		target - Element ID of the DOM node we're to apply the effect to
		effect - which effect to use
		value - A value object the effect will use
		duration - Duration of the effect in milliseconds
		direction - Direction of effect, pass false to make it go backwards
		callBack - An optional callback method for when the effect has finished.
	
	Note:
		* Effects can be chained, by using the callBack method, or better yet, the pax.fx.chain method
		* Simultaneous effects can be run
		* See the individual effects for examples
*/
pax.fx.init = function( target, effect, value, duration, direction, callBack ) {
	var startTime = new Date().getTime();

	/*	
		WIP: for 'reveal' effect, we MUST have style.overflow = 'hidden', as such, we should be 
		able to capture style info first, set the overflow, and then reset the style, once the animation is finished

	*/
	
	value.startTime = startTime;
	value.duration = ( typeof( duration ) != 'undefined' ) ? duration : 1500;
	value.direction = ( typeof( direction ) != 'undefined' ) ? direction : true;
	value.callBack = callBack;

	return pax.fx.register( target, effect, value );
};


/*	Method: pax.fx.chain
	This method runs any number of effects, one after the other. Consecutive effects inherit prior effects parameters
	
	Parameters:
		fxList - List of effect init args to use

	Example:
		(start code)
			<input type="button" id="moveBox" value="Animate!">

			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; background: #5555ff; 
				color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x + 300, y:startPos.y + 150};
				var move = function() { 
					pax.fx.chain( [
						{	target: 'pax.fx.move.example1', 
							effect: 'move', 
							value: {startPos: startPos, endPos: endPos }, 
							duration: 2000,
							direction: true
						},
						{	effect: 'move', 
							value: {startPos: startPos, endPos: endPos },
							direction: false
						},
						{	effect: 'move', 
							value: {startPos: startPos, endPos: {x:200} },
							direction: true,
							duration: 500
						},
						{	effect: 'colourFade', 
							value: {startColour: '0000ff', endColour: 'ff0000'}
						},
						{	effect: 'opacityFade', 
							value: {startOpacity: 0, endOpacity: 100}, 
							duration: 1000,
							direction: false
						},
						{	effect: 'opacityFade', 
							value: {startOpacity: 0, endOpacity: 100}, 
							duration: 1000,
							direction: true
						},
						{	effect: 'move', 
							value: {startPos: {x:200}, endPos: startPos }, 
							duration: 2000,
							direction: true
						}
					] );
				};
				pax.$('moveBox').onclick = function() { move(); };
			:]
		(end)
		
*/
pax.fx.chain = function( fxList ) {
	var target, effect, value, duration, direction = null;

	//	Set initial effects parameters, so they can be inherited by each chained effect
	var fect = fxList[0];
	target = 	(typeof(fect.target) != 'undefined' )? 	fect.target: target;
	effect = 	(typeof(fect.effect) != 'undefined' )? 	fect.effect: effect;
	value = 	(typeof(fect.value) != 'undefined' )? 	fect.value: value;
	duration = 	(typeof(fect.duration) != 'undefined' )? 	fect.duration: duration;
	direction = (typeof(fect.direction) != 'undefined' )? fect.direction: direction;

	var fxFuncs = [];
	var callBackFunc = null;

	//	Loop backwards, assigning callback methods as you go.
	for( var i = fxList.length - 1; i > -1; i-- ) {
		var fect = fxList[i];
		//	Ok, we're inheriting in reverse order (duh), maybe create an array instead?
		/*
			. The array must inherit "forward"
			. We must inherit ALL values.
			. the inherited values should be used as (fxList.length - i)
		*/
		target = (typeof(fect.target) != 'undefined' )? fect.target: target;
		effect = (typeof(fect.effect) != 'undefined' )? fect.effect: effect;
		value = (typeof(fect.value) != 'undefined' )? fect.value: value;
		duration = (typeof(fect.duration) != 'undefined' )? fect.duration: duration;
		direction = (typeof(fect.direction) != 'undefined' )? fect.direction: direction;

		//	This function creates a call back function with the correct scope for the arguments
		var createCallBack = function( callBack ) {
			var ftarget = target;
			var feffect = effect;
			var fvalue = value;
			var fduration = duration;
			var fdirection = direction;
			
			if( typeof(callBack) == 'function' ) {
				callBackFunc = function() {
					pax.fx.init( ftarget, feffect, fvalue, fduration, fdirection, callBack );
				};
			} else {
				callBackFunc = function() {
					pax.fx.init( ftarget, feffect, fvalue, fduration, fdirection );
				};
			}
		};


		if( i == 0 ) {
			if( callBackFunc ) {
				pax.fx.init( target, effect, value, duration, direction, callBackFunc );
			} else {
				pax.fx.init( target, effect, value, duration, direction );
			}
		} else {
			if( callBackFunc ) {
				createCallBack( callBackFunc );
				/*
				var callBackFunc = function() {
					pax.fx.init( target, effect, value, duration, direction, callBackFunc );
				};
				*/
			} else {
				createCallBack();
			}
		}
	}
};

/*	Method: pax.fx.yoyo
	Initialises an effect, and makes it continiously go forwards and backwards.
	
	Parameters:
		target - element to perform the effect on
		effect - which effect to use
		value - object to pass to the effect
		duration - how long the effect goes for in milliseconds
		direction - which way the effect should start at, default = true = forwards, false = backwards.
		
	Returns:
		A function to stop the yoyo action by

	Example:
		(start code)
			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; left: 4px; top: 60px;'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x, y:startPos.y + 250};
				var finishYoyo = pax.fx.yoyo( 'pax.fx.move.example1', 'move', {startPos: endPos, endPos: startPos }, 500 );
				exampleCleanup = function() {
					finishYoyo();
				};
			:]
		(end)
		You will note that the yoyo function returns an function to stop the action by

	Example:
		(start code)
			<input type="button" id="yoyoOpacity" value="opacityFade yoyo">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );" value="opacityFade+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000, false );" value="opacityFade-">

			<div id='pax.fx.opacityFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				function yoyoOpacity() {
					var finishYoyo = pax.fx.yoyo( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );
					exampleCleanup = function() {
						finishYoyo();
					};
				}
				pax.$('yoyoOpacity').onclick = function() { yoyoOpacity(); };
			:]
		(end)
		
	Example:
		(start code)
			<div id='pax.fx.move.example3' style='position:absolute; overflow:hidden; left: 4px; top: 60px;'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example3'), false );
				var endPos = {x:startPos.x, y:startPos.y + 250};
				var finishYoyo = pax.fx.yoyo( 'pax.fx.move.example3', 'move', {startPos: endPos, endPos: startPos }, 500 );
				var finishYoyo2 = pax.fx.yoyo( 'pax.fx.move.example3', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );
				exampleCleanup = function() {
					finishYoyo();
					finishYoyo2();
				};
			:]
		(end)
		You will note that the yoyo function returns an function to stop the action by

*/
pax.fx.yoyo = function( target, effect, value, duration, direction ) {
	pax.fx.init( target, effect, value, duration, !direction, function() {
		pax.fx.yoyo( target, effect, value, duration, !direction );
	} );

	//	We return a function to stop the effect with.
	return ( function() {
		if( typeof( pax.fx.dict[target][effect] ) != 'undefined' )pax.fx.dict[target][effect].stopYoyo = true;		
		pax.fx.finish( target, effect, pax.fx[effect].cleanup );
	} );

};

/*	Private Method: pax.fx.register
	Registers an ongoing effect with the fx manager
	
	Parameters:
		target - The unique ID of the DOM element the effect is being applied to
		effect - Which effect to run
		value - A value object for this effect, includes any variables
		
	Note:
		This method starts the effect interval, if it isn't running.
*/
pax.fx.register = function( target, effect, value ) {
	if( pax.util.hasKey( target, pax.fx.dict ) ) {
		//	Run finish, if an effect of this type is running, on the given target
		if( pax.util.hasKey( effect, pax.fx.dict[target] ) ) {
			pax.fx.finish( target, effect, pax.fx[effect].cleanup );
		}
	} else {
		pax.fx.dict[target] = {};
	}
	
	// Setup the timeout to stop the effect.
	var timeout = window.setTimeout( function() { pax.fx.finish( target, effect, pax.fx[effect].cleanup ); }, value.duration );
	value.timeout = timeout;
	
	pax.fx.dict[target][effect] = value;
	
	// Setup an interval, if we don't already have one. 16ms is approx 60fps
	if( ! pax.fx.effectInterval )pax.fx.effectInterval = setInterval( function() { pax.fx.animate(); }, 16 );
	
	return pax.fx.dict[target][effect];
};


/*	Method: pax.fx.cancelAll
	Cancels all ongoing effect on the given targets id
	
	Parameters:
		id - The unique ID of the DOM element the effect(s) are being applied to
		
*/
pax.fx.cancelAll = function( id ) {
	if( pax.fx.dict[id] ) {
		for( var effect in pax.fx.dict[id] ) {
			pax.fx.finish( id, effect, pax.fx[effect].cleanup );
		}
	}
};


/*
	Private method: pax.fx.animate - Iterates through the effects dict, and runs each effect.
*/
pax.fx.animate = function() {
	for( var target in pax.fx.dict ) {
		for( var effect in pax.fx.dict[target] ) {
			if( pax.util.hasKey( effect, pax.fx ) ) {
				
				var ele = document.getElementById( target );
				var val = pax.fx.dict[target][effect];
				
				// Calculate as a percentage where we're up to, using start time and duration.
				var pct = ( ( ( (new Date()).getTime() ) - val.startTime ) / val.duration ) * 100;
				pct = ( pct > 100 )? 100 : pct;
				pct = ( pct < 0 )? 0 : pct;
				
				pax.fx[effect]( val, ele, pct );

			} else {
				//	We're trying to run an undefined effect!
				console.log( 'pax.fx: effect [' + effect + '] not found' );
			}
		}
	}
};


/*	Private Method: pax.fx.finish
	Finishes an effect, by running cleanup, and optional callback.
	
*/
pax.fx.finish = function ( target, effect, cleanup, callBack ) {
    var val = pax.fx.dict[target][effect];
    var ele = document.getElementById( target );
   
	//    Run the effects cleanup method
	if( cleanup )cleanup( val, ele );
   
    if( pax.util.hasKey( target, pax.fx.dict ) ) {
        if( val )if( typeof( val.timeout ) != 'undefined' )clearTimeout( val.timeout );
        if( pax.util.hasKey( effect, pax.fx.dict[target] ) )delete pax.fx.dict[target][effect];
    }
   
    //    If the dict is empty, clear the interval
    if( pax.fx.dict == {} ) {
        clearInterval( pax.fx.effectInterval );
        pax.fx.effectInterval = null;
    }

    if( val ) {
        if( !val.stopYoyo ) {
            if( typeof( val.callBack ) == 'function' )val.callBack();    //    Run the initial callback
        }
    }
   
    if( typeof( callBack ) == 'function' )callBack();            //    Run the specified callback   
};


/*	Method: pax.css.opacity
	Sets the opacity of an element
	
	Parameters:
		element - DOM pointer to the element we want to set the opacity of
		percent - percentage of opacity
		
	Example:
		(start code)
			<div id='pax.css.opacity.example1' style='background:#2233DD; border: 2px solid red'>This is a test</div>
			[:.
				pax.css.opacity( pax.$('pax.css.opacity.example1'), 50 );
			:]
		(end)
		Sets the opacity of an element.
*/
pax.css.opacity = function( element, percent ) {
	if( ! element )return;
	if( ! element.style ) {
		element.setAttribute( 'style', {} );
		element.style = {};
	}
	element.style.filter = "alpha(opacity=" + percent + ")";
	element.style.MozOpacity = percent / 100;
	element.style.opacity = percent / 100;
	element.style.KhtmlOpacity = percent / 100;
	
	//	Workaround for IE - we MUST have layout for this to work
	if( pax.isIe && ! element.currentStyle.hasLayout )element.style.zoom = 1;
};



/*	Private Method: pax.fx.tween.sinePercent
	Returns an approximate sine percent value of pi / 180
	
	Parameters:
		percent - percentage that we want the sine function to use.
*/
pax.fx.tween.sinePercent = function( percent ) {
	return Math.sin( percent * 0.016 );	
};

/*	Private Method: tween.position
	Function to calculate x, y, width and height at a certain percentage.
	
	Parameters:
		start - starting position object, eg {x:100, y:50}
		end - ending position object, eg {x:200, y:150}
		percent - how far we have moved percentage-wise
		tweenMethod - method to use for tweening.
*/
pax.fx.tween.position = function( start, end, percent, tweenMethod ) {
	var result = {};
	if( typeof( tweenMethod ) == 'function' ) {
		result.x = Math.floor( start.x + (end.x - start.x) * tweenMethod( (percent) ) + 0.5);
		result.y = Math.floor( start.y + (end.y - start.y) * tweenMethod( (percent) ) + 0.5);
		result.width = Math.floor( start.width + (end.width - start.width) * tweenMethod( (percent) ) + 0.5);
		result.height = Math.floor( start.height + (end.height - start.height) * tweenMethod( (percent) ) + 0.5);
	} else {
		result.x = Math.floor( start.x + (end.x - start.x) * (percent / 100) + 0.5);
		result.y = Math.floor( start.y + (end.y - start.y) * (percent / 100) + 0.5);
		result.width = Math.floor( start.width + (end.width - start.width) * (percent / 100) + 0.5);
		result.height = Math.floor( start.height + (end.height - start.height) * (percent / 100) + 0.5);
	}
	return result;
};

/*	Private Method: pax.fx.tween.util.dec2hex
	Calculate the hexidecimal value of a decimal
*/
pax.fx.tween.util.dec2hex = function( dec ) {
	var hexDigit = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
	return hexDigit[dec>>4] + hexDigit[dec&15];
};

/*	Private Method: pax.fx.tween.util.hex2dec
	Calculate the decimal value of a hexidecimal
*/
pax.fx.tween.util.hex2dec = function( hex ) {
	return parseInt(hex,16);
};

/*	Private Method: pax.fx.tween.getColour
	Calculate the colour value between start and end, at percentage pct.
	
	Parameters:
		start - starting colour without the hash, eg: FF7EA8.
		end - ending colour without the hash, eg: 8AE7FF.
		pct - how far through we are, in percent
		
	Returns:
		The new colour, including the hash
*/
pax.fx.tween.getColour = function( start, end, pct ) {
	pct = pct / 100;
	
	var r1 = pax.fx.tween.util.hex2dec( start.slice(0, 2) );
	var g1 = pax.fx.tween.util.hex2dec( start.slice(2, 4) );
	var b1 = pax.fx.tween.util.hex2dec( start.slice(4, 6) );
	
	var r2 = pax.fx.tween.util.hex2dec( end.slice(0, 2) );
	var g2 = pax.fx.tween.util.hex2dec( end.slice(2, 4) );
	var b2 = pax.fx.tween.util.hex2dec( end.slice(4, 6) );

	var r = Math.floor( r1 + (pct * (r2 - r1) ) + .5 );
	var g = Math.floor( g1 + (pct * (g2 - g1) ) + .5 );
	var b = Math.floor( b1 + (pct * (b2 - b1) ) + .5 );

	return "#" + pax.fx.tween.util.dec2hex(r) + pax.fx.tween.util.dec2hex(g) + pax.fx.tween.util.dec2hex(b);
};


/*	Method: pax.fx.reveal
	To use this method, initialise via <pax.fx.init>. Reveals an element to a specified height, see <pax.fx.init> for details on how to use this.
	
	Parameters:
		endSize - Final height of element (start is assumed to be 0 and style.display='none')

	WIP:
		* Add ability to reveal by width as well

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.reveal.example1', 'reveal', {endSize: 160}, 1000 );" value="reveal+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.reveal.example1', 'reveal', {endSize: 160}, 1000, false );" value="reveal-">

			<span id='pax.fx.reveal.example1' style='display:none; overflow:hidden; background: #0000ff; width: 200px;border-bottom:1px solid #000'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</span>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
*/
pax.fx.reveal = function( val, ele, pct ) {
	if( val.direction ) {
		ele.style.height = ( val.endSize * pax.fx.tween.sinePercent( pct ) ) + 'px';
	} else {
		ele.style.height = ( val.endSize * pax.fx.tween.sinePercent( 100 - pct ) ) + 'px';
	}
	ele.style.display = 'block';
};

/*
	Private method: pax.fx.reveal.cleanup - Cleans up the reveal, ie sets the final height.
*/
pax.fx.reveal.cleanup = function( val, ele ) {
	ele.style.height = ( val.direction )? val.endSize + 'px' : '1px';
	if( !val.direction )ele.style.display = 'none';
};


/*	Method: pax.fx.colourFade
	To use this method, initialise via <pax.fx.init>. Fades between given background colours.
	
	Parameters:
		startColour - Colour to start the fade at
		endColour - Colour to finish the fade at

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.colourFade.example1', 'colourFade', {startColour: '0000ff', endColour: 'ff0000'}, 1000 );" value="ColourFade+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.colourFade.example1', 'colourFade', {startColour: '0000ff', endColour: 'ff0000'}, 1000, false );" value="ColourFade-">

			<div id='pax.fx.colourFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
*/
pax.fx.colourFade = function ( val, ele, pct ) {
	if( val.direction ) {
		ele.style.background = pax.fx.tween.getColour( val.startColour, val.endColour, pct );
	} else {
		ele.style.background = pax.fx.tween.getColour( val.startColour, val.endColour, (100 - pct) );
	}
};

/*
	Private method: fx.colourFade.cleanup - Cleans up the colour fade, ie sets the final colour
*/
pax.fx.colourFade.cleanup = function ( val, ele ) {
	ele.style.background = ( val.direction )? val.endColour : val.startColour;
};


/*	Method: pax.fx.opacityFade
	To use this method, initialise via <pax.fx.init>. Fades between opacity levels
	
	Parameters:
		startOpacity - Opacity to start the fade at
		endOpacity - Opacity to finish the fade at

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000, false );" value="Fade out">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );" value="Fade in">

			<div id='pax.fx.opacityFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
		
*/
pax.fx.opacityFade = function ( val, ele, pct ) {
    //    Calculate the percentage here, and set accordingly.
    var opacityDiff = val.endOpacity - val.startOpacity;
    if( val.direction )pax.css.opacity( ele, ( val.startOpacity + ( (pct / 100) * opacityDiff ) ) );
    else pax.css.opacity( ele, ( val.endOpacity - ( (pct / 100) * opacityDiff ) ) );
};


/*
	Private method: fx.opacityFade.cleanup - Cleans up the opacity fade, ie sets the final opacity
*/
pax.fx.opacityFade.cleanup = function ( val, ele ) {
	if( val.direction )pax.css.opacity( ele, val.endOpacity );
	else pax.css.opacity( ele, val.startOpacity );
};


/*	Method: pax.fx.fadeIn
	Fades in an element in optional time miliseconds

	Parameters:
		ele - element to fade in
		time - time in milliseconds to fade for, default time is 1000ms
		
	Example:
		(start code)
			<div id='pax.fx.fadeIn.example1' style='MozOpacity:0;opacity:0;KhtmlOpacity:0;filter:alpha(opacity=0);'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				pax.fx.fadeIn( 'pax.fx.fadeIn.example1' );
			:]
		(end)
		
*/
pax.fx.fadeIn = function( ele, time, callBack ) {
	time = (typeof(time) != 'undefined' )? time: 1000;
	pax.fx.init( ele, 'opacityFade', {startOpacity: 0, endOpacity: 100}, time, true, callBack );
};


/*	Method: pax.fx.fadeOut
	Fades out an element in optional time miliseconds

	Parameters:
		ele - element to fade out
		time - time in milliseconds to fade for, default time is 1000ms
		
	Example:
		(start code)
			<div id='pax.fx.fadeOut.example1'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				pax.fx.fadeOut( 'pax.fx.fadeOut.example1' );
			:]
		(end)
		
*/
pax.fx.fadeOut = function( ele, time, callBack ) {
	time = (typeof(time) != 'undefined' )? time: 1000;
	pax.fx.init( ele, 'opacityFade', { startOpacity: 0, endOpacity: 100 }, time, false, callBack );
};


/*	Method: pax.fx.move
	To use this method, initialise via <pax.fx.init>. Moves an element from one spot to another
	
	Parameters:
		startPos - Object with {x, y} co-ords to start at
		endPos - Object with {x, y} co-ords to end at

	Example:
		(start code)
			<input type="button" id="moveDown" value="Move down">
			<input type="button" id="moveUp" value="Move up">
			<input type="button" id="random" value="Random">

			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; background: #5555ff; color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x + 300, y:startPos.y + 150};
				var move = function( direction ) {
					pax.fx.init( 'pax.fx.move.example1', 'move', {startPos: startPos, endPos: endPos }, 1000, direction );
				};
				pax.$('moveDown').onclick = function() { move( true ); };
				pax.$('moveUp').onclick = function() { move( false ); };
				
				var random = function() {
					var endPos = {
						x: startPos.x + parseInt( Math.random() * 200),
						y: startPos.y + parseInt( Math.random() * 200)
					};
					var direction = (Math.random() > 0.5);
					pax.fx.init( 'pax.fx.move.example1', 'move', {startPos: startPos, endPos: endPos }, 1000, direction );
				};
				pax.$('random').onclick = function() { random(); };
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing, and it .
*/
pax.fx.move = function ( val, ele, pct ) {
	ele.style.position = 'absolute';
	if( val.direction ) {
		var pos = pax.fx.tween.position( val.startPos, val.endPos, pct, pax.fx.tween.sinePercent );
		if( !isNaN( pos.x ) )ele.style.left = pos.x + 'px';
		if( !isNaN( pos.y ) )ele.style.top = pos.y + 'px';
	} else {
		var pos = pax.fx.tween.position( val.startPos, val.endPos, (100 - pct), pax.fx.tween.sinePercent );
		if( !isNaN( pos.x ) )ele.style.left = pos.x + 'px';
		if( !isNaN( pos.y ) )ele.style.top = pos.y + 'px';
	}
};

/*	
	Private method: pax.fx.move.cleanup - Sets the final position
*/
pax.fx.move.cleanup = function ( val, ele ) {
	if( val.direction ) {
		if( typeof( val.endPos.x ) != 'undefined' )ele.style.left = val.endPos.x + 'px';
		if( typeof( val.endPos.y ) != 'undefined' )ele.style.top = val.endPos.y + 'px';
	} else {
		if( typeof( val.startPos.x ) != 'undefined' )ele.style.left = val.startPos.x + 'px';
		if( typeof( val.startPos.y ) != 'undefined' )ele.style.top = val.startPos.y + 'px';
	}
};


/*	Method: pax.fx.size
	To use this method, initialise via <pax.fx.init>. Sizes an element from one size to another
	
	Parameters:
		startSize - Object with {width, height} parameters to start at
		endSize - Object with {width, height} parameters to end at

	Example:
		(start code)
			<input type="button" id="sizeUp" value="size up">
			<input type="button" id="sizeDown" value="size down">
			<input type="button" id="random" value="Random">

			<div id='pax.fx.size.example1' style='position:absolute; overflow:hidden; background: #5555ff; color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startSize = pax.util.getPosition( pax.$('pax.fx.size.example1'), false );
				var endSize = {width:startSize.width + 300, height:startSize.height + 150};
				var size = function( direction ) {
					pax.fx.init( 'pax.fx.size.example1', 'size', {startSize: startSize, endSize: endSize }, 1000, direction );
				};
				pax.$('sizeDown').onclick = function() { size( false ); };
				pax.$('sizeUp').onclick = function() { size( true ); };
				
				var random = function() {
					var endSize = {
						width: startSize.width + parseInt( Math.random() * 200),
						height: startSize.height + parseInt( Math.random() * 200)
					};
					var direction = (Math.random() > 0.5);
					pax.fx.init( 'pax.fx.size.example1', 'size', {startSize: startSize, endSize: endSize }, 1000, direction );
				};
				pax.$('random').onclick = function() { random(); };
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing, and it .
*/
pax.fx.size = function ( val, ele, pct ) {
	if( val.direction ) {
		var size = pax.fx.tween.position( val.startSize, val.endSize, pct, pax.fx.tween.sinePercent );
		ele.style.width = size.width + 'px';
		ele.style.height = size.height + 'px';
	} else {
		var size = pax.fx.tween.position( val.startSize, val.endSize, (100 - pct), pax.fx.tween.sinePercent );
		ele.style.width = size.width + 'px';
		ele.style.height = size.height + 'px';
	}
};

/*
	Private method: pax.fx.size.cleanup - Sets the final size
*/
pax.fx.size.cleanup = function ( val, ele ) {
	if( val.direction ) {
		ele.style.width = val.endSize.width + 'px';
		ele.style.height = val.endSize.height + 'px';
	} else {
		ele.style.width = val.startSize.width + 'px';
		ele.style.height = val.startSize.height + 'px';
	}
};


/*	Method: pax.fx.cursorPos
	Get co-ordinates of the mouse cursor
	
	Paramaters:
		event - the event to find the cursor position with.
		
	Returns:
		an object with the x, y co-ordinates of the cursor

	WIP:
		* Perhaps move to util class

*/
pax.fx.cursorPos = function( event ) {
	var x = ( typeof( window.scrollX ) != 'undefined' )? 
		event.clientX + window.scrollX : 
		window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
		
	var y = ( typeof( window.scrollY ) != 'undefined' )? 
		event.clientY + window.scrollY : 
		window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
	
	return { x: x, y: y };
};


/*	Method: pax.fx.drag
	Sets an element as draggable
	
	Paramaters:
		event - the event to find the cursor position of.
		args - optional argument object to pass to the drag function, ie: {}
		* constrain - either 'vertical' or 'horizontal', eg: {constrain: 'vertical'}
		* grid - integer grid value, 0 = no grid, eg: {grid: 10}
		* box - limit where element can be dragged, can be the object returned by <pax.util.getPosition>, eg: {x:10, y:20, width:70, height:70} or co-ordinates, eg: {x:10, y:20, x2:80, y2:90}
		* snap - this will "snap" to a given "line".
		* callStart - function to be called when a draggable object starts being dragged, paramater is an object {x,y,args}
		* callMove - function to be called when a draggable object is being dragged, paramater is an object {x,y,args}, be careful as it's continiously being called
		* callBack - function to be called when a draggable object is dropped, paramater is an object {x,y,args}
	
	Example:
		(start code)
			<span id="pax.fx.drag.vline" style="position:absolute;left:550px;width:0px;height:400px;border-left:1px solid #aaa;"></span>
			<span id="pax.fx.drag.hline" style="position:absolute;left:525px;top:300px;width:400px;height:1px;border-top:1px solid #aaa;"></span>
			<span id="pax.fx.drag.example7.box" style="position:absolute;left:200px;top:125px;width:300px;height:200px;border: 1px solid #000;"></span>
			
			<div id="pax.fx.drag.example1" style="position:absolute;left:10px;top:40px;width:12em;border:1px solid #aaa; background:#ccf;padding:8px">
				Drag this box anywhere
			</div>
			
			<div id="pax.fx.drag.example2" style="position:absolute;left:10px;top:100px;width:12em;border:1px solid #aaa; background:#cfc;padding:8px">
				Drag this box left and right
			</div>
			
			<div id="pax.fx.drag.example3" style="position:absolute;left:10px;top:160px;width:12em;border:1px solid #aaa; background:#fcc;padding:8px">
				Drag this box up and down
			</div>
			
			<div id="pax.fx.drag.example4" style="position:absolute;left:10px;top:220px;width:12em;border:1px solid #aaa; background:#cff;padding:8px">
				Drag this box on a grid
			</div>
			
			<div id="pax.fx.drag.example5" style="position:absolute;left:10px;top:280px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Drag this box within the window only
			</div>
			
			<div id="pax.fx.drag.example6" style="position:absolute;left:200px;top:40px;border:1px solid #aaa; background:#ffc;padding:8px">
				<div id="pax.fx.drag.example6.handle" style="width:12em;background:#99f;padding:4px" >Drag Handle</div>
				<div class="content" style="width:12em;">
					Drag this box using the handle.
				</div>
			</div>
			
			<div id="pax.fx.drag.example7" style="position:absolute;left:200px;top:125px;width:12em;border:1px solid #aaa; background:#f0f;padding:8px">
				Drag this box within a certain square only
			</div>
			
			<div id="pax.fx.drag.example8" style="position:absolute;left:10px;top:340px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Drag this box and That box
			</div>
			
			<div id="pax.fx.drag.example9" style="position:absolute;left:200px;top:340px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				That box
			</div>
			
			<div id="pax.fx.drag.example10" style="position:absolute;left:550px;top:40px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Snap box - drag near lines
			</div>

			<div id="pax.fx.drag.example11" style="position:absolute;left:550px;top:100px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Return box - drag and drop to return
			</div>

			[:.
				pax.fx.drag( pax.$('pax.fx.drag.example1') );
				pax.fx.drag( pax.$('pax.fx.drag.example2'), { constrain: 'horizontal' } );
				pax.fx.drag( pax.$('pax.fx.drag.example3'), { constrain: 'vertical' } );
				pax.fx.drag( pax.$('pax.fx.drag.example4'), { grid: 30, callStart: function(arg) { console.log( arg.x + ' : ' + arg.y + ' : ' + arg.args ); } } );
				pax.fx.drag( pax.$('pax.fx.drag.example5'), { box: pax.util.getPosition( pax.$('exampleDiv'), false ) } );
				pax.fx.drag( pax.$('pax.fx.drag.example6'), { handle: pax.$('pax.fx.drag.example6.handle') } );
				pax.fx.drag( pax.$('pax.fx.drag.example7'), { box: {x:200, y:125, x2:500, y2:325} } );
				pax.fx.drag( pax.$('pax.fx.drag.example8') );
				pax.fx.drag( pax.$('pax.fx.drag.example9'), { handle: pax.$('pax.fx.drag.example8') } );
				pax.fx.drag( pax.$('pax.fx.drag.example9') );
				pax.fx.drag( pax.$('pax.fx.drag.example10'), { snap: [{x:550,y:50}, {y:300,limit:20}] } );				
				pax.fx.drag( pax.$('pax.fx.drag.example11'), { return: true } );				
			:]
		(end)
		Note that we cater for almost all situations here, and you can obviously combine each argument type.

*/
pax.fx.drag = function( element, args ) {
	element = ( typeof( element ) == 'string' )? document.getElementById( element ) : element;
	var boundElement = null;
	if( typeof( args ) != 'undefined' ) {
		var handle = (typeof(args.handle) != 'undefined' )? args.handle: null;
		args.element = element;
		if( handle ) {
			boundElement = pax.event.bind( handle, 'mousedown', function( event ) { 
				if( pax.util.getType( args.onBeforeDrag ) == 'function' )args.onBeforeDrag( event, args );
				pax.fx.dragElement( event, args ); }
			);
		} else {
			boundElement = pax.event.bind( element, 'mousedown', function( event ) { 
				if( pax.util.getType( args.onBeforeDrag ) == 'function' )args.onBeforeDrag( event, args );
				pax.fx.dragElement( event, args ); }
			);
		}
	} else {
		boundElement = pax.event.bind( element, 'mousedown', pax.fx.dragElement  );
	}
	
	return boundElement;
};


				
/*	Private Method: pax.fx.dragElement
	set drag events on elements, see the 
	
*/
pax.fx.dragElement = function( event, args ) {
	var eventElement = event.target || window.event.srcElement;
	var ele = eventElement;

	//	Ensure we have a valid args object, even if it is empty, and assign the target element if set
	if( typeof(args) != 'undefined' ) {
		ele = (typeof(args.element) != 'undefined')? args.element: ele;
	} else {
		args = {};
	}
	
	if( ele.nodeType == 3 )ele = ele.parentNode;		//	If this is a text node, use its parent element.

	//	Set the configuration drag object
	var cur = pax.fx.cursorPos( event );
	var dObj = {
		element: ele,
		zIndex: 1000,
		startZIndex: ( isNaN( parseInt(ele.style.zIndex) ) )? 0 : parseInt( ele.style.zIndex ),
		startCursorX: cur.x,
		startCursorY: cur.y,
		startLeft: ( isNaN(parseInt(ele.style.left )) )? 0: parseInt(ele.style.left ),
		startTop: ( isNaN(parseInt(ele.style.top )) )? 0: parseInt(ele.style.top ),
		args: args
	};

	ele.style.zIndex = dObj.zIndex;

	//	Function to move the objects with
	var dragMove = function( event ) {
		var cur = pax.fx.cursorPos( event );

		//	Loop through the objects we're dragging
		for( var dList in pax.fx.dragDict ) {
			var dragList = pax.fx.dragDict[dList];
			for( var d in dragList ) {
				var dObj = dragList[d];

				dObj.element.style.position = 'absolute';

				var arg = { constrain: '', grid: 0, box: {}, snap: [] };

				if( typeof(dObj.args) != 'undefined' ) {
					for( var a in args ) {
						arg[a] = args[a];
					}
				}
				
				var newX = dObj.startLeft + cur.x - dObj.startCursorX;
				var newY = dObj.startTop + cur.y - dObj.startCursorY;

				//	Snap to grid
				if( arg.grid > 0 ) {
					var offX = newX % arg.grid;
					newX += (offX >= arg.grid / 2)? (arg.grid - offX): -offX;
					var offY = newY % arg.grid;
					newY += (offY >= arg.grid / 2)? (arg.grid - offY): -offY;
				}

				//	Check we're inside the box area
				if( arg.box != {} ) {
					var dims = pax.util.getPosition( dObj.element, false );
					
					if( typeof(arg.box.x) != 'undefined' ) {
						if( newX < arg.box.x )newX = arg.box.x;
					}
					
					if( typeof(arg.box.y) != 'undefined' ) {
						if( newY < arg.box.y )newY = arg.box.y;
					}
					
					if( typeof(arg.box.x2) != 'undefined' ) {
						if( newX > (arg.box.x2 - dims.width) )newX = (arg.box.x2 - dims.width);
					} else if( typeof(arg.box.width) != 'undefined' ) {
						if( newX > (arg.box.width + arg.box.x - dims.width) )newX = (arg.box.width + arg.box.x - dims.width);
					}
					
					if( typeof(arg.box.y2) != 'undefined' ) {
						if( newY > (arg.box.y2 - dims.height) )newY = (arg.box.y2 - dims.height);
					} else if( typeof(arg.box.height) != 'undefined' ) {
						if( newY > (arg.box.height + arg.box.y - dims.height) )newY = (arg.box.height + arg.box.y - dims.height);
					}
				}

				//	Check if we're close to a snap area, and snap accordingly
				if( arg.snap != [] ) {
					var dims = pax.util.getPosition( dObj.element, false );
					
					for( var s in arg.snap ) {
						var snap = arg.snap[s];
						var limit = (typeof(snap.limit)!='undefined')? snap.limit: 10;
						
						if( typeof(snap.x) != 'undefined' ) {
							if( ( newX <= (snap.x + limit) ) && ( newX >= (snap.x - limit) ) ) {
								newX = snap.x;
							}
						}
						
						if( typeof(snap.y) != 'undefined' ) {
							if( ( newY <= (snap.y + limit) ) && ( newY >= (snap.y - limit) ) ) {
								newY = snap.y;
							}
						}						
					}					
				}

				//	Apply constraints and set new co-ordinates
				if( arg.constrain != 'vertical' ) {
					dObj.element.style.left = newX + 'px';
				}
				if( arg.constrain != 'horizontal' ) {
					dObj.element.style.top = newY + 'px';
				}
				
				//	Look for drop target observers
				for( var dropl in pax.fx.dropDict ) {
					var dropList = pax.fx.dropDict[dropl];
					for( var dr in dropList ) {
						var dropObj = dropList[dr];
						//	Check if the dragged object is on top of the drop target
						var dropDims = pax.util.getPosition( dropObj.element );
						
						//	Note: we're going to assume we want to fire on the mouse co-ords, NOT the object.
						if( cur.x >= dropDims.x && cur.x <= dropDims.x + dropDims.width && cur.y >= dropDims.y && cur.y <= dropDims.y + dropDims.height ) {

							//	Notify the observer of hover position
							if( typeof(dropObj.hover) == 'function' )dropObj.hover( cur.x, cur.y, dObj.args );
							
							if( ! dropObj.hoverOver ) {
								// Notify the observer - may have to use x and y relative to observer obj.
								if( typeof(dropObj.hoverEnter) == 'function' )dropObj.hoverEnter( cur.x, cur.y, dObj.args );
							}
							dropObj.hoverOver = true;
						} else {
							if( dropObj.hoverOver ) {
								//	Notify hover off
								if( typeof(dropObj.hoverExit) == 'function' )dropObj.hoverExit( cur.x, cur.y, dObj.args );
							}
							dropObj.hoverOver = false;
						}
					}
				}
				
				//	Run the callMove function every 10 ms
				var now = new Date().getTime();
				if( (dObj.callMoveTimer == null || ( dObj.callMoveTimer < (now - 10) ) ) && typeof(dObj.args.callMove) == 'function' ) {
					dObj.args.callMove( {x:newX, y:newY, args:dObj.args} );
				}
				dObj.callMoveTimer = now;
			}
		}
		
		//	Prevent text selection
		if( event.preventDefault ) {
			event.preventDefault();
		} else {
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		}
	};

	//	Function that fires when the dragging is finished, cleans up the drag objects
	var dragStop = function( event ) {
		var ele = event.target || window.event.srcElement;
		
		for( var dList in pax.fx.dragDict ) {
			var dragList = pax.fx.dragDict[dList];
			for( var d in dragList ) {
				//	Reset the zIndex of the object.
				var dObj = dragList[d];
				
				var resetZIndex = ( typeof(dObj.args.resetZIndex) != 'undefined' )? dObj.args.resetZIndex: true;
				if( resetZIndex ) {
					dObj.element.style.zIndex = dObj.startZIndex;					
				}
				
				//	Look for drop targets
				for( var dropl in pax.fx.dropDict ) {
					var dropList = pax.fx.dropDict[dropl];
					for( var dr in dropList ) {
						var dropObj = dropList[dr];
						//	Check if the dragged object is on top of the drop target
						var dropDims = pax.util.getPosition( dropObj.element );
						
						//	Note: we're going to assume we want to fire on the mouse co-ords, NOT the object.
						var cur = pax.fx.cursorPos( event );
						
						if( cur.x >= dropDims.x && cur.x <= dropDims.x + dropDims.width && cur.y >= dropDims.y && cur.y <= dropDims.y + dropDims.height ) {
							// Notify the observer - may have to use x and y relative to observer obj.
							if( typeof(dropObj.finish) == 'function' )dropObj.finish( cur.x, cur.y, dObj.args );
						}
					}
				}
				
				//	Could do the "snap-back" animation here, in case it's an 'invalid target...
				//	Run the callback
				if( typeof(dObj.args.callBack) == 'function' )dObj.args.callBack( {x:parseInt(dObj.element.style.left), y:parseInt(dObj.element.style.top), args:dObj.args} );
			}
			//	Remove the object's references
			delete dragList;
		}
		
		//	Unbind the events
		pax.event.unbind( document, 'mousemove', dragMove );
		pax.event.unbind( document, 'mouseup', dragStop );
		
		// Clear the dict
		pax.fx.dragDict = {};
		
		if( pax.util.getType( args.onAfterDrag ) == 'function' )args.onAfterDrag( event, dObj.args );
	};

	//	Capture events
	pax.event.bind( document, 'mousemove', dragMove );
	pax.event.bind( document, 'mouseup', dragStop );

	//	Prevent text selection
	if( event.preventDefault ) {
		event.preventDefault();
	} else {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	
	if( typeof( pax.fx.dragDict[eventElement] ) == 'undefined' )pax.fx.dragDict[eventElement] = [];
	pax.fx.dragDict[eventElement].push( dObj );

	//	Run the callStart function
	if( typeof(dObj.args.callStart) == 'function' )dObj.args.callStart( {x:parseInt(dObj.element.style.left), y:parseInt(dObj.element.style.top), args:dObj.args} );
};


/*	Method: pax.fx.drop
	Sets an element as a drop target for a draggable element
	
	Paramaters:
		element - what DOM element to use as a drop target
		args - an object with various methods and options
		* finish - function that fires when the drop finishes on the target. Parameters are {x, y, args}
		* hover - function that fires when the target is hovered by a drag element. Parameters are {x, y, args}
		* hoverEnter - function that fires when a drag element hovers above the drop target. Parameters are {x, y, args}
		* hoverExit - function that fires when a drag element exits the drop target. Parameters are {x, y, args}
		
		The args parameter is that of the drag element.
	
	Example:
		(start code)
			<span id="pax.fx.drop.example1.target" style="position:absolute;left:200px;top:125px;width:300px;height:200px;border: 1px solid #000;">
				Drop target
			</span>
			
			<div id="pax.fx.drag.example1" style="position:absolute;left:10px;top:40px;width:12em;border:1px solid #aaa; background:#ccf;padding:8px">
				Drag this box onto the drop target
			</div>
			
			[:.
				pax.fx.drag( pax.$('pax.fx.drag.example1') );
				var args = {
					finish: function( x, y, args ) {console.log('finish ' + x + ' : ' + y);},
					hover: function( x, y, args ) {return;console.log('hover ' + x + ' : ' + y);},
					hoverEnter: function( x, y, args ) {pax.$('pax.fx.drop.example1.target').style.border='3px solid red';console.log('hoverEnter ' + x + ' : ' + y);},
					hoverExit: function( x, y, args ) {pax.$('pax.fx.drop.example1.target').style.border='1px solid #000';console.log('hoverExit ' + x + ' : ' + y);}
				};
				pax.fx.drop( pax.$('pax.fx.drop.example1.target'), args );
				exampleCleanup = function() {
					pax.fx.dropDict = {};
				};
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.

*/
pax.fx.drop = function( element, args ) {
	var dObj = {
		element: element,
		hoverOver: false
	};

	if( typeof(args) != 'undefined' ) {
		dObj.finish = (typeof(args.finish) == 'function')? args.finish: null;
		dObj.hover = (typeof(args.hover) == 'function')? args.hover: null;
		dObj.hoverEnter = (typeof(args.hoverEnter) == 'function')? args.hoverEnter: null;
		dObj.hoverExit = (typeof(args.hoverExit) == 'function')? args.hoverExit: null;
	}
	
	if( typeof( pax.fx.dropDict[element] ) == 'undefined' )pax.fx.dropDict[element] = [];
	pax.fx.dropDict[element].push( dObj );	
};
/* ----------------------------------------------------------------------------

	pax.validate.js Copyright (C) 2005, 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.validate
		This is the form validation part of the PAX library
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

/*

	WIP
	---

	. Add a change detection method, to show that data has changed. Note: use field.defaultValue, see:
		http://www.comptechdoc.org/independent/web/cgi/javamanual/javaform.html
		
	. Add a "hint wizard" mode, where you can display hints one after the other, with 'back' 'cancel' and 'next' buttons.
		- This will show how to acheive something specific, kind of like a "learning mode".
		- You should be able to insert comments in between hint steps, in the "hint wizard".		
		- We have hints, so we just need a simple list of the order in which to enter the data fields,
			then a way to manage which fields have been entered correctly.
		- The wizard start would be assigned to an elemnt click
		- Each step would focus on a field, and show the message with buttons to go "Back" or "Next" at the bottom.
		- The wizard should be aware of tabsets, and be able to focus on fields in a specific tab.

	. As we now support multiple validators per field, we need a way to show 
		the message, when a validation fails, so people know what went wrong...

--- */

var pax = pax || {};
pax.validate = pax.validate || {};


pax.validate.validators = {};								// The validator methods
pax.validate.ajaxValidatorQueue = {};						// The ajax validator queue, obj shape: { oldValue: [VALUE], robj: [REQUEST OBJECT] }
pax.validate.showHintOnFocus = true;						// If we show the hint as soon as you enter the field
pax.validate.showHintOnHover = true;						// If we show the hint as you hover over the field
pax.validate.showHintOnlyOnError = true;					// If we show the hint only if there is an error
pax.validate.showAlertOnError = false;						// If we show an alert on error, or simply focus on field, and display a hint
pax.validate.preventSubmit = true;							// If prevent the form from being submitted, if it has errors
pax.validate.hintMessageClass = 'hintMessage';				// CSS Class to use for hint messages
pax.validate.errorMessageClass = 'errorMessage';			// CSS Class to use for error messages
pax.validate.fieldValidClass = 'validField';				// CSS Class to use for valid field
pax.validate.fieldInvalidClass = 'invalidField';			// CSS Class to use for invalid field
pax.validate.fieldToBeClass = 'tobeValidatedField';			// CSS Class to use for ajax validation fields, before they are validated


/* ----------------------------------------------------------------------------
	The validators take a field, and return true if it is valid, false if not.
---------------------------------------------------------------------------- */

/*	Method: pax.validate.validators.ip
	Validate an ip address, also checks for optional port number
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="ipaddress">
				</span>
			</form>
			[:.
				valDict = {
					'ipaddress': { mask: 'ip', hint: 'This must be a valid IP Address. You can specify a port, with <b>:[port number]</b>' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="ipaddress" value="127.0.0.1:8080">
			</span>
			<div id='pax.validate.validators.ip.example1'></div>
			[:.
				pax.$('pax.validate.validators.ip.example1').innerHTML = 'Is this a valid email address?: ' + pax.validate.validators.ip( pax.$('ipaddress') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.ip = function( field, mask ) {
	var validField = true;
	var fieldValue = pax.validate.defaultWhiteSpace( field.value, mask );
	portMin = 0;
	portMax = 99999;
	if ( !( /^\d{1,3}(\.\d{1,3}){3}(:\d+)?$/.test( fieldValue ) ) )validField = false;
	else {
		var part = fieldValue.split( /[.:]/ );
		for( var i = 0; i < part.length; i++ ) {
			if ( i == 4 ) {	// Check port
				if ( part[i] < portMin || part[i] > portMax )validField = false;
			}
			else if ( part[i] < 0 || part[i] > 255 ) {
				validField = false;
			}
		}
	}
	return validField;
};


/*	Method: pax.validate.validators.email
	Validate an email address
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<label for="emailAddress">Email:</label>
					<input type="text" name="emailAddress">
				</span>
			</form>
			[:.
				valDict = {
					'emailAddress': { mask: 'email', hint: 'This must be in email format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="emailAddress" value="bill@microsoft.com">
			</span>
			<div id='pax.validate.validators.email.example1'></div>
			[:.
				pax.$('pax.validate.validators.email.example1').innerHTML = 'Is this a valid email address?: ' + pax.validate.validators.email( pax.$('emailAddress') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.email = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	//	return value.match(/\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/gi);
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(2([0-4]\d|5[0-5])|1?\d{1,2})(\.(2([0-4]\d|5[0-5])|1?\d{1,2})){3} \])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test( value );
};


/*	Method: pax.validate.validators.alpha
	Validates an alpha value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="alpha">
				</span>
			</form>
			[:.
				valDict = {
					'alpha': { mask: 'alpha', hint: 'This must be in alpha format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="alpha" value="qwerty">
			</span>
			<div id='pax.validate.validators.alpha.example1'></div>
			[:.
				pax.$('pax.validate.validators.alpha.example1').innerHTML = 'Is this only alpha chars?: ' + pax.validate.validators.alpha( pax.$('alpha') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.alpha = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[a-zA-Z]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.numeric
	Validates a numeric value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="numeric">
				</span>
			</form>
			[:.
				valDict = {
					'numeric': { mask: 'numeric', hint: 'This must be in numeric format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="numeric" value="123456">
			</span>
			<div id='pax.validate.validators.numeric.example1'></div>
			[:.
				pax.$('pax.validate.validators.numeric.example1').innerHTML = 'Is this only numeric chars?: ' + pax.validate.validators.numeric( pax.$('numeric') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.numeric = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[0-9]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.alphaNumeric
	Validates an alpha numeric value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="alphaNumeric">
				</span>
			</form>
			[:.
				valDict = {
					'alphaNumeric': { mask: 'alphaNumeric', hint: 'This must be in alphaNumeric format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="alphaNumeric" value="123456">
			</span>
			<div id='pax.validate.validators.alphaNumeric.example1'></div>
			[:.
				pax.$('pax.validate.validators.alphaNumeric.example1').innerHTML = 'Is this only alphaNumeric chars?: ' + pax.validate.validators.alphaNumeric( pax.$('alphaNumeric') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.alphaNumeric = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[a-zA-Z0-9]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.len
	Validates length of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="len">
				</span>
			</form>
			[:.
				valDict = {
					'len': { mask: [ { mask: 'len', minLen: '4', maxLen: '6'} ], hint: 'This must be in at least 4 chars, and no more than 6 chars' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="len" value="12345">
			</span>
			<div id='pax.validate.validators.len.example1'></div>
			[:.
				pax.$('pax.validate.validators.len.example1').innerHTML = 'Is this only len chars?: ' + pax.validate.validators.len( pax.$('len'), { minLen: 4, maxLen: 6 } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.len = function( field, mask ) {
	var validField = true;
	var min = ( mask.minLen != null )? mask.minLen : 0;
	var max = ( mask.maxLen != null )? mask.maxLen : 1000000;

	//	Select field length
	if( field.type.indexOf( 'select' ) > -1 ) {
		var selectedOptions = 0;
		for( var x =0; x < field.options.length; x++ ) {
			if( field.options[x].selected )selectedOptions += 1;
		}
		validField = ( selectedOptions >= min && selectedOptions <= max );
	} else {
		validField = ( field.value.length >= min && field.value.length <= max );
	}
	
	return validField
};


/*	Method: pax.validate.excludeSelect
	Excludes an option value from a select box
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="exclude">
						<option value='one'>One</option>
						<option value='two'>Two</option>
						<option value='three'>Three</option>
					</select>
				</span>
			</form>
			[:.
				valDict = {
					'exclude': { mask: [ { mask: 'excludeSelect', exclude: [0] } ], hint: 'Select anything but the first option' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<select name="exclude" id="exclude">
					<option value='one'>One</option>
					<option value='two'>Two</option>
					<option value='three'>Three</option>
				</select>
			</span>
			<div id='pax.validate.validators.exclude.example1'></div>
			[:.
				pax.$('pax.validate.validators.exclude.example1').innerHTML = 'Is this a valid option?: ' + pax.validate.validators.excludeSelect( pax.$('exclude'), { exclude: [0] } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.excludeSelect = function( field, mask ) {
	var validField = true;
	var exclude = ( mask.exclude != null )? mask.exclude : [];

	//	Select disallow options
	if( field.type.indexOf( 'select' ) > -1 ) {
		for( var x = 0; x < field.options.length; x++ ) {
			if( field.options[x].selected ) {
				for( var e = 0; e < exclude.length; e++ ) {
					if( x == exclude[e] ) {
						validField = false;
						break;
					}
				}
			}
		}
	} else {
		validField = true;
	}
	
	return validField
};


/*	Method: pax.validate.validators.range
	Validates range of the value of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="rangeField">
				</span>
			</form>
			[:.
				valDict = {
					'rangeField': { mask: [ { mask: 'range', min: '10', max: '50'} ], hint: 'This must be in at least 10, and no more than 50' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="rangeField2" value="30">
			</span>
			<div id='pax.validate.validators.range.example1'></div>
			[:.
				pax.$('pax.validate.validators.range.example1').innerHTML = 'Is this within range?: ' + pax.validate.validators.range( pax.$('rangeField2'), { min: 20, max: 40 } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.range = function( field, mask ) {
	var min = ( mask.min != null )? mask.min : false;
	var max = ( mask.max != null )? mask.max : false;
	var value = parseInt( field.value ) || 0;
	//	TODO: fail, if we can't parse int here.
	return ( value >= min && value <= max );
};


/*	Method: pax.validate.validators.fieldEqual
	Validates that the value of one field is the same as another
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise
		
	Example:
		(start code)
			<form id='valForm' name='valForm'>
				<label for='pass1'>Password: </label><input type='text' name='pass1'/>
				<label for='pass2'>Re-enter password: </label><input type='text' name='pass2'/>
				<input type='submit'/>
			</form>
			<div id='pax.validate.validators.len.example2'></div>
			[:.
				var valDict = {
					'pass1': { 
						mask: [ 
							{ mask: 'len', minLen: '6', maxLen: '12'},
							{ mask: 'checkPasswords', field2: 'pass2' } 
						], hint: 'Please enter a pasword between 6 and 12 chars, you must renter the same password in the password 2 box.' }
				};  

				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This will validate the two password fields contain the same value
*/
pax.validate.validators.fieldEqual = function( field, mask ) {
	var field2 = pax.$(mask.field) || document.forms[field.formName][mask.field];
	return ( field.value == field2.value)? true : false;
};


/*	Method: pax.validate.validators.notEmpty
	Validates contents of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="notEmptyField">
				</span>
			</form>
			[:.
				valDict = {
					'notEmptyField': { mask: 'notEmpty', hint: 'This field must have a value specified' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input name="level" type="radio" value="one"> 1
					<input name="level" type="radio" value="two"> 2
					<input name="level" type="radio" value="three"> 3
				</span>
			</form>
			[:.
				valDict = {
					'level': { mask: 'notEmpty', hint: 'You must check a level' }
				};

				pax.validate.initValidation( 'valForm', valDict );  
			:]
		(end)
		This would set a validator on the radio fields, and display a hint with the specified text.
		It should be noted that the formName is requied to validate radio fields, ie: you can't validate
		radio fields outside a form.
		
	Example:
		(start code)
			<span>
				<input type="text" id="notEmptyField2" value="12345">
			</span>
			<div id='pax.validate.validators.len.example2'></div>
			[:.
				pax.$('pax.validate.validators.len.example2').innerHTML = 'Is this field not empty?: ' + pax.validate.validators.notEmpty( pax.$('notEmptyField2') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.notEmpty = function( field, mask ) {
	var validField = false;
	if( field.type == 'radio' ) {
		if( typeof field.formName != 'undefined' ) {
			if( field.formName ) {
				rfields = document.forms[field.formName][field.name];
				for( var r = 0; r < rfields.length; r++ ) {
					if( rfields[r].checked )validField = true;
				}
			}
		}
	} else if( field.type.indexOf( 'select' ) > -1 ) {
		//	Note: In IE, using field.value won't work, if the select box options don't have a value attribute, so we MUST always use a seletc box properly.
		validField = (field.value != '' && field.value != 'undefined' && field.value != null);
	} else {
		validField = (field.value != '' && field.value != 'undefined' && field.value != null);
	}
	
	return validField;
};


/*	Method: pax.validate.validators.postcode
	Validates an Australian post code
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="postcode">
				</span>
			</form>
			[:.
				valDict = {
					'postcode': { mask: 'postcode', hint: 'This must be in postcode format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="postcode" value="2444">
			</span>
			<div id='pax.validate.validators.postcode.example1'></div>
			[:.
				pax.$('pax.validate.validators.postcode.example1').innerHTML = 'Is this a valid postcode?: ' + pax.validate.validators.postcode( pax.$('postcode') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.postcode = function( field, mask ) {
	return ( field.value.match(/^[0-9]{4}$/gi) )? true : false;
};


/*	Method: pax.validate.validators.luhn
	Validates a number with the luhn algorithm. Note that this is used in CC validation, and IMEI validation.
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="number">
				</span>
			</form>
			[:.
				valDict = {
					'number': { mask: 'luhn', hint: 'This must be a number that has a valid luhn check digit' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="number" value="4242424242424242">
			</span>
			<div id='pax.validate.validators.luhn.example1'></div>
			[:.
				pax.$('pax.validate.validators.luhn.example1').innerHTML = 'Is this a valid luhn check digit number?: ' + pax.validate.validators.luhn( pax.$('number') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.luhn = function( field, mask ) {
	var value = field.value;
	if( ! pax.validate.validators.numeric( { value: value } ) )return false;
	
	var sum = 0;
	var alt = false;
	var numvar = 0;
	for( var i = value.length - 1; i >= 0; i-- ) {
		numvar = parseInt( value.charAt(i) );
		if( alt ) {
			numvar *= 2;
			if( numvar > 9 )numvar -= 9;
		}
		sum += numvar;
		alt = !alt;
	}
	return ( sum % 10 == 0 );
};


/*	Method: pax.validate.validators.imei
	Validates a IMEI number using luhn algorithm
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="imei" id="imei">
				</span>
			</form>
			[:.
				valDict = {
					'imei': { mask: 'imei', hint: 'This must be a valid IMEI number' },
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
*/
pax.validate.validators.imei = function( field ) {
	var value = field.value;
	value = value.split(' ').join('').split('-').join('');	//	remove spaces and dashes
	if( ! ( value.length == 15 ) )return false;
	return pax.validate.validators.luhn( { value: value } );
};



/*	Method: pax.validate.validators.creditcard
	Validates a credit card number; optionally validates card type
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: [ { mask: 'creditcard', cardtype: 'Visa' } ], hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		
	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="card_type" id="card_type">
						<option>Mastercard</option>
						<option>Visa</option>
						<option>Diners</option>
						<option>Amex</option>
					</select>
				</span>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: 
						[ { 
							mask: 'creditcard', 
							cardtype: 'Visa',
							cardSelect: pax.$('card_type')
						} ], 
						hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="card_type" id="card_type">
						<option value=''>Please select card type</option>
						<option value='1'>Mastercard</option>
						<option value='2'>Visa</option>
						<option value='3'>Diners</option>
						<option value='4'>Amex</option>
					</select>
				</span>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: 
						[ { 
							mask: 'creditcard', 
							cardtype: 'Visa',
							cardSelect: pax.$('card_type'),
							cardSelectByText: true
						} ], 
						hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		Note that by setting cardSelectByText to ture, we use the option text, rather than the option value.
*/
pax.validate.validators.creditcard = function( field, mask ) {
	var fieldValid = true;
	var value = field.value;
	
	//	remove spaces and dashes
	value = value.split(' ').join('').split('-').join('');
	
	//	Critical validations
	if( ! pax.validate.validators.numeric( { value: value } ) )return false;
	var luhnValid = pax.validate.validators.luhn( { value: value } );
	if( ! luhnValid )return false;
	var cardType = ( mask.cardtype != null && mask.cardtype != '' )? mask.cardtype: null;
	
	//	Check if there is a select box with the card type
	/*	WIP: Make sure this is rechecked, if the select box is changed.	*/
	var cardSelect = ( mask.cardselect != null )? mask.cardselect: null;
	if( pax.util.getType( cardSelect ) == 'string' )cardSelect = pax.$(cardSelect);
	var cardSelectByText = ( mask.cardselectbytext != null )? mask.cardselectbytext: null;
	if( cardSelect ) {
		//	TODO: Check that cardSelect is indeed a select, it could perhaps be a radio set too? if( indexOf( field.type, 'select' ) > -1 )...
		//	POSSIBLE SOLUTION: Use pax.form.getFieldValue?
		//	When the cardSelect box is changed, we revalidate the CC.
		pax.event.bindOne( cardSelect, ['change', 'keyup'], function() {
			pax.validate.validateField( null, field );
		} );
		
		var cardType = ( cardSelectByText )? 
			cardSelect.options[cardSelect.selectedIndex].text: 
			pax.form.getFieldValue( cardSelect ).toLowerCase();
	};
	
	if( cardType ) {
		cardType = cardType.toLowerCase();
		
		/*	Check the different card types.
			Test Credit Card Numbers:
			
			Visa: 		4111111111111111
			MasterCard: 5431111111111111
			Amex: 		341111111111111
			Discover: 	6011601160116611
		*/
		if( cardType == 'visa' ) {
			if( ! ( ( value.length == 16 || value.length == 13 ) && value.substring( 0, 1 ) == '4' ) )fieldValid = false;
		} else if( cardType == 'mastercard' ) {
			var sfx = value.substring( 0, 2 );
			if( ! ( value.length == 16 && ( pax.validate.validators.range( { value: sfx }, { min: 51, max: 55 } ) ) ) )fieldValid = false;
		} else if( cardType == 'amex' ) {
			var sfx = value.substring( 0, 2 );
			if( ! ( value.length == 15 && ( sfx == '34' || sfx == '37' ) ) )fieldValid = false;
		} else if( cardType == 'diners' ) {
			fieldValid = false;
			var sfx = value.substring( 0, 4 );
			//	Carte Blanche
			if( value.length == 14 && pax.validate.validators.range( { value: sfx.substring( 0, 3 ) }, { min: 300, max: 305 } ) )fieldValid = true;
			//	International
			if( value.length == 14 && sfx.substring( 0, 2 ) == '36' )fieldValid = true;
			//	US and Canada
			if( value.length == 16 && sfx.substring( 0, 2 ) == '55' )fieldValid = true;
		} else if( cardType == 'jcb' ) {
			fieldValid = false;
			var sfx = value.substring( 0, 4 );
			if( value.length == 15 && ( sfx == '1800' || sfx == '2131' ) )fieldValid = true;
			if( value.length == 16 && ( sfx == '35' ) )fieldValid = true;
		} else if( cardType == 'discover' ) {
			var sfx = value.substring( 0, 4 );
			if( ! ( value.length == 16 && ( sfx == '6011' || sfx.substring( 0, 2 ) == '65' ) ) )fieldValid = false;
		}
	}	

	return fieldValid;
};


/*	Method: pax.validate.validators.zip
	Validates a US Zip code
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="zip">
				</span>
			</form>
			[:.
				valDict = {
					'zip': { mask: 'zip', hint: 'This must be in zip format (NNNNN-NNNN)' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="zip" value="90210-5555">
			</span>
			<div id='pax.validate.validators.zip.example1'></div>
			[:.
				pax.$('pax.validate.validators.zip.example1').innerHTML = 'Is this a valid zip?: ' + pax.validate.validators.zip( pax.$('zip') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.zip = function( field, mask ) {
	return ( field.value.match(/^[0-9]{5}\-[0-9]{4}$/gi) )? true : false;
};


/*	Method: pax.validate.validators.domain
	Validates domain name
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="domain">
				</span>
			</form>
			[:.
				valDict = {
					'domain': { mask: 'domain', hint: 'This must be a valid domain name' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="domain" value="pointful.com">
			</span>
			<div id='pax.validate.validators.domain.example1'></div>
			[:.
				pax.$('pax.validate.validators.domain.example1').innerHTML = 'Is this a valid domain?: ' + pax.validate.validators.domain( pax.$('domain') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.domain = function( field, mask ) {
	return ( field.value.match(/\b[A-Z0-9.-]+\.[A-Z]{2,4}\b/gi) )? true : false;
};


/*	Method: pax.validate.validators.ajaxValidate
	Validates via an ajax call. Note that your should assign any non-ajax validators first, when using an ajax validator.
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm' action='../pax.validate.example.validation.php'>
				<span>
					<textarea name="notes">These are my notes</textarea>
				</span>
			</form>
			[:.
				valDict = {
					'notes': { mask: [ { mask: 'ajaxValidate', 'method': 'notes'} ], hint: 'The notes are checked for some bad words on the server, try "firetruck"' }
				};

				pax.validate.initValidation( 'valForm', valDict );  
			:]
		(end)
		This sets a validator on the field, and displays a hint with the specified text.
		
		When validating, the server request hits the action of the form ('../pax.validate.example.validation.php'), with the following parameters:
			ajaxValidate - 1
			method - 'notes'
			formName - 'valForm'
			fieldName - 'notes'
			fieldValue - 'These are my notes' (or whatever notes are in the field)
			
		The server side function will then return the following values (in a JSON object) to the <pax.validate.ajaxValidateCallBack> method:
			error - string (if a validation error occured, it will contain a message that will be displayed for 5 seconds)
			formName - 'valForm'
			fieldName - 'notes'
			validField - boolean (if the field is valid, it will be true)
		
*/
pax.validate.validators.ajaxValidate = function( field, mask ) {
	if( typeof( pax.validate.ajaxValidatorQueue[field.formName] ) == 'undefined' ) {
		pax.validate.ajaxValidatorQueue[field.formName] = {};
		pax.validate.setTobeValidatedField( field );	// Assumes non-validated on first entry
	} else if( typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) == 'undefined' ) {
		pax.validate.setTobeValidatedField( field );	// Assumes non-validated on first entry
	}
	
	var postedAjax = false;
	if( pax.validate.ajaxValidatorQueue[field.formName] && typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined') {
		postedAjax = ( pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] != false );
	};
	
	//	If the field hasn't been changed at all, we don't try and validate it.
	if( field.defaultValue == field.value )return false;
	
	//	Setup the initial posting, if we're not waiting for a callback.
	//	Note: the ajax callback will re-validate the field for us...
	if( ! postedAjax ) {
		var oldFieldValue = ( typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined' )? 
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['oldValue']: null;
		
		if( field.value != oldFieldValue ) {
			
			pax.validate.setTobeValidatedField( field );
				
			var validateAjaxField = function() {
				var url = ( typeof(mask.target) != 'undefined' )? mask.target: document.forms[field.formName].action;	//	Default to the form target, for server validation methods
				var ajaxMethod = ( typeof(mask.method) != 'undefined' )? mask.method: field.method;
				var post = 'ajaxValidate=1' + '&ajaxMethod=' + ajaxMethod + '&_program_mode=' + ajaxMethod + '&formName=' + field.formName + '&fieldName=' + field.name + '&fieldValue=' + field.value;
				var callBack = pax.validate.ajaxValidateCallBack;
				// check for field.ajaxCallBack here, for non-standard handler.
				if( typeof field.ajaxCallBack != 'undefined' )callBack = eval( field.ajaxCallBack );
				
				//	Set the status box spinner
				var pos = pax.util.getPosition( field );
				var spinnerBox = pax.box.showOnRight( field.formName + field.name + '_spinner', '', 'ajaxSpinner', field, 0 );
				pax.util.setPosition( spinnerBox, { x: ( pos.x + pos.width - 20 ), y: ( pos.y + 3 ) } );

				//	Hide error message
				pax.validate.hideFieldMessage( field );
				//	Make the call
				var rObject = pax.post( url, post, callBack, 'Ajax Validating ' + field.name, field.formName + field.name + '_spinner' );
				
				pax.validate.ajaxValidatorQueue[field.formName][field.name] = {
					oldValue: field.value, 
					robj: rObject, 
					mask: mask, 
					inProgress: true 
				};
				
				//	Clear the timeout - may not be necessary, but doesn't hurt.
				window.clearTimeout( pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] );
				pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] = null;
			};
			
			if( ! pax.validate.ajaxValidatorQueue[field.formName][field.name] ) {
				pax.validate.ajaxValidatorQueue[field.formName][field.name] = {};
			}

			//	Clear the timeout, and assign new timeout
			window.clearTimeout( pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] );
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] = window.setTimeout( validateAjaxField, 300 );
			
			return true;
		}
	} else {
		// Try and validate field, without running ajax, and cancel ajax call if local validation fails.
		pax.validate.validateField( null, field, false );
		if( ! field.valid ) {
			if( pax.validate.ajaxValidatorQueue[field.formName][field.name] && pax.validate.ajaxValidatorQueue[field.formName][field.name]['robj'] ) {
				//	Cancel current ajax call, and reset queue items
				pax.cancel( pax.validate.ajaxValidatorQueue[field.formName][field.name]['robj'].ro );
			}
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] = false;
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['oldValue'] = null;
			//	Revalidate, in case the field has changed
			pax.validate.validateField( null, field );
		}
	}
		
	return false;
};


/*	Private Method: pax.validate.ajaxValidateCallBack
	Ajax validation callback - handles response from the ajax validation; internal method only.
	
	Parameters:
		xml
		txt
		url

	Returns:
		true - boolean if the field validation passes, false otherwise

*/
pax.validate.ajaxValidateCallBack = function( xml, txt, url ) {
	var result = ( txt != '' )? pax.unJSON( txt ) : {};
	var error = result.error;
	var formName = result.formName;
	var fieldName = result.fieldName;
	var field = document.forms[formName][fieldName];
	var oldValue = (typeof pax.validate.ajaxValidatorQueue[formName][fieldName] != 'undefined')? pax.validate.ajaxValidatorQueue[formName][fieldName]['oldValue'] : null;

	//	Clear the progress flag
	pax.validate.ajaxValidatorQueue[formName][fieldName]['inProgress'] = false;

	//	Check if the value has changed while we were waiting for the callback, and run a new one if required
	if( oldValue != field.value && oldValue != null ) {
		// We take the mask from the queue, then remove the progress flag, and run the ajax validation again.
		var fieldMask = pax.validate.ajaxValidatorQueue[formName][fieldName].mask;
		pax.validate.validators.ajaxValidate( field, fieldMask );
	} else {
		//	We're done, and should set the field validity.
		if( result.validField == 1 ) {
			pax.validate.setValidField( field );
		} else {
			pax.validate.showFieldError( field, error );
			pax.validate.setInvalidField( field );
		}
		pax.validate.ajaxValidatorQueue[formName][fieldName]['validField'] = ( result.validField == 1 );
	}
};


/* ----------------------------------------------------------------------------
	The actual validator methods
---------------------------------------------------------------------------- */


pax.validate.showFieldError = function( field, message ) {
	pax.validate.showFieldMessage( field, message, pax.validate.errorMessageClass, true );
};

//
//	Create an errorMessage DIV next to the field,
//	or on value change or if we exit the field.
//
pax.validate.showFieldMessage = function( field, message, messageClass, alwaysShow ) {
	var showMessage = alwaysShow || false;
	if( pax.validate.showHintOnlyOnError ) {
		if( pax.util.hasClassName( field, pax.validate.fieldInvalidClass ) ) {
			showMessage = true;
		}
	} else {
		showMessage = true;
	}
	
	if( showMessage ) {
		messageClass = messageClass || pax.validate.hintMessageClass;
		//	Hide field error here.
		if( field.messageEventQueue )pax.validate.hideFieldMessage( field );

		var hintBox = pax.box.showOnRight( field.formName + field.name + '_message', message, messageClass, field, 0 );
		pax.css.opacity( hintBox, 100 );

		//	Add events
		field.messageEventQueue = [];
		field.messageEventQueue.push( pax.event.bind( field, 'blur', function(e) { pax.validate.hideFieldMessage( field ) } ) );
		field.messageEventQueue.push( pax.event.bind( field, 'mouseout', function(e) { pax.validate.hideFieldMessage( field ) } ) );
	}
};


//
//	Hide the field message, and clear the events
//
pax.validate.hideFieldMessage = function( field ) {
	//	Hide the box, and clear the events
	pax.box.hide( field.formName + field.name + '_message' );
	for( var event in field.messageEventQueue ) {
		var evt = field.messageEventQueue[event];
		pax.event.unbind( evt['obj'], evt['event'], evt['func'] );
	}
};


//
//	Remove white space
//
pax.validate.removeWhiteSpace = function( value ) {
	value = value.split(' ').join('').split('\t').join('');
	return value;
};


//
//	Default white space by mask, or by def. If neither mask.whiteSpace nor def, we allow white space.
//	Note: this returns a value with white space removed, if we allow white space.
//
pax.validate.defaultWhiteSpace = function( value, mask, def ) {
	var allowWhiteSpace = true;
	
	if( pax.util.hasKey( 'whiteSpace', mask ) )allowWhiteSpace = ( mask.whiteSpace == true );
	else {
		//	Look to default
		if( def != 'undefined' ) {
			allowWhiteSpace = def;
		} else 	allowWhiteSpace = true;
	}
	
	return ( allowWhiteSpace )? pax.validate.removeWhiteSpace( value ): value;
};


//
//	returns the masked value - this could really be split up a bit better, ie: each mask field could be a function...
//
//	WIP - TODO: this does nothing yet.
//
pax.validate.maskEnforcer = function( value, forceMask ) {
	/*
		tmpMask = tmpMask.replace(/-/g,'\\-');
		tmpMask = tmpMask.replace(/S/g,'[A-Z]');
		tmpMask = tmpMask.replace(/N/g,'[0-9]');
		tmpMask = eval("/^" + tmpMask + "$/gi");
	*/
	
	var newValue = "";
	//	Allow free format masks - you can use S for a char string, N for a number and - for a dash
	for( i=0; i < value.length + 1; i++ ) {
		var tv = (i < value.length)?value.charAt(i).toUpperCase():'';
		
		if( forceMask.length >= i ) {
			var fm = forceMask[i];
			fm = fm.toUpperCase();	// IE sux!!!
			// Simply remove anything that does not match the mask.
			if( fm == 'S' ) {
				if( /[A-Z]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == 'N' ) {
				if( /[0-9]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == '-' ) {
				// if( tv == fm )newValue += value[i];
				// else newValue += '-';
				newValue += fm;
			}
		}
		else {
			if( fm == '-' )newValue += fm;
			//else newValue += ' ';
		}
	}
	
	/*
	for( i=0; i < forceMask.length; i++ ) {
		var fm = forceMask[i].toUpperCase();
		if( value.length >= i ) {
			var tv = value.charAt(i).toUpperCase();
			// Simply remove anything that does not match the mask.
			if( fm == 'S' ) {
				if( /[A-Z]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == 'N' ) {
				if( /[0-9]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == '-' ) {
				if( tv == fm )newValue += value[i];
				// else newValue += '-';
			}
		}
		else {
			if( fm == '-' )newValue += fm;
			//else newValue += ' ';
		}
	}
	*/
	
	/*
	WIP
	---
		Perhaps make the freemask a validator, instead of a stand-alone, which means we have a validator that can change the value (when applying the mask)
		


		//	This could work...!!! - Bind on "keypress" on the field
		- need to work out where inthe mask we are, and look at the field value too...

		function floatFilter(event) {
		    var keyPressed;
		    if (document.all) {
		        keyPressed = event.keyPressed; 
		    } else if (document.getElementById) {
		        keyPressed = event.which;   
		    } else if (document.layers) {
		        keyPressed = event.which;   
		    }

		    if (keyPressed >= 33 && keyPressed <= 43) {
		        return false;
		    } else if (keyPressed == 47) {
		        return false;
		    } else if (keyPressed >= 58 && keyPressed <= 126) {
		        return false;
		    } else {  
		        return true;     
		    }
		}



		
		
		
	*/

	return newValue;
};


//	TODO: We need a way to return the validation error message, see ipvalidator for an example of a message

//
//	Validates a field, and sets the surrounding countainer class.
//
pax.validate.validateField = function( e, field, useAjaxValidator ) {
	useAjaxValidator = ( typeof( useAjaxValidator ) != 'undefined' )? useAjaxValidator : true;
	if( !field )field = this;
	var fieldValid = true;

	if( field.mask ) {
		var fieldIsValid = true;
		//	Put ajax validations last, so that we don't try and run it if a client side validator fails first.
		//	Check if we have an array, or other sortable object.
		if( typeof( field.mask.sort ) == 'function' ) {
			field.mask.sort( function( a, b ) {
				var aMask = ( typeof(a.mask) != 'undefined' )? a.mask : a;
				var bMask = ( typeof(b.mask) != 'undefined' )? b.mask : b;
				if( ( aMask == 'ajaxValidate' && bMask == 'ajaxValidate' ) )return 0;
				else if( ( aMask == 'ajaxValidate' && bMask != 'ajaxValidate' ) )return 1;
				else return -1;
			} );
		}
		
		for( var msk in field.mask ) {
			// We assume an object here: { mask: 'MASK STRING', ATTRIBUTE1: VALUE, ATTRIBUTE@: VALUE }, eg: { mask: 'len', minLen: 3 }
			// Add the mask obj to the field
			var fieldMask = field.freeMask;
			if( typeof( field.mask[msk] ) == 'string' ) {
				if( field.mask[msk] == 'ajaxValidate' )fieldMask = field.mask;
				else fieldMask = { mask: field.mask[msk] };
			} else fieldMask = field.mask[msk];
	
			//	Check if we have a validator that matches specified mask
			var canValidate = false;
			for( var v in pax.validate.validators )if( v == fieldMask.mask )canValidate = true;
			
			if( canValidate ) {
				//	If we're using an ajax validator, we run just it, and return.
				//	This assumes all client side validation passed, as validators
				//	are sorted by clientside first, then ajax.
				if( fieldMask.mask == 'ajaxValidate' ) {
					//	Check if the ajax validation is in progress.
					if( useAjaxValidator ) {
						//	We only return if the ajaxvalidation returns true (ie: is in progress)
						if( pax.validate.validators[fieldMask.mask]( field, fieldMask ) )return false;
						//	Assume field is as per server response, or is not valid, if we don't have a response yet.
						if( pax.validate.ajaxValidatorQueue[field.formName] && pax.validate.ajaxValidatorQueue[field.formName][field.name] ) {
							fieldIsValid = pax.validate.ajaxValidatorQueue[field.formName][field.name]['validField'];
						} else fieldIsValid = false;
					}
				} else {
					// Run the validator, as long as we're not waiting for an ajax call.
					var postedAjax = false;
					if( typeof( pax.validate.ajaxValidatorQueue[field.formName] ) != 'undefined' && typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined') {
						if( pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] != false )postedAjax = true;
						else postedAjax = false;
					}
					
					if( ! postedAjax ) {
						fieldIsValid = true;
						if( !pax.validate.validators[fieldMask.mask]( field, fieldMask ) ) {
							fieldIsValid = false;
							break;
						}
					} else fieldIsValid = false;
				}
			} else console.log( "Validator [" + fieldMask.mask + "] not found for field [" + field.name + "]" );
		}
		if( ! fieldIsValid )fieldValid = false;
	}
	
	//	Allow free format masks - you can use S for a char string, N for a number and - for a dash
	//	See above TODO: create a mask enforcer.
	if( field.freemask ) {
		for( var fmsk in field.freemask ) {
			var tmpMask = field.freemask[fmsk];
			
			tmpMask = tmpMask.replace(/-/g,'\\-');
			tmpMask = tmpMask.replace(/S/g,'[A-Z]');
			tmpMask = tmpMask.replace(/N/g,'[0-9]');
			tmpMask = eval("/^" + tmpMask + "$/gi");
			if( !field.value.match( tmpMask ) )fieldValid = false;
			
			// We would run the value mask enforcer here...
			// TODO: maskEnforcer to be developed still...
			if( !fieldValid ) {
				// pax.$('debugBox').value = pax.validate.maskEnforcer( field.value, field.freemask );
				// field.value = pax.validate.maskEnforcer( field.value, field.freemask );
			}
		}
	}

	//	We always validate the field if there is no "notEmpty" validator.
	var hasNotEmptyValidator = pax.util.hasValue( 'notEmpty', field.mask );
	if( ! hasNotEmptyValidator ) {	// Check for list / object
		if( pax.util.getType( field.mask ) == 'array' ) {
			for( var m in field.mask ) {
				if( field.mask[m]['mask'] == 'notEmpty' )hasNotEmptyValidator = true;
			}
		} else if ( typeof( field.mask ) == typeof( {} ) ) {
			if( field.mask['mask'] == 'notEmpty' )hasNotEmptyValidator = true;
		}
	}
	
	if( ! hasNotEmptyValidator && field.value.length == 0 && field.tagName == 'INPUT' )fieldValid = true;

	var fieldList = [];
	
	// Check for radio button here.
	if( field.type == 'radio' ) {
		if( typeof field.formName != 'undefined' ) {
			if( field.formName ) {
				rfields = document.forms[field.formName][field.name];
				for( var r = 0; r < rfields.length; r++ ) {
					fieldList[fieldList.length] = rfields[r];
				}
			}
		}
	} else fieldList[fieldList.length] = field;
	
	for( var fi = 0; fi < fieldList.length; fi++ ) {
		fld = fieldList[fi];
		//	Set the class of the field's surround div, and the validity.
		if( fieldValid )pax.validate.setValidField( fld );
		else pax.validate.setInvalidField( fld );
	}

	//	Show error
	if( ! fieldValid ) {
		if( fieldMask ) {
			if( pax.util.hasKey( 'message', pax.validate.validators[fieldMask.mask] ) ) {
				var tpl = pax.validate.validators[fieldMask.mask]['message'][false];
				var errorMessage = field.name + ' ' + pax.template.parse( tpl, fieldMask ).html;
				pax.validate.showFieldError( field, errorMessage );
			}
		}
	}
	
	return fieldValid;
};


//	Sets a field as valid
pax.validate.setValidField = function( field ) {
	if( ! pax.util.hasClassName( field, pax.validate.fieldValidClass ) ) {
		pax.util.addClassName( field, pax.validate.fieldValidClass );
	}
	pax.util.removeClassName( field, pax.validate.fieldToBeClass );
	pax.util.removeClassName( field, pax.validate.fieldInvalidClass );
	field.valid = true;
};

//	Sets a field as invalid.
pax.validate.setInvalidField = function( field ) {
	if( ! pax.util.hasClassName( field, pax.validate.fieldInvalidClass ) ) {
		pax.util.addClassName( field, pax.validate.fieldInvalidClass );
	}
	pax.util.removeClassName( field, pax.validate.fieldValidClass );
	pax.util.removeClassName( field, pax.validate.fieldToBeClass );
	field.valid = false;
};

//	Sets a field as not validated yet: assumes it's not valid. Used for ajax validation
pax.validate.setTobeValidatedField = function( field ) {
	pax.util.removeClassName( field, pax.validate.fieldInvalidClass );
	pax.util.removeClassName( field, pax.validate.fieldValidClass );
	pax.util.addClassName( field, pax.validate.fieldToBeClass );
	field.valid = false;
};

//
//	This checks all fields, and sets focus on the first invalid field.
//	If the optional formName has been specified, it will only check the
//	fields in that form.
//
pax.validate.isFormValid = function( formName ) {
	if( typeof formName != 'undefined' )myFields = pax.validate.allFields( formName );
	else myFields = pax.validate.allFields();
	
	// for( var f in myFields ){
	for( var f = 0; f < myFields.length; f++ ) {
		field = myFields[f];
		pax.validate.validateField( null, field );	// Re-validate the field
		
		if( !field.valid == true ) {
			//	TODO: Be able to configure an alert or modal dialog here,
			//	TODO: Configure if we want ALL false fields mentioned instead of just the first one.
			//	TODO: Be able to use the hint instead of the generated error message (default behaviour?)
			
			var errorMessage = (field.hint)? field.hint: "Please enter a valid value for: " + field.name;
			
			if( pax.validate.showAlertOnError )alert( errorMessage );
			else {
				pax.validate.showFieldError( field, errorMessage );
			} if( ( typeof field.type != 'undefined' ) ) {
				field.focus();
			}
			return false;
		}
	}

	return true;
};

//
//	Returns an array of all currently generically supported fields: input, select, textearea.
//	the formName is optional, however it is required to get the radio validations to work.
//	If form name is not specified, all supported form fields in the DOM will be returned.
//
pax.validate.allFields = function( formName, includeHidden ) {
	myFields = ( typeof formName != 'undefined' )? document.forms[formName].getElementsByTagName('*'): document.getElementsByTagName('*');
	var fields = new Array();

	//	Note that doing a "for(f in selectBoxes)" does NOT return the same as the below 'for' loop.
	for( var i = 0; i < myFields.length; i++ ) {
		var field = myFields[i];
		var fieldType = ( typeof field.nodeName != 'undefined' )? field.nodeName.toLowerCase(): '';
		
		if( fieldType == 'input' || fieldType == 'textarea' || ( fieldType.indexOf( 'select' ) > -1 ) ) {
			//	do not handle fields without a name, or hidden fields
			if( field.name == '' || field.name == 'undefined' )continue;
			if( ! includeHidden )if( field.type == 'hidden' )continue;
			fLen = fields.length;
			fields[fLen] = field;
			fields[fLen].formName = ( typeof formName != 'undefined' )? formName : false;
		}
	}

	return fields;
};


/*	Method: pax.validate.initValidation
	Initialises all form variables, and sets validation events

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<label for="email">Email:</label>
					<input type="text" name="email">
				</span>
			</form>
			[:.
				valDict = {
					'email': { 
						mask: [
							{ mask: 'email' },
							{ mask: 'notEmpty' }
						],
						hint: 'The email address is mandatory, and must be a valid email address' 
					}
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the email field, and display a hint with the specified text.
	
*/
pax.validate.initValidation = function( formName, valDict ) {
	var myFields = ( typeof formName != 'undefined' )? pax.validate.allFields( formName ): pax.validate.allFields();
	var valDict = ( typeof valDict == 'undefined' )? false: valDict;
	var radioSet = [];

	if( valDict ) {
		// Iterate through the form fields, and set the mask, freemask, required and formName attributes.
		for( var f = 0; f < myFields.length; f++ ) {
			field = myFields[f];
			for( var key in valDict ) {
				if( key == field.name ) {
							
					field.formName = formName;
					// Set the attributes - Note: we need to just set the radio field on the first instance???
					if( field.type == 'radio' ) {
						var hasBeenSet = false;
						for( var rs = 0; rs < radioSet.length; rs++ )if( field.name == radioSet[rs] )hasBeenSet = true;
						if( !hasBeenSet )radioSet[radioSet.length] = field.name;
					}
					
					// Set the attributes from the validation dict
					var attribs = valDict[key];
					for( var att in attribs ) {
						// Check if we have a mask or freemask; they can be lists, so allow that.
						
						if( att == 'mask' || att == 'freemask' ) {
							if( pax.util.getType( attribs[att] ) == 'array' ) {
								field[att] = attribs[att];
							} else {
								// Insert a list, as expected by the validation method.
								if( ! field[att] )field[att] = [];
								field[att][field[att].length] = attribs[att];
							}
						} else field[att] = attribs[att];
						
						// Set the hints
						if( att == 'hint' ) {
							if( pax.validate.showHintOnFocus ) {
								pax.event.bind( field, 'focus', function(e){ pax.validate.showFieldMessage( this, this.hint ); } );
							}
							if( pax.validate.showHintOnHover ) {
								pax.event.bind( field, 'mouseover', function(e){ pax.validate.showFieldMessage( this, this.hint ); } );
							}
						}
					}
				}
			}
			
			// Check if we need to bind the validation methods - only include a field if we have at least one validation method
			if( field.mask || field.freemask ) {
				var fieldList = [];
				// Need to check for radio button here. If radio button, then loop on all the radio buttons with that name...
				if( field.type == 'radio' ) {
					if( typeof field.formName != 'undefined' ) {
						if( field.formName ) {
							rfields = document.forms[field.formName][field.name];
							for( var r = 0; r < rfields.length; r++ ) {
								fieldList[fieldList.length] = rfields[r];
								rfields[r].required = field.required;
								rfields[r].formName = field.formName;
								rfields[r].valid = false;
							}
						}
					}
				}
				else fieldList[fieldList.length] = field;

				for( var fi = 0; fi < fieldList.length; fi++ ) {
					var fld = fieldList[fi];
					
					// Add validation events
					pax.event.bind( fld, 'blur', 	function(e) { 
						pax.validate.validateField( e, this ) 
					} );
					
					pax.event.bindEventList( fld, ['change', 'paste', 'keyup', 'focus', 'click'], function(e) { pax.validate.validateField( e, this ) } );

					// Check if this is an ajaxvalidation
					for( var fm in fld.mask ) {
						var hasAjaxValidate = pax.util.hasKey( 'ajaxValidate', fld.mask[fm]['mask'] );
						if( ! hasAjaxValidate ) {	// Check for list / object
							if( pax.util.getType( fld.mask[fm] ) == 'array' ) {
								for( var m = 0; m < fld.mask[fm].length; m++ ) {
									if( fld.mask[fm][m]['mask'] == 'ajaxValidate' )hasAjaxValidate = true;
								}
							} else if ( pax.util.getType( fld.mask[fm] ) == 'object' ) {
								if( fld.mask[fm]['mask'] == 'ajaxValidate' )hasAjaxValidate = true;
							}
						}
					}

					if( hasAjaxValidate )pax.validate.validateField( false, fld, false );
					else pax.validate.validateField( false, fld );
				}
			}
			else pax.validate.setValidField( field );
		}
		if( pax.validate.preventSubmit ) {
			//	Bind the onsubmit to validate, and prevent submission if the form isn't valid.
			if( document.forms[field.formName] ) {
				pax.event.bind( document.forms[field.formName], 'submit', function(e) {
					if( ! pax.validate.isFormValid( formName ) ) {
						if( e.preventDefault )e.preventDefault();	//	Most browsers
						else e.returnValue = false;					//	IE
						return false;								//	Fallback?
					}
				} );
			}
		}
	}
};

/* ----------------------------------------------------------------------------

	pax.template.js Copyright (C) 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.template
		This PAX library can parse and render a PAX template
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
	Note:
		In IE we sometimes get: "Unkown runtime error...", when we try to write html that is partially rendered,
		such as a table part or a as list item without enclosing list start tags.
		The reason is that you can not write a partial table using innerHTML with IE. This is acknowledged by microsoft:
			http://support.microsoft.com/kb/239832
		The solution is to use proper DOM methods or write a full (valid) table, ie: ensure you have properly formed HTML...
*/

var pax = pax || {};
pax.template = pax.template || {};

/*	Property: sTag
		[: Start tag - executes inline JS code.	*/
pax.template.sTag = "[:";
/*	Property: eTag
		:] End tag.	*/
pax.template.eTag = ":]";
/*	Property: vTag
		[:= Value tag.	*/
pax.template.vTag = "[:=";
/*	Property: bTag
		[:_ Before tag - runs code before template rendering. Eg: for setting up an OBJ to be accessed during rendering.	*/
pax.template.bTag = "[:_";
/*	Property: aTag
		[:. After tag - runs code after template rendering. Eg: for unobtrusive JS.	*/
pax.template.aTag = "[:.";
/*	Property: pTag
		[:p Partial tag - signifies a part of a template that can be rendered partially.	*/
pax.template.pTag = "[:p";
//	The partial end tag is the only end tag that is different; you 
//	MUST be able to discern partial parts before parsing all other tags.
/*	Property: peTag
		p:] Partial end tag - End of partial template rendering.	*/
pax.template.peTag = "p:]";
/*	Private Property: ajaxTargetID
		Used to get a unique ajax template target id, ensuring loaded templates are rendered correctly.	*/
pax.template.ajaxTargetID = 0;


/*	Method: pax.template.render
	Renders the template with given value into the target container.
	
	Parameters:
		template - String with the contents of the template
		value - Object with values as required
		target - A DOM element to render the template into
		
	Example:
		(start code)
			<div id='pax.template.render.example1'></div>
			[:.
				var value = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var template = "[" + ":= greeting :" + "] [" + ":= name :" + "]!";
				pax.template.render( template, {
					value: value,
					target: pax.$('pax.template.render.example1') 
				} );
			:]
		(end)
		This will display "Hello World!" in the DIV. Note that the formatting is a bit funny, as we're using the PAX template system for this documentation as well.

	Example:
		(start code)
			<div id='pax.template.render.example2'></div>

			<button id='partial1'>Partial update 1</button>
			<button id='partial2'>Partial update 2</button>

			[:.	var myTemplate = "" +
					"[" + ":= greeting :" + "] [" + ":= name :" + "]!<br>" +
					"[" + ":p(template_stub)" +
					" Time stamp 1 is: [" + ":= time :" + "]" +
					"p:" + "]" +
					"<br>" +
					"[" + ":p(template_stub2)" +
					" Time stamp 2 is: [" + ":= time :" + "]" +
					"p:" + "] The end of the template." +
				"";

			    var myVars = {
			        greeting: 'Hello',
			        name: 'World',
					time: new Date().getTime()
			    };
				
				pax.template.render( myTemplate, {
					value: myVars, 
					target: pax.$('pax.template.render.example2') 
				} );
				
				pax.event.bindOne( pax.$('partial1'), 'click', function() {
					myVars.time = new Date().getTime();
					pax.template.render( myTemplate, { value: myVars, partial: 'template_stub' } );
				} );
				
				pax.event.bindOne( pax.$('partial2'), 'click', function() {
					myVars.time = new Date().getTime();
					pax.template.render( myTemplate, { value: myVars, partial: 'template_stub2' } );
				} );
			:]
		(end)
		This will display a sentence, and two buttons that can initiate a partial render of the template. Note that you MUST have a DOM element to render the template into, in order to use partial rendering.
		
*/
pax.template.render = function( template, args ) {
	args = pax.defaultArgs( {
		value: {},
		target: false,
		partial: []
	}, args );
	
	if( pax.util.getType( args.partial ) != 'array' )args.partial = [args.partial];
	
	if( args.partial.length > 0 ) {
		var result = pax.template.parse( template, args.value, args.partial );
			
		for( var p = 0; p < args.partial.length; p++ ) {
			pax.template.render( result[args.partial[p]], { 
				value: args.value, 
				target: pax.$(args.partial[p]) 
			} );
		}
	} else {
		var result = pax.template.parse( template, args.value );
		
		//	Note: see Comment a the start of this document if you're getting "Unkown runtime error..."
		if( args.target )args.target.innerHTML = result.html;
		else return result.html;
		
		for( var j in result.js )eval( result.js[j] );
	}
};



/*	Method: pax.template.parse
	Private method: Parses a template, and returns a dict with a HTML string, and a list with JS code to evaluate afterwards.
	You should only need to use <pax.render>, unless you're writing your own rendering method.
	
	Parameters:
		template - String with the contents of the template
		_ - Object with values as required
		
	Returns:
		{ html: string, [js1, js2, ..., jsX] }
		
	Note that this method has internal methods that you can use in a template:
		include( id ) - A method to get the value of the textarea with given id, and use it as a template
		load( url, target ) - A method to get a template from a url, and render it after the parent template has been rendered

	Example:
		(start code)
			<div id='pax.template.parse.example1'></div>
			[:.
				var myVars = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var result = pax.template.parse( "[" + ":= greeting :" + "] [" + ":= name :" + "]!", myVars );
				pax.$('pax.template.parse.example1').innerHTML = pax.util.toString( result );
			:]
		(end)
		This will set result as {"html":"Hello World!","js":[]}, and then display that in the DIV.
		
	Example:
		(start code)
			<div id='pax.template.parse.example2'></div>
			[:.
				var myVars = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var result = pax.template.parse( "[" + ":= greeting :" + "] [" + ":= name :" + "]! <span id='pax.template.parse.example2.update'></span>", myVars );
				pax.$('pax.template.parse.example2').innerHTML = pax.util.toString( result.html );
			:]
		(end)
		This will set result as {"html":"Hello World!","js":[]}, and then display that in the DIV.
*/
pax.template.parse = function( template, value, partial ) {
	template = template.replace(/[\n\r]/g,"");						// Remove new lines
	template = template.replace(/&lt;/g,"<").replace(/&gt;/g,">");	// Add angle brackets, as innerHTML automatically converts < to &lt; and > to &gt;

	//	Bring in the modifiers as localised variables, if necessary.
	if( ! (typeof _def === 'function') )pax.scope( pax.template.modifier );
	pax.scope( value );		//	Scope the value object

	// First we split by the partial tags, and re-create the template without partial tags in it.
	var partialBits = {};
	var partialResult = [];
	var endStr = template.split( pax.template.peTag );				//	split by partial end tag
	
	for( var s = 0; s < endStr.length; s++ ) {
		var sPartialTagIndex = endStr[s].indexOf( pax.template.pTag );
		if( sPartialTagIndex > -1 ) {			// 	If we have a start partial tag in this part
			var startStr = endStr[s].split( pax.template.pTag );	//	Split by start partial tag
			
			//	This assumes we have the partial tag here, in brackets, eg: "(partial_template_bit)"
			var partialFound = startStr[1].substring( 1, startStr[1].indexOf( ')' ) );
			
			//	We parse the partial string, so it can be unique in the DOM
			partialFound = pax.template.parse( partialFound ).html;
			var foundBit = startStr[1].substring( startStr[1].indexOf( ')' ) + 1, startStr[1].length );
			if( pax.util.hasValue( partialFound, partial ) ) {
				partialBits[partialFound] = foundBit;
			}
			
			partialResult.push( startStr[0] );
			partialResult.push( "<span id='" + partialFound + "'>" + foundBit + "</span>" );
		}
		else partialResult.push( endStr[s] );
	}

	//	 Dodgy - return something different, if we've specified a partial!
	if( typeof( partial ) != 'undefined' )return partialBits;
	
	template = partialResult.join('');

	// First we split by the tags
	var splitResult = [];
	var startStr = template.split( pax.template.sTag );				//	Split by start tag
	for( var s = 0; s < startStr.length; s++ ) {
		if( startStr[s].indexOf( pax.template.eTag ) > -1 ) {		// 	If we have an end tag
			var endStr = startStr[s].split( pax.template.eTag );	//	Split by end tag
			var addStart = true;
			for( var e = 0; e < endStr.length; e++ ) {
				//	Re-add the start tag for parts that is not plain HTML.
				splitResult.push( (addStart)? pax.template.sTag + endStr[e] : endStr[e] );
				addStart = false;
			}
		}
		else splitResult.push( startStr[s] );
	}

	// Next we push the parts in the result array, by tags
	var result = [];
	var runBefore = [];
	var runAfter = [];
	for( var r = 0; r < splitResult.length; r++ ) {
		var part = splitResult[r];
		if( part.indexOf( pax.template.vTag ) > -1 )		result.push( "parsedParts.push(" + part.replace( pax.template.vTag, '' ) + ");" );
		else if( part.indexOf( pax.template.aTag ) > -1 )	runAfter.push( part.replace( pax.template.aTag, '' ) );
		else if( part.indexOf( pax.template.bTag ) > -1 )	runBefore.push( part.replace( pax.template.bTag, '' ) );
		else if( part.indexOf( pax.template.sTag ) > -1 )	result.push( part.replace( pax.template.sTag, '' ) );
		else 												result.push( "parsedParts.push(\"" + part.replace(/\"/g,"\\\"") + "\");" );
	}
	
	result.push( "parsedParts.join('');" );
	
	//	Pushes a template into this template. Note: This function is local to the parse function and will override a global push function in the template scope only.
	var push = function( template, pushValue ) {



		//	NOTE: This may be the issue: parsing here would not necessarily preserve the values???

		var incResult = pax.template.parse( template, pushValue );
		
		
		
		

		//	This will push the value "in front" of the javascript part, thus preserving the values!
		//	Note: nodes or object pointers won't work, as we're serialising it...
		//	TODO: Don't serialise somehow, especially with nodes...
		//	for( var j in incResult.js )runAfter.push( "var value = " + pax.util.toString(pushValue) + ";" + incResult.js[j] );
		
		for( var j in incResult.js )runAfter.push( "var value = " + pax.util.toString(pushValue) + ";" + incResult.js[j] );
		
		// In IE, if you try to EVAL plain HTML, it errors (works ok in FF though). So, we add a list around it, and works!
		var incResValue = eval( [ incResult.html ] );
		
		return incResValue;
	};

	//	Includes a locally defined template (via innerHTML). Note: This function is local to the parse function and will override a global include function in the template scope only.
	var include = function( id ) {
		// var included = document.getElementById( id ).innerHTML;
		var included = document.getElementById( id ).value;
		var incResult = pax.template.parse( included, value );
		for( var j in incResult.js )runAfter.push( incResult.js[j] );
		// In IE, if you try to EVAL plain HTML, it errors (works ok in FF though). So, we add a list around it, and works!
		var incResValue = eval( [ incResult.html ] );
		return incResValue;
	};

	//	Loads a template via an ajax call, and renders it after parent template has been rendered.
	var load = function( url, target ) {
		pax.template.ajaxTargetID += 1;
		target = (typeof target != 'undefined')? target : url + '_target_' + pax.template.ajaxTargetID;	// Ensure we have a unique target
		runAfter.push( "pax.cache.template.loadTemplate( '"+url+"', false, function( templateUrl ) { pax.template.render( pax.cache.get( templateUrl ), { target: pax.$('"+target+"') } ) } );" );
		return "<span id='" + target + "'></span>";
	};
	
	var parsedParts = [];
	for( var j in runBefore )eval( runBefore[j] );
	
	/*	WARNING: This WILL cause 'invalid XML tag syntax' in FF, and "Operation Aborted" in IE, if result is invalid JS.

		. This can occur from unclosed closures, or other related problems.
		. Ie: this will occur if the result is not enclosed in "parsedParts.push(..." commands.
			-> simply output the result here, to see; basically we should have valid javascript here.
			-> firebug doesn't log the whole string, so you will need to use a textarea.
	*/
	var htmlResult = eval( result.join( '\n' ) );

	return { html: htmlResult, js: runAfter }
};
/* ----------------------------------------------------------------------------

	pax.template.modifier.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.template.modifier
		This PAX library contains a set of methods to be used in a PAX template
		to format template data, so that it appears uniform, eg: capitalise names.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

	Note: 
		You should note that each method from this library is imported "locally" 
		into each template, so that they can be referenced by their short name,
		for example "pax.template.modifier._def" can be referenced by just "_def".
		All PAX examples utalise this short name facility.
		Due to this facility, you must be careful when implementing your own 
		functions, so the names don't clash. 
		To make it easier, the template system will allow overriding of the methods,
		so that they have your expected functionality. This of course explains
		why each method starts with an underscore.
		
*/


var pax = pax || {};
pax.template = pax.template || {};
pax.template.modifier = pax.template.modifier || {};

/*	Method: _def
	Returns defaultValue, if value is null or empty, otherwise value.
	
	Parameters:
		value - which value to check
		defaultValue - which value to return, if value is null or empty

	Example:
		(start code)
			<div id='pax.template.modifier.def.example1'></div>
			[:.
				var myVars = {
					currency: '$',
					value: null 
				};
				pax.template.render( "You have [" + ":= currency :" + "][" + ":= _def( value, 0) :" + "] in your pocket.", myVars, pax.$('pax.template.modifier.def.example1') );
			:]
		(end)
		This ensures you get a default value of 0, even when values are null.
		
*/
pax.template.modifier._def = function( value, defaultValue ) {
	if( value == null )return defaultValue;
	return ( value != '' )? value : defaultValue;
};


/*	Method: _caps
	Returns string with first letter in uppercase, optionally converts the rest to lowercase.
	
	Parameters:
		value - which value to use
		restLower - boolean to decide if we should convert the rest of the string to lowercase

	Example:
		(start code)
			<div id='pax.template.modifier.capitalise.example1'></div>
			[:.
				var myVars = { name: 'john' };
				pax.template.render( "Hello [" + ":= _caps( name ) :" + "]!", myVars, pax.$('pax.template.modifier.capitalise.example1') );
			:]
		(end)
		This would show "Hello John!". Notice that "john" has been capitalised.

	Example:
		(start code)
			<div id='pax.template.modifier.capitalise.example2'></div>
			[:.
				var myVars = { name: 'joHnAtHaN' };
				pax.template.render( "Hello [" + ":= _caps( name, true ) :" + "]!", myVars, pax.$('pax.template.modifier.capitalise.example2') );
			:]
		(end)
		This would show "Hello Johnathan!". Notice that "joHnAtHaN" has been capitalised, and lower cased after the first letter.

*/
pax.template.modifier._caps = function( value, restLower ) {
	if( value == null )return value;
	if( value.length > 0 ) {
		var restOfValue = ( restLower )? value.substring(1).toLowerCase() : value.substring(1);
		return value.substring(0,1).toUpperCase() + restOfValue;
	}
	else return value;
};


/*	Method: _trunc
	Returns the first *length* characters of value
	
	Parameters:
		value - which value to use
		length - how many characters to return
		
	Example:
		(start code)
			<div id='pax.template.modifier.truncate.example1'></div>
			[:.
				var myVars = { 
					account: '0123456789012',
					bsb: 6
				};
				pax.template.render( "Your BSB is: [" + ":= _trunc( account,  bsb) :" + "].", myVars, pax.$('pax.template.modifier.truncate.example1') );
			:]
		(end)
		This would show "Your BSB is: 012345.".
*/
pax.template.modifier._trunc = function( value, length ) {
	if( value == null )return value;
	if( value.length > length )return value.substr(0, length);
	else return value;
};


/*	Method: _round
	Returns the value rounded to the specifed number of places
	
	Parameters:
		value - which value to use
		placs - how many decimal places to round to
		
	Example:
		(start code)
			<div id='pax.template.modifier.round.example1'></div>
			[:.
				var myVars = { 
					value: '256.187287',
					places: 2
				};
				pax.template.render( "Rounded value is: [" + ":= _round( value, places ) :" + "]", myVars, pax.$('pax.template.modifier.round.example1') );
			:]
		(end)
		This would show "Rounded value is: 256.19".
*/
pax.template.modifier._round = function( value, places ) {
	if( value == null )return value;
	return ( Math.round( value * ( 10 * places ) ) / ( 10 * places ) );
	
};


/*	Method: _indent
	Returns string with spaces at the front, optionally set number, and what string to use as seperator.
	
	Parameters:
		value - Which value to use
		number - How many spaces to insert
		string - What string to use for indentation

	Example:
		(start code)
			01234567890123456789
			<div id='pax.template.modifier.indent.example1'></div>
			[:.
				var myVars = { fruit: 'apples' };
				pax.template.render( "([" + ":= _indent( fruit, 7 ) :" + "])", myVars, pax.$('pax.template.modifier.indent.example1') );
			:]
		(end)
		This would show "(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;apples)", ie: adding 7 spaces before the fruit.

	Example:
		(start code)
			01234567890123456789
			<div id='pax.template.modifier.indent.example2'></div>
			[:.
				var myVars = { fruit: 'apples' };
				pax.template.render( "([" + ":= _indent( fruit, 1, 'I like ' ) :" + "])", myVars, pax.$('pax.template.modifier.indent.example2') );
			:]
		(end)
		This would show "(I like apples)", ie: adding the specified string 1 time before the fruit.

*/
pax.template.modifier._indent = function( value, number, string ) {
	if( value == null )return value;
	string = ( typeof string != 'undefined' )? string : "&nbsp;";
	number = ( typeof number != 'undefined' )? number : 4;
	return ( new Array( number + 1 ).join( string ) ) + value;
};



/*	Method: _undent
	Removes extra whitespace characters from a multi-line string
	
	Parameters:
		value - The string to use
		tabSpaces - Optionally specify how many spaces per tab, default = 4

	Example:
		(start code)
			var myCode = "
				for( var x = 0; x < 10; x ++ ) {\
					for( var y = 0; y < 10; y ++ ) {\
						result += x * y;\
					}\
				}\
			";
			<div id='pax.template.modifier.undent.example1'></div>
			[:.
				var myVars = { code: myCode };
				pax.template.render( "<xmp>[" + ":= _undent( code ) :" + "]</xmp>", myVars, pax.$('pax.template.modifier.undent.example1') );
			:]
		(end)
		This would show the code in the div, un-indented

*/
pax.template.modifier._undent = function( value, tabSpaces ) {
	if( ! tabSpaces )tabSpaces = 4;
	var lines = value.split( '\n' );	//	Split by new line char
	
	//	Find smallest indent
	var indent = 100000;
	for( var i = 0; i < lines.length; i++ ) {
		var line = lines[i];
		var count = 0;
		if( line != '' ) {
			var useLine = false;								//	We don't count lines that contain just whitespace
			for( var x = 0; x < line.length; x++ ) {
				var c = line.charAt( x );
				if( c == ' ' )count += 1;
				else if( c == '\t' )count += tabSpaces;			//	tab = X spaces
				else if( c != '' && c.charCodeAt(0) != 13 ) {	//	Not empty and not new line
					useLine = true;
					break;
				}
			}
			if( count < indent && useLine ) {
				indent = count;
			}
		}
	}
	
	//	Remove smallest indent from all lines
	var newValue = '';
	for( var i = 0; i < lines.length; i++ ) {
		var line = lines[i];
		var count = 0;
		var newLine = '';
		for( var x = 0; x < line.length; x++ ) {
			var c = line.charAt( x );
			if( c == ' ' )count += 1;
			else if( c == '\t' )count += tabSpaces;
			//	We check if either the indentation is right, or if we have non-whitepsace chars.
			//	Note: we can't just use count >= indent, as that yields the whitespace char, 
			//	When tabs and spaces are mixed.
			if( count > indent || ( c != ' ' && c != '\t' ) )newLine += c;
		}
		newValue += '\n' + newLine;
	}
	
	return newValue;
};


/*	Method: _trim
	Returns string with whitespace removed rom both the front and end of a string
	
	Parameters:
		value - Which value to use

	Example:
		(start code)			
			<div id='pax.template.modifier.trim.example1'></div>
			[:.
				var myVars = { value: '		  This is some text that has whitespace befor and after the text		    ' };
				pax.template.render( "([" + ":= _trim( value ) :" + "])", myVars, pax.$('pax.template.modifier.trim.example1') );
			:]
		(end)
		This would show "(This is some text that has whitespace befor and after the text)", ie: removing whitespace before and after the text

*/
pax.template.modifier._trim = function( value ) {
	return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
};


/*	Method: _wrap
	Wraps a line *value* of text at *max*, using *newLine*, optionally cutting through words (*cutWords*).
	
	Parameters:
		value - the string to wrap
		max - where to wrap lines at in number of characters
		newLine - string to use for seperating lines, defaults to "<br>"
		cutWords - boolean to allow cutting of words, false by default

	Example:
		(start code)
			<div>0--------10--------20--------30--------40</div>
			<div>|123456789|123456789|123456789|123456789|</div>
			<div>'----'----'----'----'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example1'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20
				};
				pax.template.render( "[" + ":= _wrap( text, max ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example1') );
			:]
		(end)
		This would show the line wrapped at 20 chars, without cutting any long words.

	Example:
		(start code)
			<div>0--------10--------20--------30--------40--------50--------60--------70--------80--------90-------100-------110</div>
			<div>|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|</div>
			<div>'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example2'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20,
					newLine: '|'
				};
				pax.template.render( "[" + ":= _wrap( text, max, newLine ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example2') );
			:]
		(end)
		This would show the line seperated at 20 chars by the specified seperator "|", without cutting any long words.

	Example:
		(start code)
			<div>0--------10--------20</div>
			<div>|123456789|123456789|</div>
			<div>'----'----'----'----'</div>
			<div id='pax.template.modifier.wrap.example3'></div>
			[:.
				var myVars = {
					text: 'Hi there, I am a line of text I need my words, such as supercalafragalisticexpialidocious wrapped asap!.',
					max: 20,
					newLine: '<br' + '>',
					cutWords: true
				};
				pax.template.render( "[" + ":= _wrap( text, max, newLine, cutWords ) :" + "]", myVars, pax.$('pax.template.modifier.wrap.example3') );
			:]
		(end)
		This would show the line wrapped at 20 chars, but with words longer than 20 chars cut.

*/
pax.template.modifier._wrap = function( value, max, newLine, cutWords ) {
	if( value == null )return value;
	var seperator = " ";												// The seperator, usually space
	cutWords = (typeof(cutWords) != 'undefined') ? cutWords : false;	// Default to not cut the words
	newLine = ( typeof newLine != 'undefined' )? newLine : "<br>";
	var lines = value.split( newLine );
	if( max > 0 ) {
		for( var i = 0; i < lines.length; i++ ) {
			var line = lines[i];
			lines[i] = "";
			while( line.length > max ) {
				// Work out where to cut: it's at the last space if that occurs before max, otherwise it is max if the seperators is found, otherwise length of line.
				var cutAt = ( line.substring(0,max).lastIndexOf( seperator ) == -1 ) ? line.indexOf( seperator ) : line.substring(0,max).lastIndexOf( seperator ) + 1;
				if( cutAt == -1 )cutAt = line.length;
				if( cutAt < 1 || cutWords )cutAt = max;
				
				// Add the bits back into the line
				lines[i] += line.substr(0, cutAt);
				line = line.substr( cutAt );
				lines[i] += (line.length)? newLine : "";
			}
			lines[i] += line;
		}
	}
    return lines.join( newLine );
};
/* ----------------------------------------------------------------------------

	pax.widget.js Copyright (C) 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.widget
		Base widget class for all PAX widgets
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.ajax = pax.widget.ajax || {};

/*	Property: pax.widget.uniqueId
		Unique identifiers counter for widgets	*/
pax.widget.uniqueId = 0;


/*	Method: pax.widget.init
	Initialises a PAX widget
	
	Parameters:
		args - Object with widget functionality
		* model - Model used by the associated widget template
		* paxWidgetId - Optionally specify a unique ID that can be used to identify an instance of a widget in the DOM (This is auto generated if not specified)
		* template - PAX Template (string) to render
		* target - Element to output the widget to
		
	Returns:
		Object with model and various widget function.
		
	Example:
		(start code)
			<div style='border: 1px solid grey' id='widget_output'></div>
			[:.
				var myWidget = pax.widget.init( {
					model: {
						text: 'Hello, this is my first widget!'
					},
					template: 'Your text is: "[:= text :]".',
					target: pax.$('widget_output')
				} );
				
				myWidget.render();
			:]
		(end)
	This would show the template rendered in the target. See <pax.template> for template details.

*/
pax.widget.init = function( args ) {
	pax.widget.uniqueId += 1;	// The unique ID can be used to identify this widget

	var myWidget = pax.defaultArgs( {						// Widget object default required args
		model: {},											// Model used by the template
		paxWidgetId: 'paxWidget' + pax.widget.uniqueId,		// Unique ID you can optionally used to identify an instance of a widget in the DOM
		template: 'empty template',							// PAX Template (string form) to render
		templateURL: '',									// Optional URL to load a template from
		target: document.body,								// Bad, but what else could we use? At least it will be noticed ;o)
		bindQueue: [],										// Queue for the bound objects.
		widgetQueue: [],									// Queue for widgets used (so we can call destroy() on them)
		bindElements: {},									// Elements we want to bind using the widget binding methods
		
		//	Keeps track of widgets that are used within this widget.
		useWidget: function( widget ) {
			this.widgetQueue.push( widget );
		},

		//	Adds a bound object to the bind queue
		addToBindQueue: function( bindObj ) {
			this.bindQueue.push( bindObj );
		},

		//	Removes a bound object from the bind queue
		removeFromBindQueue: function( bindObj ) {
			for( var i = 0; i < this.bindQueue.length; i++ ) {
				if( this.bindQueue[i].obj == bindObj.obj && this.bindQueue[i].event == bindObj.event )this.bindQueue.splice(i, 1);
			}
		},

		//	Pass-through method for bind, we keep a seperate queue for this widget.
		bind: function( obj, event, func ) {
			this.addToBindQueue( pax.event.bind( obj, event, func ) );
		},

		//	Pass-through method for unbind, remove bound obj from queue for this widget.
		unbind: function( obj, event, func ) {
			this.removeFromBindQueue( { obj: obj, event: event, func: func } );
			pax.event.unbind( obj, event, func );
		},

		//	Pass-through method for bindOne
		bindOne: function( obj, event, func ) {
			this.addToBindQueue( pax.event.bindOne( obj, event, func ) );
		},

		onBeforeDestroy: function( widget) {	// You can override this to do *something* before destroy is executed.
		},

		_destroy: function() {	// Cleans up the DOM events, and destroys any registered widgets. If you override this, don't forget to run onBeforeDestroy
			this.onBeforeDestroy( this );
			for( var w = 0; w < this.widgetQueue.length; w++ )this.widgetQueue[w].destroy();
			for( var b = 0; b < this.bindQueue.length; b++ )this.unbind( this.bindQueue[b].obj, this.bindQueue[b].event, this.bindQueue[b].func );
			pax.event.unbind( this );
		},
		
		//	You can override this method with your own destroy, just remember to call _destroy somewhere
		destroy: function() {
			this._destroy();
		},

		getElements: function( target, selector, className ) {
			//	We could optimise here, by caching all elements before traversing
			return pax.util.getElementsByClassName( target, selector, className );
		},
		
		/*	Method: bindElements.classNames
			This will bind functions, by using the class names that you specify in the <pax.widget.init> method, through the bindElements object
			
			Parameters:
				args - Object with CSS class name as key, and functions as values
				
			Returns:
				Not applicable
				
			Note:
				This is an internal method to the pax.widget system, and can only be accessed through the init methods.
			
			Example:
				(start code)
					pax.widget.init(
						target: target, 
						model: model, 
						template: args.template,
						bindElements: {
							classNames: {
								'backButtonCSSClass': {
									click: function( e ) {
										if( model.offset > 0 ) {
											model.offset -= 1;
											model.widget.render();
										}
									}
								}
							}
						}
					);
				(end)
				This example shows how to access the classNames binding through the <pax.widget.init> method. 'backButtonCSSClass' is the CSS class on 
				the button you want to attach the events to, in this case we have a 'click' event to attach to the button.
		*/
		bindClassNames: function( target, args ) {
			for( var className in args ) {
				//	This is VERY slow in IE, for a large set of elements, use the bindOne pattern method instead
				var elements = this.getElements( target, '*', className );
				
				for( var i = 0; i < elements.length; i++ ) {
					var item = elements[i];
					var funcOnly = ( typeof(args[className]) == 'function' );
					// if( funcOnly )this.bindQueue.push( pax.event.bindOne( item, 'click', args[className] ) );
					if( funcOnly )this.bindOne( item, 'click', args[className] );
					else {
						for( var evt in args[className] ) {
							if( typeof( args[className][evt] ) == 'function' )this.bindOne( item, evt, args[className][evt] );
						}
					}
				}
			}
		},
		
		unbindClassNames: function( target, args ) {	// This unbinds functions, by using the class names
			for( var className in args ) {
				var elements = this.getElements( target, '*', className );
				for( var i = 0; i < elements.length; i++ ) {
					var item = elements[i];
					for( var evt in args[className] ) {
						//	pax.event.unbind( item, evt, args[className][evt] );
						this.unbind( item, evt, args[className][evt] );
					}
				}
			}
		},
		
		
		/*	Method: bindElements.id
			This will bind functions, by using the element ids that you specify in the <pax.widget.init> method, through the bindElements object
			
			Parameters:
				args - Object with element ids as keys, and functions as values
				
			Returns:
				Not applicable
				
			Note:
				This is an internal method to the pax.widget system, and can only be accessed through the init methods.
			
			Example:
				(start code)
					pax.widget.init(
						target: target, 
						model: model, 
						template: args.template,
						bindElements: {
							id: {
								'myButtonID': {
									click: function( e ) {
										alert('You clicked on the button!');
									}
								}
							}
						}
					);
				(end)
				This example shows how to access the id binding through the <pax.widget.init> method. 'myButtonID' is the element ID of the button you 
				want to attach the events to, in this case we have a 'click' event to attach to the button.
		*/
		bindIds: function( target, args ) {			// This binds functions by using element IDs
			for( var id in args ) {
				for( var evt in args[id] ) {
					this.bind( pax.$(id), evt, args[id][evt] );
				}
			}
		},
		unbindIds: function( target, args ) {			// This unbinds functions by using element IDs
			for( var id in args ) {
				for( var evt in args[id] ) {
					this.unbind( pax.$(id), evt, args[id][evt] );
				}
			}
		},
		
		/*	Method: bindElements.internal
			This will bind functions, by using the element id + _target id that you specify in the <pax.widget.init> method, through the bindElements object.
			The purpose of this method is to semi-automatically provide a way to have unique elements for instances of the same widget.
			
			Parameters:
				args - Object with element id as keys, and functions as values
				
			Returns:
				Not applicable
				
			Note:
				This is an internal method to the pax.widget system, and can only be accessed through the init methods.
			
			Example:
				(start code)
					//	Assume that you have a widget where you have defined a button in the template like so:
					//		<input type='button' id='forward_[:= model.target.id :]' value = 'Forward'/>
					pax.widget.init(
						target: target, 
						model: model, 
						template: args.template,
						bindElements: {
							internal: {
								'forward': {
									click: function( e ) {
										alert('You clicked on the forward button!');
									}
								}
							}
						}
					);
				(end)
				This example shows how to access the internal binding through the <pax.widget.init> method. The 'forward' key referes to an element that is 
				defined as "forward_" + model.target.id
		*/
		internalBindIds: function( target, args ) {	// This binds functions by using element IDs post-fixed with '_' + target.id
			for( var id in args ) {
				for( var evt in args[id] ) {
					if( pax.$(id + '_' + target.id) ) {
						this.bind( pax.$(id + '_' + target.id), evt, args[id][evt] );
					}
				}
			}
		},
		internalUnbindIds: function( target, args ) {	// This unbinds functions by using element IDs post-fixed with '_' + target.id
			for( var id in args ) {
				for( var evt in args[id] ) {
					if( pax.$(id + '_' + target.id) )this.unbind( pax.$(id + '_' + target.id), evt, args[id][evt] );
				}
			}
		},
		bindOneEvent: function( target, args ) {			// Binds one function to the target, for each event type in the args. This is MUCH faster if we have a lot of classes
			var evtDict = {};
			
			// Create dict with event as key, and value is an object with className -> function to run.
			for( var className in args ) {
				for( var evt in args[className] ) {
					evtDict[evt] = (typeof(evtDict[evt]) == 'undefined' )? {}: evtDict[evt];
					evtDict[evt][className] = args[className][evt];
				}
			}
			
			// Bind the events
			for( var elementEvt in evtDict ) {
				if( elementEvt != 'prototype' ) {
					this.bindOne( target, elementEvt, function(e) {
						if( pax.util.hasKey( e.type, evtDict ) ) {
							for( var className in evtDict[e.type] ) {
								var element = e.target || window.event.srcElement;
								/*
									We don't get event bubbeling here, as we have captured the click event...
										- This means we only get the inner most click element...
										- We must handle that ourselves, as we have over-ridden the event management
										- Perhaps iterate through all parent elements of the target element?
											. This *may* need to be reursive, though element.parentElements should be enough...
								*/
								// Loop on element.parentNode until we reach target
								var foundElement;
								while( element != target ) {
									var elx = pax.$( element );
									foundElement = false;
									//	Check element first
									if( pax.util.hasClassName( elx, className ) ) {
										//	Run the function, with the orignal event, and the target we found
										evtDict[e.type][className].apply( elx, [e, elx] );
										foundElement = true;
										break;
									}
										
									//	Then check child nodes
									for( var x = 0; x < element.childNodes.length; x++ ) {
										var elx = pax.$( element.childNodes[x] );
										if( pax.util.hasClassName( elx, className ) ) {
											//	Run the function, with the orignal event, and the target we found
											evtDict[e.type][className].apply( elx, [e, elx] );
											foundElement = true;
											break;
										}
										if( foundElement )break;
									}
									if( foundElement )break;
									element = element.parentNode;
								}
							}
						}
					} );
				}
			}
		},
		
		render: function() {
			//	Check if template is a function, and run it first, if so...
			var tpl = ( pax.util.getType( this.template ) == 'function' )? this.template(): this.template;
			if( pax.util.getType( this.beforeRender ) == 'function' )this.beforeRender( this );
			
			//	Automatically unbind events
			this.internalUnbindIds( this.model.target, this.bindElements.internal );
			this.unbindClassNames( this.model.target, this.bindElements.className );
			this.unbindIds( this.model.target, this.bindElements.id );
			
			pax.template.render( tpl, { target: this.target, value: this.model } );
			
			if( pax.util.getType( this.afterRender ) == 'function' )this.afterRender( this );
			
			//	Automatically bind events
			this.internalBindIds( this.model.target, this.bindElements.internal );
			this.bindClassNames( this.model.target, this.bindElements.className );
			this.bindIds( this.model.target, this.bindElements.id );
		}
	}, args );
	
	//	Internal reference
	myWidget.model.widget = myWidget;
	
	return myWidget;
};



//	Returns an internal element.
pax.widget.getInternalElement = function(id, target) {
	return pax.$(id + '_' + target.id);
};

//	Returns an internal ID.
//	TODO: Allow using an internal pax ID, if the target doesn't have an ID.
pax.widget.getInternalID = function(id, target) {
	return id + '_' + target.id;
};


/*	"behaviour" style init, useful for initting by setting a class on elements, and then runnig this after load.	*/
pax.widget.initByClassName = function( element, elementType, className, model ) {
	// var elements = pax.util.getElementsByClassName( element, elementType, className );
	var elements = this.getElements( element, elementType, className );
	for( var i = 0; i < elements.length; i++ ) {
		pax.widget.init( { target: elements[i], model: model } );
	};
};


/*	Method: pax.widget.ajax.init
	This creates an ajax based widget based on the widget object. ie: it adds a controller, and a server request method.
*/
/*	Method: pax.widget.ajax.init
	This creates an ajax based widget, using pax.widget.init as a base widget object. ie: it adds an ajax controller, and a server request method.
	
	Parameters:
		args - Object with widget functionality
		* url - The URL that the widget sends it's requests to.
		* model - Model used by the associated widget template
		* paxWidgetId - Optionally specify a unique ID that can be used to identify an instance of a widget in the DOM (This is auto generated if not specified)
		* template - PAX Template (string) to render
		* templateURL - Optional URL to load a template from
		* target - Element to output the widget to.
		
	Returns:
		Object with model and various widget function.
		
	Example:
		(start code)
			<div style='border: 1px solid grey' id='ajax_widget_output'></div>
			[:.
				pax.widget.ajax.init( '../examples/widget_introduction_example3.txt', {
				    target: pax.$('ajax_widget_output')
				} ).serverRequest();
			:]
		(end)
	This would show the template rendered in the target. See <pax.template> for template details.
	Also, be sure to read the widget introduction article!
*/
pax.widget.ajax.init = function( url, args ) {
	//	Create widget object, and extend with args and default ajax functionality
	//	var myAjaxWidget = pax.defAddArgs( pax.widget.init( args ), pax.defaultArgs( {
	var myAjaxWidget = pax.defAddArgs( pax.widget.init( args ), pax.defAddArgs( {
		url: url,						// Data loading URL
		model: { param: {} },			// Parameters to pass to the url
		serverRequest: function() {		// This method sends a request to the server, using this.param as the post string
			if( this.templateURL != '' ) {
				var loadData = function() {
					pax.post( this.url, pax.postString( this.model.param ), this.controller, 'pax.widget.serverRequest posted' );
				};
				pax.cache.template.loadTemplate( this.templateURL, false, loadData );
			} else {
				pax.post( this.url, pax.postString( this.model.param ), this.controller, 'pax.widget.serverRequest posted' );
			}
		},
		controller: function( xml, txt, url ) {									// This is the default callback handle function
			var response = pax.unJSON( txt );									//	Unmarshal the response
			var template = args.template || "[:= response :]";					//	Default template
			var value = pax.defAddArgs( { response: response }, myAjaxWidget );	//	Add in the widget
			if( response && response != '' ) {									//	Show response, by rendering template
			
				if( pax.util.getType( myAjaxWidget.beforeRender ) == 'function' )myAjaxWidget.beforeRender( myAjaxWidget );
				
				//	Automatically unbind events
				myAjaxWidget.internalUnbindIds( myAjaxWidget.model.target, myAjaxWidget.bindElements.internal );
				myAjaxWidget.unbindClassNames( myAjaxWidget.model.target, myAjaxWidget.bindElements.className );
				myAjaxWidget.unbindIds( myAjaxWidget.model.target, myAjaxWidget.bindElements.id );
				
				pax.template.render( template, {
					value: value,
					target: args.target 
				} );
				
				if( pax.util.getType( myAjaxWidget.afterRender ) == 'function' )myAjaxWidget.afterRender( myAjaxWidget );
				
				//	Automatically bind events
				myAjaxWidget.internalBindIds( myAjaxWidget.model.target, myAjaxWidget.bindElements.internal );
				myAjaxWidget.bindClassNames( myAjaxWidget.model.target, myAjaxWidget.bindElements.className );
				myAjaxWidget.bindIds( myAjaxWidget.model.target, myAjaxWidget.bindElements.id );
				
			}
		}
	}, args ) );
	
	//	Internal reference
	myAjaxWidget.model.widget = myAjaxWidget;
	
	return  myAjaxWidget;

};
/* ----------------------------------------------------------------------------

	pax.widget.hint.js Copyright (C) 2009 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.widget.hint
		This is a hint widget, that shows hints (or "tool tips") near elements
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.hint = pax.widget.hint || {};

/*	Method: pax.widget.hint.init
	Shows hints at the given targets
	
	Parameters:		
		target - an element to render the hint into
		args - arguments for the grid widget
		* target - Element to show the hint near, you MUST supply this.
		* message -  Hint message
		* messageAttribute - If we want to use a specific attribute for the message text, eg: alt or title
		* hintClass - The CSS class name, default is 'hintMessage'
		* width - Width of the hint in pixels, default is 120
		* height - Height of the hint, leave this empty, for height to automatically fit the content
		* x - How many pixels to offset the hint by, horizontally from the element
		* y - How many pixels to offset the hint by, vertically from the elements right side
		* opacity - Opacity in percent, default is 100
		* clickToHide - Do we wait for mouse out of the element, or a click on the hint? Default is false
		* dontHideOnHover - Do we keep showing the hint until you mouse out?
		* showDelay - Delay in milliseconds to wait till we show the hint on hover, default is null
		* hideDelay - Delay in milliseconds to wait till we hide the hint on mouseout, default is 20
		* id - Optional ID of hint box, default is auto generated
		* showFunc - Optional function to show the hint with, see <pax.fx> for functions.
		* hideFunc - Optional function to hide the hint with, see <pax.fx> for functions.

	Returns:
		Element of the hint

	Example:
		(start code)
			<a href='#' id='widget.hint.init.example1'>Hover me for hint</a>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example1': 'testing 123'
				} );
			:]
		(end)
		This example shows a simple hint
		
	Example:
		(start code)
			<a href='#' id='widget.hint.init.example2'>Hover me for hint that fades in and out, with a delay</a>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example2': {
						showFunc: pax.fx.fadeIn,
						hideFunc: pax.fx.fadeOut,
						opacity: 0,
						message: 'testing 123<br>new line',
						showDelay: 300,
						hideDelay: 2000
					}
				} );
			:]
		(end)
		This example shows a hint that fades in and out. Note the use of "opacity: 0", as that is required for the fadein.
		
	Example:
		(start code)
			<a href='#' id='widget.hint.init.example3'>Hover me for hint that fades in and out, with a delay</a>
			<img src='http://paxjs.com/resource/logo.png' id='widget.hint.init.example3.img'/>
			[:.
				pax.widget.hint.init( {
					'widget.hint.init.example2': {
						message: 'testing 123<br>new line',
						showFunc: null,
						showDelay: null
					}, 
					'widget.hint.init.example3.img': 'This is a message from an image hover'
				}, {
					showFunc: pax.fx.fadeIn,
					hideFunc: pax.fx.fadeOut,
					opacity: 0,
					showDelay: 300,
					hideDelay: 2000
				} );
			:]
		(end)
		This example shows two hints that fade in and out. We are using the defaultModel feature, to allow the hints to share the configuration. 
		Note that you can override the default configuration parameters, by specifying them within each hint, as done on the first hint.

	Example:
		(start code)
			<a href='#' id='widget.hint.init.example4'>Hover me for hint initialised from the title attribute</a>
			[:.
				pax.widget.hint.init( ['widget.hint.init.example4'] } );
			:]
		(end)
		This example shows a hint that uses the title attribute for the message, this allows fallback if JS is not available.
		Note that you can set which attribute to use, by specifying a messageAttribute in the configuration
		
*/
pax.widget.hint.init = function( args, defaultModel ) {
	//	Iterate on arguments, and initialise hints
	var isList = (pax.util.getType( args ) == 'array');
	for( var target in args ) {
		var argument = args[target];
		var model = {};
		target = ( isList )? argument: target;
		
		if( typeof( argument ) == 'string' ) {
			//	If we are using a list, get the message from the title or alt attribute
			model = pax.widget.hint.model( (defaultModel)? defaultModel: {} );
			//	Default to using the title attribute
			if( isList && model['messageAttribute'] == null ) {
				model['messageAttribute'] = 'title';
			} else {
				if( model['messageAttribute'] )model['messageAttribute'] = model['messageAttribute'].toLowerCase();
			}
			var message = ( model['messageAttribute'] )? pax.$(target).getAttribute( model['messageAttribute'] ): argument;
			
			model['message'] = message;
		} else {
			//	Use the argument as the model
			if( defaultModel ) {
				model = pax.widget.hint.model( defaultModel );
				model = pax.defaultArgs( model, argument );
			} else {
				model = pax.widget.hint.model( argument );
			}
			
			if( model['messageAttribute'] ) {
				model['messageAttribute'] = model['messageAttribute'].toLowerCase();
				model['message'] = pax.$(target).getAttribute( model['messageAttribute'] );
			}
			
		}
		//	We always use the key as the target
		model['target'] = target;
		
		pax.widget.hint.initHint( model );
	}
};


/*	Private Method: pax.widget.hint.initHint
	This function shows a hint
*/
pax.widget.hint.initHint = function( args ) {
	var hintBox = null;
	var hintId = ( args.id )? args.id: pax.getNextId();
	var target = pax.$( args.target );
	if( args['messageAttribute'] != null ) {
		//	Note: IE need the attribute name in lowercase; all other browsers seem to work with this too.
		target.setAttribute( args['messageAttribute'].toLowerCase(), '' );
	}

	if( pax.util.getType( args.events ) == 'string' )args.events = [args.events]; 

	pax.event.bindEventList( target, args.events, function() {
		//	Internal function that shows the hint
		function showHint() {
			//	Cancel any effects on the hint
			if( args.hideFunc || args.showFunc )pax.fx.cancelAll( hintId );
			window.clearTimeout( target.hideDelayTimeout );
		
			var dims = pax.util.getPosition( target );
			//	If there is a showFunc, we hide the box
			var displayStyle = ( args.showFunc )? 'none': 'block';
			hintBox = pax.box.show( hintId, args.message, args.hintClass, dims.x + dims.width + args.x, dims.y + args.y, document.body, displayStyle );
			
			pax.css.opacity( hintBox, args.opacity );
			
			if( args.width != '' || args.height != '' ) {
				pax.util.setPosition( hintBox, {
					width: (args.width)? args.width: '',
					height: (args.height)? args.height: ''
				} );
			}
			
			//	Allow clicking anywhere on the hint, to make it disappear
			if( args.clickToHide ) {
				pax.event.bind( hintBox, 'click', function() {
					pax.box.hide( hintBox, args.hideFunc, function() {
						pax.event.unbind( hintBox );
						pax.box.destroy( hintBox );
					} );
				} );
			}
		
			if( args.dontHideOnHover ) {
				//	Bind hint mouse over function
				pax.event.bindOne( hintBox, 'mouseover', function() {
					//	Clear the hide event timer
					window.clearTimeout( target.hideDelayTimeout );
					//	Attach mouseout function
					pax.event.bindOne( hintBox, 'mouseout', function() {
						window.clearTimeout( target.showDelayTimeout );
						window.clearTimeout( target.hideDelayTimeout );
						target.hideDelayTimeout = window.setTimeout( function() {
							pax.box.hide( hintBox, args.hideFunc, function() {
								pax.event.unbind( hintBox );
								pax.box.destroy( hintBox );
							} );
						}, args.hideDelay );
					} );
				} );
			}
			
			//	Apply the show func; we start the function, then set the display style to block
			if( args.showFunc ) {
				var id = (typeof(hintBox.id) != 'undefined' )? hintBox.id: hintBox;
				args.showFunc( id );
				hintBox.style.display = 'block';
			}
		}
		
		window.clearTimeout( target.hideDelayTimeout );
		
		if( args.showDelay ) {
			window.clearTimeout( target.showDelayTimeout );
			target.showDelayTimeout = window.setTimeout( function() { showHint(); }, args.showDelay );
		} else {
			showHint();
		}
	} );
	
	//	Don't add a mouseout, if we are using a click function to hide the hint
	if( ! args.clickToHide ) {
		//	Bind element mouse out function
		pax.event.bind( target, 'mouseout', function() {
			window.clearTimeout( target.showDelayTimeout );
			window.clearTimeout( target.hideDelayTimeout );
			target.hideDelayTimeout = window.setTimeout( function() {
				if( args.hideFunc || args.showFunc )pax.fx.cancelAll( hintId );
				pax.box.hide( hintBox, args.hideFunc, function() {
					pax.event.unbind( hintBox );
					pax.box.destroy( hintBox );
				} );
			}, args.hideDelay );
		} );
	}
};

/*	Private Method: pax.widget.hint.model
	Setup the default config values for this widget
*/
pax.widget.hint.model = function( args ) {
	
	return pax.defaultArgs( {
		target: null,				//	Element to show the hint near, you MUST supply this.
		message: null,		 		//	Hint message
		messageAttribute: null,		//	If we want to use a specific attribute for the message text, eg: alt or title
		hintClass: 'hintMessage',	//	The default base class name, for applying other than the default style
		width: 120,					//	Width of the hint in pixels
		height: '',					//	Height of the hint - leave this empty, for height as big as the content
		x: 5,						//	X Offset in pixels for the hint
		y: 5,						//	Y Offset in pixels for the hint
		opacity: 100,				//	Opacity in percent
		events: ['mouseover'],		//	Which event(s) to show the hint on
		clickToHide: false,			//	Do we wait for mouse out of the element, or a click on the hint?
		dontHideOnHover: false,		//	Do we wait for mouse out of the hint?
		showDelay: null,			//	Delay in milliseconds to wait till we show the hint on hover
		hideDelay: 20,				//	Delay in milliseconds to wait till we hide the hint on mouseout
		id: '',						//	Optional ID of hint box (it will be auto generated otherwise)
		showFunc: null,				//	Optional function to show the hint with
		hideFunc: null				//	Optional function to hide the hint with
	}, args );
};
/* ----------------------------------------------------------------------------

	pax.widget.modal.js Copyright (C) 2008, 2009 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.widget.modal
		Modal widget
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.modal = pax.widget.modal || {};

//      IE6 hack value container, so we can reset values after hack is finished.
pax.widget.modal.ie6SelectHackValues = {};


/*
	WIP
	---

	[done]	. Create a default surround template, and allow overriding in the init.
	. Add a loader class
	. Create simple slideshow functionality, then a plugin for the detailed implementation
	. Allow using existing markup, eg like here:

		http://colorpowered.com/colorbox/

*/
pax.widget.modal.model = function() {
	var model = {
		message: '<a href="javascript:pax.widget.modal.hide();">Close</a>No message specified.',
		image: null,		//	Image parameter
		url: null,			//	Ajax parameter
		src: null,			//	iFrame parameter
		width: 400,			//	Width in pixels
		height: 300,		//	Height in pixels
		fitViewport: true,	//	Do we shrink the modal to fit, if the viewport is too small
		footerHeight: 20,	//	Height of footer in pixels
		showFooter: true,	//	Show the footer
		iFrame: {			//	iFrame properties
			scrolling: 'auto',
			frameborder: '0',
			hspace: '0',
			src: 'javascript:false;',
			id: 'paxModalBoxIframe',
			name: null,
			width: 400,
			height: 300
		},
		template: pax.widget.modal.template()
	};
	
	return model;
};

//      initialise the modal box
pax.widget.modal.init = function( model ) {
	model = pax.defaultArgs( pax.widget.modal.model(), model );
	var element = pax.$( 'paxModalBox' );
	if( ! element ) {
		element = pax.util.genElement( 'DIV', { id: 'paxModalBox' } );
		document.body.appendChild( element );
	}

	var offsetLeft = 0;
	var offsetTop = 0;
	
	//	//	For ie6, we add the scroll offsets
	//	if( pax.isIe6Down ) {
		//var offsetLeft = parseInt( document.documentElement.scrollLeft, 10 );
		//var offsetTop = parseInt( document.documentElement.scrollTop, 10 );
	//	}
	
	//	Adjust height and width if necessary
	if( model.fitViewport ) {
		var wDims = pax.util.getWindowDimensions();
		model['width'] = ( model['width'] < wDims['width'] )? model['width']: wDims['width'];
		model['height'] = ( model['height'] < wDims['height'] )? model['height']: wDims['height'];
	}

	pax.util.setStyle( element, {
		width: model['width'] + 'px',
		height: model['height'] + 'px',
		marginLeft: offsetLeft + parseInt( '-' + parseInt( ( model['width'] / 2 ), 10 ) ) + 'px',
		marginTop: offsetTop + parseInt( '-' + parseInt( ( model['height'] / 2 ), 10 ) ) + 'px'
	} );
	
	//	Add the contents
	if( model['src'] ) {			//	If iFrame
		var attribs = model.iFrame;
		attribs.name = (attribs.name)? attribs.name: attribs.id + '_' + Math.round( Math.random() * 1000 );
		attribs.src = model.src;
		
		var myIframe =	"<iframe width='" + attribs.width + "px' scrolling='" + attribs.scrolling + "' height='" + 
						(attribs.height - ( (model.showFooter)? model.footerHeight: 0 ) ) + 
						"px' frameborder='" + attribs.frameborder + "' hspace='" + attribs.hspace + "' src='" + attribs.src + 
						"' id='" + attribs.id + "' name='" + attribs.name + "'/></iframe>";
		
		model.content = myIframe;
	} else if( model['url'] ) {		//	If ajax
		pax.get( model.url, function( xml, txt, url ) {
			model.content = txt;
			pax.widget.modal._init( element, model );
		} );
	} else {						//	If HTML
		model.content = model['message'];
	}

	//	Init the modal box, unless it's ajax, in which case the callback function handles it.	
	if( ! model['url'] )pax.widget.modal._init( element, model );
};


//	Initialise the modal box
pax.widget.modal._init = function( element, model ) {
	//	Show overlay
	pax.widget.modal.overlay( model );
	
	//	Initialise widget and render
	var modal = pax.widget.init( {
		model: model,
		template: model.template,
		target: element
	} );
	
	modal.render();

	element.style.display = '';
};


//	Default modal template
pax.widget.modal.template = function() {
	return "" +
		"<div id='templatePaxModalBox'>" +
		"	<div id='templatePaxModalBoxContent'>[:= content :]</div>" +
		"	[: if( showFooter ) { :]" +
		"	<div id='templatePaxModalBoxFooter'>" +
		"		<a href='javascript:pax.widget.modal.hide();'>X</a>" +
		"	</div>" +
		"	[: } :]" +
		"</div>" +
	"";
};


//	Hides the box
pax.widget.modal.hide = function() {
	var mod = pax.$( 'paxModalBox' );
	if( mod )mod.style.display = 'none';
	pax.widget.modal.hideOverlay();
};


//	Insert the overlay (if it doesn't exist)
pax.widget.modal.overlay = function( model ) {

	var element = pax.$( 'paxModalOverlayBox' );
	if( !element ) {
		element = (element) ? element : pax.util.genElement('DIV', { 'id': 'paxModalOverlayBox', 'class': 'paxModalOverlay' } );
		document.body.appendChild(element);
	}
	element.style.display = '';

	//	IE6 hack - the body 
	if( pax.isIe6Down ) {
		//	Save body settings for later
		pax.widget.modal.ie6SelectHackValues['body'] = {
			height: document.body.style.height,
			width: document.body.style.width,
			overflow: document.body.style.overflow
		};
		pax.widget.modal.ie6SelectHackValues['document'] = {
			overflowY: document.documentElement.style.overflowY,
			height: document.documentElement.style.height
		};

		document.body.style.height = "100%";
		document.body.style.width = "100%";
		document.body.style.overflow = "hidden";
		document.documentElement.style.overflowY = "hidden";
		document.documentElement.style.height = "100%";
		
		element.style.zIndex = 100;

		// Only insert iFrame, if it doesn't already exist
		if( ! pax.widget.modal.ie6SelectHackValues['iframe'] ) {
			myZIndex = element.style.zIndex - 1;      // Insert iFrame layer just below Div
			// The ugly string below is an iframe with the correct parameters.
			var myFrameText = "<iframe src=\"javascript:false;\" id=\"paxModalOverlayIframe\" class=\"paxModalOverlay\" scrolling=\"no\" frameborder=\"0\" style=\"z-index:"+myZIndex+";filter:'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';\"></iframe>";
			element.insertAdjacentHTML('afterEnd', myFrameText);
			pax.widget.modal.ie6SelectHackValues['iframe'] = pax.$( 'paxModalOverlayIframe' );
		}
	}
};


//	Hides the overlay
pax.widget.modal.hideOverlay = function() {
	var mo = pax.$( 'paxModalOverlayBox' );
	if( mo )mo.style.display = 'none';

	//	IE6 hack to hide selects.
	if( pax.isIe6Down ) {
		//	Remove iFrame
		if( pax.widget.modal.ie6SelectHackValues['iframe'] ) {
			pax.widget.modal.ie6SelectHackValues['iframe'].removeNode( true );
			pax.widget.modal.ie6SelectHackValues['iframe'] = null;
		}
		document.body.style.height = pax.widget.modal.ie6SelectHackValues['body'].height;
		document.body.style.width = pax.widget.modal.ie6SelectHackValues['body'].width;
		document.body.style.overflow = pax.widget.modal.ie6SelectHackValues['body'].overflow;
		document.documentElement.style.overflowY = pax.widget.modal.ie6SelectHackValues['document'].overflowY;
		document.documentElement.style.height = pax.widget.modal.ie6SelectHackValues['document'].height;
	}
};

/* ----------------------------------------------------------------------------

	pax.widget.button.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.widget.button
		This is a button widget, that can render buttons, for use in other widgets
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.button = pax.widget.button || {};

/*	Method: pax.widget.button.init
	Renders a button into the given target
	
	Parameters:		
		target - an element to render the button into
		args - arguments for the grid widget
		* id - Optional ID of our new button
		* width - Width of the button, eg: '70px'
		* height - Height of the button
		* content - Stuff in the button (if not specified, we use the contents of the target)
		* showChrome - Do we show a nice button chrome surround, or just the contents?
		* inline - Do we want the button to be displayed inline, or as table-layout?
		* func - Click function
		* baseClass - The default base class name, for applying style class on hover, and click, your class will need to be named baseClass + '_pButtonHover', and baseClass + '_pButtonClick'

	Returns:
		Button widget object


	Example:
		(start code)
			<span id='widget.button.init.example1'></span>
			[:.
				pax.widget.button.init( pax.$('widget.button.init.example1'), {
					id: 'myButton',
					content: 'Hello!',
					func: function(e){ alert('Hi!') }
				} );
			:]
		(end)


	Example:
		(start code)
			<span id='widget.button.init.example1.but1' style='float:left'></span>
			<span id='widget.button.init.example1.but2' style='float:left'></span>
			<span id='widget.button.init.example1.but3' style='float:left'></span>
			<span id='widget.button.init.example1.but4' style='float:left'></span>
			[:.
				pax.widget.button.init( pax.$('widget.button.init.example1.but1'), {
					func: function(e) { alert( 'you clicked button 1.' ); },
					content: '<img src="/pax/resource/img/arrow_left_full.gif">',
					width: '18px'
				} );
				pax.widget.button.init( pax.$('widget.button.init.example1.but2'), {
					func: function(e) { alert( 'you clicked button 2.' ); },
					content: '<img src="/pax/resource/img/arrow_left.gif">',
					width: '18px'
				} );
				pax.widget.button.init( pax.$('widget.button.init.example1.but3'), {
					func: function(e) { alert( 'you clicked button 3.' ); },
					content: '<img src="/pax/resource/img/arrow_right.gif">',
					width: '18px'
				} );
				pax.widget.button.init( pax.$('widget.button.init.example1.but4'), {
					func: function(e) { alert( 'you clicked button 4.' ); },
					content: '<img src="/pax/resource/img/arrow_right_full.gif">',
					width: '18px'
				} );
			:]
		(end)
		This example shows some built-in image buttons
		
	TODO: Make it autodetect 'button' and 'input type="button"', and render accordingly.
*/
pax.widget.button.init = function( target, args ) {

	var model = pax.widget.button.model( target, args );
	
	var myButton = pax.widget.init( {
		target: target,
		model: model, 
		template: pax.widget.button.template()
	} );

	myButton.render();

	myButton.bindClassNames( target, {
		'pButton-wrap': {
			mouseover: function(ele) { 
				pax.util.addClassName( this, ( model.baseClass )? model.baseClass + '_pButtonHover': 'pButtonHover' ); 
			},
			mouseout: function(ele)	{ 
				pax.util.removeClassName( this, ( model.baseClass )? model.baseClass + '_pButtonHover': 'pButtonHover' );
				pax.util.removeClassName( this, ( model.baseClass )? model.baseClass + '_pButtonClick': 'pButtonClick' );
			},
			mousedown: function(ele) {
				pax.util.addClassName( this, ( model.baseClass )? model.baseClass + '_pButtonClick': 'pButtonClick' ); 
			},
			mouseup: function(ele) {
				pax.util.removeClassName( this, ( model.baseClass )? model.baseClass + '_pButtonClick': 'pButtonClick' ); 
			},
			click: model.func
		}
	} );

	return myButton;
};



//	Setup the default config values for this widget
//	TODO: Rename 'func' to 'click'
pax.widget.button.model = function( target, args ) {
	return pax.defaultArgs( {
		id: '',						//	Optional ID of our new button
		width: '70px',				//	Width of the button
		height: '20px',				//	Height of the button
		content: target.innerHTML,	//	Stuff in the button
		showChrome: true,			//	Do we show a nice button chrome surround, or just the contents?
		inline: false,				//	Do we want the button to be displayed inline, or as browser default (usually table-layout)?
		baseClass: '',				//	The default base class name, for applying other than the default style
		buttonClass: '',			//	The CSS class name to apply to the button part of the button template
		func: false					//	Click function
	}, args );
};


//	Private method: pax.widget.button.template - Returns a PAX template for the button
//	Note: as we only have one tempate, 'name' is not really required here. If we had multiple templates, we would conditionally return a template, based on name.
pax.widget.button.template = function( name ) {

	var myTemplate = "" +
		"<table id='[:= id :]' class='[:= (showChrome)? '': 'pButtonNoChrome' :] [:= baseClass :] pButton-wrap pButton' cellspacing='0' cellpadding='0' "+
		"border='0' width='[:= width :]' height='[:= height :]' style='width:[:= width :]; height:[:= height :];[:= (inline)? 'display: inline': '' :]'>"+
		"	<tbody>" +
		"		<tr>"+
		"			[: if(showChrome) { :]"+
		"			<td class='pButton-left'><span></span></td>"+
		"			[: } :]"+
		"			<td class='pButton-center'>"+
		"				<em><button type='button' class='pButton-text[:= (pax.isIe)? ' paxisIe': '' :][:= (buttonClass != '')? ' ' + buttonClass: '' :]' style='width:[:= width :]; height:[:= height :]'>[:= content :]</button></em>"+
		"			</td>"+
		"			[: if(showChrome) { :]"+
		"			<td class='pButton-right'><span></span></td>"+
		"			[: } :]"+
		"		</tr>"+
		"	</tbody>" +
		"</table>"+
	"";
	
	return myTemplate;
};
/* ----------------------------------------------------------------------------

	pax.widget.autocomplete.js Copyright (C) 2002, 2005, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.widget.autocomplete
		This is an ajax based auto-complete text box
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.autocomplete = pax.widget.autocomplete || {};

/*	Method: pax.widget.autocomplete.init
	Initialises an autocomplete widget
	
	Parameters:		
		displayField - the field we display the value in
		args - arguments for the grid widget
		* url - URL to submit the auto completer requests to.
		* searchDelay - Time in ms to delay the search, default is 500ms
		* minSearchLength - Minimum chars to input before searching, default is 1
		* valueField - Optionally you can choose a seperate field to store the data in. By default this is the same as the displayField
		* useIndex - Use the index value rather than the displayed value (like a select dropdown), usually used in conjunction with a hidden displayField; default is false
		* resultMaxLength - Maximum number of results to display, default is 0 = no maximum
		* resultCharWidth - Length of each string to display from the result, default is 20
		* selectResult - Select the non-matching part of the text, for typing over, default is true
		* selectFirstMatch - Automatically selects the first match from the result set, default is true
		* searchMode - Which search mode to be in by default
		* searchModeClasses - An object of the search modes available, format is { 'MODE': { className: 'CSSCLASSNAME', title: 'TITLE' }
		* showConfigIcon - Do we show the configuration icon?

	Returns:
		nothing

	Example:
		(start code)
			Autocomplete widget example: start by typing 'la' in the box below:
			<form name='pAutoCompForm' onsubmit='return false;'>
				<span>
					<input name="town" id="town">
				</span>
			</form>
			This example uses a static php file, with a random selection of 300 Australian town names from NSW. 
			It will show an autocomplete widget where you can quickly find town names, via three diffrent methods:
			<ul>
				<li><b>Start match</b> - matches from the start of the value string</li>
				<li><b>Partial match</b> - matches anywhere within the value string</li>
				<li><b>Sounds like</b> - matches on complete values that sound like the string (using a soundex lookup)</li>
			</ul>
			Click on the icon at the end of the text box, to configure a different search method.
			[:.
				pax.widget.autocomplete.init( pax.$('town'), '/pax/documentation/pax.widget.autocomplete.example.php' );
				exampleCleanup = function() { 
					pax.widget.autocomplete.hideMessageBox( 'town_icon' );
				};
			:]
		(end)
*/
pax.widget.autocomplete.init = function( displayField, args ) {
	// The valueField is the same as the displayField if it has not been specified.
	args.valueField = ( typeof args.valueField != 'undefined' && args.valueField )? args.valueField : displayField;

	var cfg = pax.defaultArgs( {
		valueField: null,								// Optional field to place the value into; use when display value is different to actual value
		url: null,										// URL to submit the auto completer requests to.
		searchDelay: 500,								// Time in ms to delay the search
		minSearchLength: 1,								// Minimum chars to input before searching
		resultMaxLength: 0,								// Maximum number of results to display, 0 = no maximum
		resultCharWidth: 20,							// Length of each string to display from the result
		useIndex: ( args.valueField != displayField ),	// Use the index value rather than the displayed value; always uses index value, if we have a seperate displayFieldID
		selectResult: true,								// Display the matching text as "normal", and the rest as selected text
		selectFirstMatch: true,							// Automatically selects the first match from the result set, default is true
		searchMode: 'default',							// Which search mode to be in by default
		showConfigIcon: true,							// Do we show the configuration icon?
		searchModeClasses: {							// The search modes available.
			'default': { className: 'pAutoCompMatchDefault', title: 'Start match' },
			'partial': { className: 'pAutoCompMatchPartial', title: 'Partial Match' },
			'soundex': { className: 'pAutoCompMatchSoundex', title: 'Sounds like' }
		},
		parseResult: pax.widget.autocomplete.parseResult,				// The default parsing function
		
		// System variables
		lastSearch: '',									// Previous search string
		requestTimer: null,								// The timeout variable.
		result: {},										// The result
		resultCount: 0,									// Count of results
		resultOffset: 0,								// Offset of chosen result.
		resultDiv: 'pAutoCompResult_' + displayField.id,				// The DIV we show the result in.
		originalValue: (args.valueField)? args.valueField.value: ''		// The original value of the field
	}, args );

	//	Don't select values, if the searchmode is not the default
	if( cfg.searchMode != 'default' )cfg.selectResult = false;
	
	// Attach config to the displayField
	displayField.pAutoCompConfig = cfg;
	
	// turn off browser auto-complete feature for the fields.
	displayField.setAttribute( 'autocomplete', 'off' );
	args.valueField.setAttribute( 'autocomplete', 'off' );

	// bind various key events
	pax.event.bindKeyUp( displayField, pax.widget.autocomplete.keyUp );
	pax.event.bindKeyPress( displayField, pax.widget.autocomplete.keyDown);
	pax.event.bind( displayField, 'blur', function( event ) {
		var displayField = (event.srcElement)? event.srcElement : event.target;
		var cfg = displayField.pAutoCompConfig;
		pax.widget.autocomplete.setValueAndHide( cfg.resultOffset, displayField );
	} );

	pax.widget.autocomplete.showConfigIcon( displayField.id );
};


/*	Private Method: pax.widget.autocomplete.showConfigIcon
	Shows the configuration icon, depending on the selected mode.
*/
pax.widget.autocomplete.showConfigIcon = function( displayFieldID ) {
	var displayField = pax.$( displayFieldID );
	var cfg = displayField.pAutoCompConfig;
	if( cfg.showConfigIcon ) {
			
		//	WIP: This should append the box
		//	pax.box.show( displayField.id + '_icon', cfgBox, 'pAutoCompConfigIcon', pos.x + pos.width - 19, pos.y + 3 );

		var iconId = displayField.id + '_icon';
		
		var myBox = pax.$( iconId );
		
		if( ! myBox ) {
			//	The display field MUST float left for us to position the button correctly
			var style = displayField.style || '';
			style += (style == '')? '': '; ';
			style += 'float: left;';
			displayField.setAttribute( 'style', style );
			
			//	Add the button, to display the calendar.
			var myBox = pax.box.append( iconId, '', 'pAutoCompConfigIcon', displayField, 'E' );
			
			//	We give focus to the field, when the box is clicked; this is necessary for FF, as the box overlaps the field.
			//	There must be a better way to do this? - we MUST append it, or the box goes "wonky" on browser resize / text resize...
			pax.event.bind( myBox, 'click', function() { displayField.focus(); } );
			
			//	Get button offsets in pixels
			var offsetRight = (cfg.buttonOffset && cfg.buttonOffset.x)? cfg.buttonOffset.x: 0;
			var offsetDown = (cfg.buttonOffset && cfg.buttonOffset.y)? cfg.buttonOffset.y: 0;
			
			//	Adjust left
			var left = myBox.style.left;
			left = ( left.indexOf( 'px' ) > -1 )? left.substring( 0, left.indexOf( 'px' ) ): left;
			myBox.style.left = (parseInt(left - 23) + offsetRight) + 'px';
			pax.css.opacity( myBox, 50 );	// Set opacity at 50%, so we can see text through it.
			
			//	Adjust top
			var top = myBox.style.top;
			top = ( top.indexOf( 'px' ) > -1 )? top.substring( 0, top.indexOf( 'px' ) ): top;
			//	myBox.style.top = (parseInt(top + 7) + offsetDown) + 'px';
			myBox.style.top = (parseInt(top) + offsetDown) + 'px';
		}
		
		//	myBox.innerHTML = '';

		//	Turn the box into a button
		pax.widget.button.init( myBox, {
			baseClass: cfg.searchModeClasses[cfg.searchMode]['className'],
			showChrome: false,
			width: '16px',
			height: '14px',
			func: function( e ) {
				pax.widget.autocomplete.showConfigBox( displayField.id );
				return false;
			},
			inline: false
		} );
		
	}
};


/*	Private Method: pax.widget.autocomplete.showConfigBox
	Show the configuration box
*/
pax.widget.autocomplete.showConfigBox = function( displayFieldID ) {
	var displayField = pax.$( displayFieldID );
	var cfg = displayField.pAutoCompConfig;
	var pos = pax.util.getPosition( displayField );
	var iconPos = pax.util.getPosition( pax.$( displayField.id + '_icon' ) );
	var myConfig = "";

	// Add a selection for each configuration
	for( var c in cfg.searchModeClasses ) {
		myConfig += "<div onclick=\"javascript:" +
			" pax.widget.autocomplete.setSearchMode('"+c+"', pax.$('" + displayFieldID + "'));" + 
			" pax.box.hide('" + displayFieldID + "_config');" + 
			" pax.widget.autocomplete.showConfigIcon('" + displayFieldID + "');" +
			" pax.$('" + displayFieldID + "').focus();" +
			" return false;\" " +
			" class='" + cfg.searchModeClasses[c]['className'] + "'" + 
			" style='padding-left: 20px; height: 20px;'>" + 
			cfg.searchModeClasses[c]['title'] + "</div>";
	}

	pax.box.show( displayFieldID + '_config', myConfig, 'pAutoCompConfigBox', pos.x + pos.width, iconPos.y );
};


/*	Private Method: pax.widget.autocomplete.setValueAndHide
	Sets the value, and hides the search box.
*/
pax.widget.autocomplete.setValueAndHide = function( index, displayField ) {
	var cfg = displayField.pAutoCompConfig;
	var valueField = cfg.valueField;
	var disValue = pax.widget.autocomplete.getDisplayResult( displayField );
	var valValue = pax.widget.autocomplete.getValueResult( displayField );
	
	if( disValue != 'undefined' )displayField.value = disValue;
	if( valValue != 'undefined' )valueField.value = valValue;
	pax.widget.autocomplete.hideResult( displayField );
};


/*	Private Method: pax.widget.autocomplete.getIndex
	Get the index result at a specific offset - useful with allowIndex
*/
pax.widget.autocomplete.getIndex = function( displayField ) {
	var cfg = displayField.pAutoCompConfig;
	var count = 0;
		
	for( var r in cfg.result ) {
		if( count == cfg.resultOffset )return r;
		count += 1;
	}
	
	// If we don't get an index, set the original value.
	if( ! cfg.selectFirstMatch )return ( cfg.useIndex )? cfg.originalValue: false;
};


/*	Private Method: pax.widget.autocomplete.getResultOffset
	Get the offset, from the index result
*/
pax.widget.autocomplete.getResultOffset = function( displayField, index ) {
	var cfg = displayField.pAutoCompConfig;
	var count = 0;
	for( r in cfg.result ) {
		if( r == index )return count;
		count += 1;
	}
	return cfg.resultOffset;		// If we don't get an index, return the current result offset.
};


/*	Private Method: pax.widget.autocomplete.getDisplayResult
	Get the selected value for display: always the value, not the key.
*/
pax.widget.autocomplete.getDisplayResult = function( displayField ) {
	var cfg = displayField.pAutoCompConfig;
	var valueField = cfg.valueField;
	var myValue = cfg.result[pax.widget.autocomplete.getIndex( displayField )];
	
	if( typeof( myValue ) == 'undefined' || myValue == false ) {
		return displayField.value;
	} else if( myValue == 'undefined' )myValue = '';	// Note, we will get the string 'undefined' if the result is empty...

	return myValue;
};


/*	Private Method: pax.widget.autocomplete.getValueResult
	Get the selected value: could be either value or key (index)
*/
pax.widget.autocomplete.getValueResult = function( displayField ) {
	var cfg = displayField.pAutoCompConfig;
	var myValue = ( cfg.useIndex )?'' + pax.widget.autocomplete.getIndex( displayField ) : '' + cfg.result[pax.widget.autocomplete.getIndex( displayField )];
	return ( typeof( myValue ) == 'undefined' )? cfg.valueField.value : myValue;
};


/*	Private Method: pax.widget.autocomplete.keyDown
	Events that fire as key is pressed, and before the key is released
*/
pax.widget.autocomplete.keyDown = function( event ) {
	var displayField = (event.srcElement)? event.srcElement : event.target;
	var cfg = displayField.pAutoCompConfig;
	// TAB - set the value of the textbox to the offset value.
	if( event.keyCode == 9 )pax.widget.autocomplete.setValueAndHide( cfg.resultOffset, displayField );
};


/*	Private Method: pax.widget.autocomplete.keyUp
	Events that fire as the key is released
*/
pax.widget.autocomplete.keyUp = function( event ) {
	var displayField = (event.srcElement)? event.srcElement : event.target;
	var cfg = displayField.pAutoCompConfig;	
	var highlight = false;
	
	if( event.keyCode == 40 ) {							// Arrow key down - move down list of options
		if( !pax.isIe ) { event.preventDefault(); }		// Stop keypress from propagating.
		cfg.resultOffset += 1;
		if( cfg.resultOffset > ( cfg.resultCount - 1 ) )cfg.resultOffset = cfg.resultCount -1;
		highlight = true;
	} else if( event.keyCode == 38 ) {					// Arrow key up - move up list of options
		if( !pax.isIe ) { event.preventDefault(); }		// Stop keypress from propagating.
		cfg.resultOffset -= 1;
		if( cfg.resultOffset < 0 )cfg.resultOffset = 0;
		highlight = true;
	} else if( event.keyCode == 13 ) {					// Enter - set the value of the textbox to the offset value.
		if( !pax.isIe ) { event.preventDefault(); }		// Stop keypress from propagating.
		pax.widget.autocomplete.setValueAndHide( cfg.resultOffset, displayField );
		return;
	} else if( event.keyCode == 27 ) {					// ESC - hide list of options
		if( !pax.isIe ) { event.preventDefault(); }		// Stop keypress from propagating.
		displayField.value = cfg.originalValue;
		pax.widget.autocomplete.hideResult( displayField );
		return;
	}

	if( highlight )pax.widget.autocomplete.highlightResult( displayField );
	else {
		if( displayField.value != cfg.lastSearch ) {	// Do the search via a timeout.
			window.clearTimeout( cfg.requestTimer );
			cfg.requestTimer = window.setTimeout( function() { 
				pax.widget.autocomplete.search( displayField ) 
			}, cfg.searchDelay );
		}
	}
};


/*	Method: pax.widget.autocomplete.setSearchMode
	Chooses which search mode to use
	
	Parameters:		
		searchMode - which search mode to use
		displayField - which field has the config, and is used for display

	Returns:
		nothing

	Example:
		(start code)
			Try typing 'Lake cat eye', and you should get 'Lake Cathie'. "Lake cat eye" is what the locals call it ;o)
			<form name='pAutoCompForm' onsubmit='return false;'>
				<span>
					<input name="pax.widget.autocomplete.setSearchMode.town" id="pax.widget.autocomplete.setSearchMode.town">
				</span>
			</form>
			[:.
				pax.widget.autocomplete.init( pax.$('pax.widget.autocomplete.setSearchMode.town'), '/pax/documentation/pax.widget.autocomplete.example.php' );
				pax.widget.autocomplete.setSearchMode( 'soundex', pax.$('pax.widget.autocomplete.setSearchMode.town') );
				pax.widget.autocomplete.showConfigIcon( 'pax.widget.autocomplete.setSearchMode.town' );
				
				exampleCleanup = function() { 
					pax.box.hide( 'pax.widget.autocomplete.setSearchMode.town_icon' );
				};
			:]
		(end)
		This example is almost identical to the <pax.widget.autocomplete.init> example, except it starts in 'sounds like' mode. 
		You should note that we must use pax.widget.autocomplete.showConfigIcon to update the config icon.
*/
pax.widget.autocomplete.setSearchMode = function( searchMode, displayField ) {
	var cfg = displayField.pAutoCompConfig;
	var isDifferentSearchMode = ( searchMode != cfg.searchMode );
	cfg.searchMode = ( typeof searchMode != 'undefined' ) ? searchMode : 'default';		// If the search is to match within text (ie:partially - see .php script for details)
	// Note, we can't use selectResult with special searchMode!
	if( cfg.searchMode != 'default' )cfg.selectResult = false;
	//	Run the search
	if( isDifferentSearchMode ) {
		pax.widget.autocomplete.search( displayField );
	}
};


/*	Private Method: pax.widget.autocomplete.search
	Run the search.
*/
pax.widget.autocomplete.search = function( displayField ) {
	var cfg = displayField.pAutoCompConfig;
	cfg.resultCount = 0;
	cfg.resultOffset = ( cfg.selectFirstMatch )? 0: -1;
	
	if( displayField.value.length >= cfg.minSearchLength ) {
		cfg.lastSearch = displayField.value;
		
		var postString = pax.postString( {
			self: displayField.id,
			searchMode: cfg.searchMode,
			query: displayField.value
		} );
		
		if( cfg.currentPost )pax.cancel( cfg.currentPost );
		cfg.currentPost = pax.post( cfg.url, postString, cfg.parseResult );
	} else {
		pax.widget.autocomplete.hideResult( displayField );
	}
};


/*	Private Method: pax.widget.autocomplete.parseResult
	Parse result, and call the show function.
*/
pax.widget.autocomplete.parseResult = function( xml, txt, url ) {
	var result = pax.unJSON( txt );

	//	We have a pointer sent to the server called __self__, which we then use when the result is passed back.
	var displayField = pax.$( result['__self__'] );
	var cfg = displayField.pAutoCompConfig;
	
	// Remove '__self__' from result
	var myResult = {};
	for( var r in result )if( r != '__self__' )myResult[r] = result[r];

	cfg.result = myResult;											// Keep a copy, for displaying the selected option.
	pax.widget.autocomplete.showResult( displayField, myResult );	// Show the result
	window.clearTimeout( cfg.requestTimer );						// Reset search request timer
};


/*	Private Method: pax.widget.autocomplete.highlightResult
	Highlights the result that is currently selected, and un-highlights the rest.
*/
pax.widget.autocomplete.highlightResult = function( displayField, offset ) {
	var cfg = displayField.pAutoCompConfig;
	var offset = ( offset ) ? offset : cfg.resultOffset;
	var resDiv = pax.$( cfg.resultDiv );
	if( !resDiv )return;	// If the result div hasn't been shown yet, we return.
	// Loop on the divs within the result div, and turn off.
	var myDivs = resDiv.getElementsByTagName('div');
	var count = 0;
	for( var d = 0; d < myDivs.length; d++ ) {
		if( typeof myDivs[d] != 'undefined' ) {	
			if( typeof myDivs[d].id != 'undefined' ) {
				myDivs[d].className = 'pAutoCompResultLine';
				if( count == offset ) {
					myDivs[d].className = 'pAutoCompResultLineSelected';
				}
				count += 1;
			}
		}
	}

	if( cfg.selectResult ) {
		//	Display the matching text as "normal", and the rest as selected text
		var resultValue = pax.widget.autocomplete.getDisplayResult( displayField );
		var matchValue = cfg.lastSearch;	//	Match on the search value, rather than the field value
		if( resultValue != false ) {
			if( resultValue.length > matchValue.length ) {
				displayField.value = resultValue;
				pax.form.selectRange( displayField, matchValue.length, resultValue.length );
			}
		}
		displayField.focus();
	}
};


/*	Private Method: pax.widget.autocomplete.showResult
	Show the result box
*/
pax.widget.autocomplete.showResult = function( displayField, res ) {
	if( res == null )return;
	var cfg = displayField.pAutoCompConfig;
	var myList = '';
	var count = 0;
	for( var r in res ) {
		if( cfg.resultCharWidth > 0 ) {
			resultValue = res[r].substring(0,cfg.resultCharWidth);
			if( resultValue.length > cfg.resultCharWidth - 1 )resultValue += '...';
		} else resultValue = res[r];
		
		// Highlight the matching text in the result value here.
		matchValue = resultValue.toUpperCase();		
		boldStart = matchValue.indexOf( displayField.value.toUpperCase() );
		boldEnd   = displayField.value.length;
		matchValue = resultValue.substring( boldStart, boldStart + boldEnd );
		resultValue = resultValue.substring(0,boldStart) + '<b>' + matchValue  + '</b>' + resultValue.substring(boldStart + boldEnd, resultValue.length);

		var selectJS = " onmouseover=\"pax.$('" + displayField.id + "').pAutoCompConfig.resultOffset = pax.widget.autocomplete.getResultOffset( pax.$('" + displayField.id + "'), " + r + ");pax.widget.autocomplete.highlightResult(pax.$('" + displayField.id + "') );\" ";

		if( count == cfg.resultOffset ) {
			myList += "<div " + selectJS + " class='pAutoCompResultLineSelected' id='" + cfg.resultDiv + "_" + count + "'>" + resultValue + "</div>\n";
		} else {
			myList += "<div " + selectJS + " class='pAutoCompResultLine' id='" + cfg.resultDiv + "_" + count + "'>" + resultValue + "</div>\n";
		}
		count += 1;
		if( cfg.resultMaxLength > 0 && count >= cfg.resultMaxLength )break;
	}
	cfg.resultCount = count;
	
	var pos = pax.util.getPosition( displayField );
	var myBox = pax.box.append( cfg.resultDiv, myList, 'pAutoCompResult' );
	myBox.style.position = 'absolute';
	myBox.style.display = 'block';
	
	myBox.style.left = pos.x + 'px';
	myBox.style.top = parseInt( pos.y + pos.height ) + 'px';
	myBox.style.width = pos.width;
	if( myBox.style.zIndex < 1 )myBox.style.zIndex = 3;  // Ensure that the box has a positive zIndex.

	if( cfg.selectResult ) {
		//	Display the matching text as "normal", and the rest as selected text
		var resultValue = pax.widget.autocomplete.getDisplayResult( displayField );
		var matchValue = cfg.lastSearch;	//	Match on the search value, rather than the field value
		if( resultValue != false ) {
			if( resultValue.length > matchValue.length ) {
				displayField.value = resultValue;
				pax.form.selectRange( displayField, matchValue.length, resultValue.length );
			}
		}
		displayField.focus();
	}
};


/*	Private Method: pax.widget.autocomplete.hideResult
	Hide the result box
*/
pax.widget.autocomplete.hideResult = function( displayField ) {
	var cfg = displayField.pAutoCompConfig;
	pax.box.hide( pax.$( cfg.resultDiv ) );
};
/* ----------------------------------------------------------------------------

	pax.widget.datagrid.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.widget.datagrid
		This is a datagrid widget, that can render in various ways
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.datagrid = pax.widget.datagrid || {};					// The data grid
pax.widget.datagrid.filter = pax.widget.datagrid.filter || {};		// A set of pre defined filter templates

/*	Method: pax.widget.datagrid.init
	Initialises a grid for ajax loading
	
	Parameters:		
		target - an element to render the grid into
		args - arguments for the grid widget
		* url - Data loading URL
		* rowClick - A function we fire on row click
		* itemClick - A function we fire on item click
		* offset - Data index offset
		* limit - Data row limit
		* pageNumberLimit - Limit of how many pages to show before using dots, ie: "..."
		* sortDirection - The sort direction variable name sent to the server
		* reverseSortDirection - The reverse direction sort variable name sent to the server
		* ignoreCols - Columns to ignore in the display
		* applyCols - Templates to apply to items in a column
		* filterCols - Templates to apply to the filter header, this is work in progress
		* colName - Column name override
		* colWidth - Optional width of each column
		* colWidthDefault - Default column width
		* colAlign - Optional alignment of each column, use column name: [left, right, center]
		* width - Width of the grid - automatically grows if not specified, use pixels only
		* height - Height of the grid - if not specified, grid won't scroll, use pixels only
		* showHeader - If we want to show the header.
		* showFooter - If we want to show the footer.
		* showSearch - If we want to show the search box
		* data - The data in the grid

	Returns:
		nothing

	Example:
		(start code)
			<div id='widget.datagrid.init.example1'></div>
			[:.
				var args = {
					url: '/pax/documentation/pax.widget.example.table.php',
					limit: 10,
					ignoreCols: [ 'notes' ],
					colWidth: { id: '30px', name: '30%', country: '30%' },
					sort: 'name'
				};
				
				pax.widget.datagrid.init( pax.$('widget.datagrid.init.example1'), args );
			:]
		(end)
		This example sets up a simple ajax data grid, getting the data from the given php file.
		See the data grid demo for a live demo: <http://paxjs.com/demo_data_grid.htm>
*/
/*
	TODO: 
	. Add a template for row rendering, so it can easily be over ridden.
	. Make this use pax.data.js, so we can parse various formats easily. Allow passing in a formatter function.
	. Make templates overridable from the model
	
*/
pax.widget.datagrid.init = function( target, args ) {
	args = args || {};

	//	Setup the default config values for this widget (used internally for keeping state)
	var model = pax.defaultArgs( {
		target: target,							// Target for rendering the data grid
		url: '',								// Data loading URL
		rowClick: null,							// The function we fire on row click
		itemClick: null,						// The function we fire on item click
		offset: 0,								// Data index offset
		limit: 5,								// Data row limit
		pageNumberLimit: 8,						// Limit of how many pages to show before using ...
		sortDirection: 'SORT_ASC',				// The sort direction sent to the server
		reverseSortDirection: 'SORT_DESC',		// The reverse sort direction sent to the server
		ignoreCols: [],							// Columns to ignore in the display
		applyCols: {},							// Templates to apply to items in a column
		filterCols: {},							// Templates to apply to the filter header
		colName: {},							// Column name override
		colWidth: {},							// Optional width of each column
		colWidthDefault: 150,					// Default column width
		colAlign: {},							// Optional alignment of each column, use left, right, center
		width: 500,								// Width of the grid - automatically grows if not specified
		height: 200,							// Height of the grid - if not specified, grid won't scroll
		offsetScrollWidth: 17,					// Pixels to offset for the scrollbar on the right of the grid
		showHeader: true,						// If we want to show the header.
		showFooter: true,						// If we want to show the footer.
		showSearch: false,						// If we want to show the search box
		hasRendered: false,						// If the template has been rendered once.
		data: null,								// The data in the grid
		param: {								// Setup the default parameters (passed to the server on each request)
			offset: 0,							// Data index offset
			limit: 5,							// Data row limit
			sort: '',							// Column to sort by
			sortDirection: 'SORT_ASC',			// Sort direction, either SORT_ASC or SORT_DESC
			search: ''							// Keyword search
		},
		
		/*	retreives the name of a column, from the element clicked */
		getColumnName: function( target ) {
			var value = target.getAttribute( 'colName' );
			if( ! pax.util.hasClassName( target, 'headerContent' ) ) {
				value = pax.util.getElementsByClassName( target, '*', 'headerContent' )[0].getAttribute( 'colName' );
			}
			return value;
		},
		
		/* 	Below we have functions that will be bound to various parts of the widget.
			These can be overridden as the grid is initialised. */
			
		// These functions are bound by class name
		bindClasses: {
			firstButton: {
				click: function(ele) {
					grid.model.param.offset = 0;
					grid.serverRequest();
				} 
			},
			prevButton: {
				click: function(ele) {
					grid.model.param.offset = parseInt( grid.model.param.offset, 10 ) - parseInt( grid.model.param.limit );
					if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
					grid.serverRequest();
				} 
			},
			nextButton: {
				click: function(ele) {
					//	Whilst we do know the size of the data set, when someone clicks the 'next' button, we still check to see if the data set has grown
					grid.model.param.offset = parseInt( grid.model.param.offset, 10 ) + parseInt( grid.model.param.limit, 10 );
					if( typeof( response.offsetEnd ) != 'undefined' && grid.model.param.offset > (response.offsetEnd * grid.model.param.limit) ) {
						grid.model.param.offset = response.offsetEnd * grid.model.param.limit; 
					};
					grid.serverRequest();
				} 
			},
			lastButton: { 
				click: function(ele) {
					grid.model.param.offset = (response.pages - 2) * grid.model.param.limit;
					grid.serverRequest();
				} 
			},
			pGridHeader: {
				click: function(ele) {
					var target = ele.target || window.event.srcElement;
					var value = grid.model.getColumnName( target );
					
					/*	Set the sortDirection; it should be the default, if we're clicking on a different 
						header than the current sort, and swapped if not.	*/
					if( grid.model.param.sort == value ) {
						grid.model.param.sortDirection = ( grid.model.param.sortDirection == grid.model.sortDirection )? 
							grid.model.reverseSortDirection : 
							grid.model.sortDirection;
					} else grid.model.param.sortDirection = grid.model.sortDirection;
					grid.model.param.sort = value;
					grid.serverRequest();
				},
				mouseover: function(ele) {
					var target = ele.target || window.event.srcElement;
					target = ( pax.util.hasClassName( target.parentNode, 'pGridTD' ) )? target.parentNode: target; 
					pax.util.addClassName( target, 'headerRowHover' );
				},
				mouseout: function(ele) {
					var target = ele.target || window.event.srcElement;
					target = ( pax.util.hasClassName( target.parentNode, 'pGridTD' ) )? target.parentNode: target; 
					pax.util.removeClassName( target, 'headerRowHover' );
				}
			}
		},
		
		// These functions are bound by event type, then class name.
		bindOneClasses: {
			pageNumber: {
				click: function(e) {			// Clicking on a page number
					grid.model.param.offset = ( parseInt(this.innerHTML) - 1 ) * grid.model.param.limit;
					if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
					grid.serverRequest();
				},
				mouseover: function(e)	{ pax.util.addClassName( this, 'hoverPage'); },
				mouseout: function(e)	{ pax.util.removeClassName( this, 'hoverPage'); }
			},
			pGridRow: {
				click: function( e, target ) {
					//	The row number is the number after the last _ in the id. id='[:= theGrid.model.target.id + '_row_' + i :]'
					var rn = target.id;
					rn = rn.substring( rn.lastIndexOf( '_' ) + 1, 99 );
					//	Return the data row, target and event
					if( grid.model.rowClick )grid.model.rowClick( { 
						row: rn,
						data: grid.model.data[rn], 
						target: target, 
						event: e 
					} );
				}
			},
			item: {
				click: function( e, target ) {
					//	The row number is the number after the last _ in the id. id='[:= theGrid.target.id :]_[:= name :]_[:= i :]'
					var rn = target.id;
					rn = rn.substring( rn.lastIndexOf( '_' ) + 1, 1000 );
					//	The column name is 2nd last
					var col = target.id;
					col = col.substring( 0, col.lastIndexOf( '_' ) );
					col = col.substring( col.lastIndexOf( '_' ) + 1, 1000 );
					
					var target = pax.util.getTarget( e );
					if( grid.model.itemClick )grid.model.itemClick( {
						row: rn,
						col: col,
						data: grid.model.data[rn][col],
						target: target,
						event: e
					} );
				},
				mouseout: function(e) {
					pax.util.removeClassName( this, 'itemHover' ); 
					var pEle = this.parentNode;	// Each item is contained in a dataRow, which is what we want to change the hover over
					pEle = pEle.parentNode;
					pax.util.removeClassName( pEle, (pax.util.hasClassName(pEle, 'r0Hover')? 'r0Hover' : 'r1Hover' ) ); 
				},
				mouseover: function(e) {
					pax.util.addClassName( this, 'itemHover' ); 
					var pEle = this.parentNode;	// Each item is contained in a dataRow, which is what we want to change the hover over
					pEle = pEle.parentNode;
					pax.util.addClassName( pEle, (pax.util.hasClassName(pEle, 'r0')? 'r0Hover' : 'r1Hover' ) ); 
				}
			}
		},
		
		// Bind IDs post-fixed with '_' + model.target.id
		internalBindIds: {
			search: {
				keydown: function( e ) {
					if( e.keyCode == 13 ) {
						grid.model.param.search = this.value;
						grid.serverRequest();
					}
				}
			},
			goSearch: {
				click: function() {
					grid.model.param.search = pax.$('search_' + grid.target.id ).value;
					grid.serverRequest();
				}
			},
			offsetBox: {
				keydown: function( e ) {
					if( e.keyCode == 13 ) {
						grid.model.param.offset = ( parseInt(this.value) - 1 ) * grid.model.param.limit;
						if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
						grid.serverRequest();
					}
				}
			}
		}
	}, args );

	//	Setup controller: it receives the ajax response, renders the template, and sets up event handeling
	var gridController = function( xml, txt, url ) {	// This is the default callback handle function
		var response = pax.unJSON( txt );
		
		//	Re-render the widget if we have a response
		if( response && response != '' ) {
			//	Save the response data
			if( response.data )grid.model.data = response.data;
		
			// Check if the server response was at the end of the result set, and set the offset accordingly
			if( typeof( response.offsetEnd ) != 'undefined' )grid.model.param.offset = response.offsetEnd * grid.model.param.limit;
			
			if( pax.$('sort_' + grid.target.id) )pax.box.hide( 'sort_' + grid.target.id );	// Hide the sort direction image
			
			//	Get tags with 'headerContent' class to link to the server side sorting.
			var myHeaders = pax.util.getElementsByClassName( grid.target, '*', 'headerContent' );
			
			for( var h = 0; h < myHeaders.length; h++ ) {
				var myHeader = myHeaders[h];
				pax.util.removeClassName( myHeader, 'sortHeaderOn' );
				pax.util.removeClassName( myHeader, 'sortHeaderOnAsc' );
				pax.util.removeClassName( myHeader, 'sortHeaderOff' );
				
				var value = grid.model.getColumnName( myHeader );
				
				//	Find out which class we should add
				var classToAdd = ( grid.model.param.sort != value )? 
					'sortHeaderOff': ( grid.model.param.sortDirection == grid.model.sortDirection )?
					'sortHeaderOnAsc' : 
					'sortHeaderOn';
					
				pax.util.addClassName( myHeader, classToAdd );
			};
		
			if( grid.model.hasRendered ) {	// Render the partial view
				pax.template.render( pax.widget.datagrid.template( grid ), {
					value: { response: response, theGrid: grid }, 
					partial: 'DataGridPartial_' + grid.model.target.id
				} );
				//	Update footer offset
				pax.$( 'offsetBox_' + grid.model.target.id ).value = ( grid.model.param.offset / grid.model.param.limit ) + 1;
			} else {
				grid.model.hasRendered = true;
				pax.widget.datagrid.destroy( grid );	//	Destroy any old widget, if necessary

				// Calculate width of the grid in pixels. Note: percent won't work!
				var myWidth = 0;
				for( var name in response.data[0] ) {
					if( !pax.util.hasValue( name, grid.model.ignoreCols ) ) {
						myWidth += parseInt(
							( pax.util.hasKey( name, grid.model.colWidth ) )? 
								grid.model.colWidth[name]: 
								grid.model.colWidthDefault
							);
					};
				};
				
				grid.model.width = myWidth;

				// Render the view
				pax.template.render( pax.widget.datagrid.template( grid ), {
					value: { response: response, theGrid: grid }, 
					target: target 
				} );
				
				grid.bindOneEvent( grid.target, grid.model.bindOneClasses );		// Bind just one function for each event type, as we have a lot of items / page numbers.
				grid.bindClassNames( grid.target, grid.model.bindClasses );			// Bind specific class names 
				grid.internalBindIds( grid.target, grid.model.internalBindIds );	// Bind specific IDs, automatically post-fixed with '_' + grid.model.target.id
			}
		}
	};

	var grid = pax.widget.ajax.init( model.url, {
		model: model, 
		controller: gridController, 
		target: target 
	} );
	
	grid.serverRequest();
};


pax.widget.datagrid.destroy = function( model ) {
	model._destroy();	
};


/*	Private method: pax.widget.datagrid.rowTemplate
	Returns template that is rendered for each row
	
	Note: 	if you're going to change this, make sure you name elements EXACTLY
			THE SAME, as they are referred to in various events.

*/
pax.widget.datagrid.rowTemplate = function( grid ) {
	var tpl = "" +
		"					<table cellpadding='0' cellspacing='0'>" +
		"						<tr>" +
		"							[:" +
		"								for( var name in response.data[i] ) {" +
		"									if( !pax.util.hasValue( name, theGrid.model.ignoreCols ) ) {" +
		"										var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"										var colWidthStyle = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"							:]" +
		"							<td class='pGridTD' [:= colWidthStyle :] align='[:= ( pax.util.hasKey(name, theGrid.model.colAlign) )? theGrid.model.colAlign[name]: 'left' :]'>" +
		"								<div style='width: [:= colWidth - 9 :]px' class='pGridCell item content' id='[:= theGrid.target.id :]_[:= name :]_[:= i :]' unselectable='on'>"+
		"									[: if( pax.util.hasKey( name, theGrid.model.applyCols ) ) { :]" +
		"										[:= push( theGrid.model.applyCols[name], { value: response.data[i][name], rowTarget: pax.getNextId() } ) :]" +
		"									[: } else { :]" + 
		"										[:= response.data[i][name] :]" +
		"									[: } :]" +
		"								</div>" +
		"							</td>" +
		"							[:" +
		"									}" +
		"								}" +
		"							:]" +
		"						</tr>" +
		"					</table>";
	return tpl;
};


/*	Method: pax.widget.datagrid.template
	Returns a PAX template for the datagrid
*/
pax.widget.datagrid.template = function( grid ) {
	var myTemplate = "" +
		"<div class='pGrid' style='width: [:= theGrid.model.width + theGrid.model.offsetScrollWidth :]px'>";

	//	Add Header and filters, if needed.
	if( grid.model.showHeader )myTemplate += pax.widget.datagrid.templateHeader();
	if( pax.util.numItems( grid.model.filterCols ) > 0 )myTemplate += pax.widget.datagrid.templateFilter();
	
	myTemplate += "" +
		"	<div class='pGridBody container'>" +
		"		<div class='pGridScroll' style='height: [:= theGrid.model.height :]px'>" +
		"			<div style='width: [:= theGrid.model.width :]px'>" +
		"				[:p(DataGridPartial_[:= theGrid.model.target.id :])" +		//	Partial rendering
		"				<span>" +
		"				[: for( var i = 0; i < response.data.length; i++ ) { :]" +
		"				<div class='pGridRow r[:= i % 2 :]' id='[:= theGrid.model.target.id + '_row_' + i :]'>" +
		"";
		
	myTemplate += pax.widget.datagrid.rowTemplate();
		
	myTemplate += "" +
		"				</div>" +
		"				[: } :]" +
		"				</span>" +
		"				p:]" +		//	End partial rendering
		"			</div>			" +
		"		</div>" +
		"	</div>" +
		"	";

	//	Add footer
	if( grid.model.showFooter )myTemplate += pax.widget.datagrid.templateFooter();

	myTemplate += "" +
		"</div>" +
	"";
	return myTemplate;
};

/*
	Header of data grid
*/
pax.widget.datagrid.templateHeader = function() {
	var myHeaderTemplate = "" +
		"	<div class='pGridHeader'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					[:	for( var name in response.data[0] ) { " +
		"							if( !pax.util.hasValue( name, theGrid.model.ignoreCols) ) {" +
		"								var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"								colWidth = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"					:]" +
		"					<td class='pGridTD' [:= colWidth :]>" +
		"						[: if( !pax.isIe )colWidth = '' :]" +
		"						<div class='pGridCell header headerContent' unselectable='on' colName='[:= name :]'>[:= _caps( ( pax.util.hasKey( name, theGrid.model.colName ) )? theGrid.model.colName[name]: name ) :]</div>" +
		"					</td>" +
		"					[:		}" +
		"						}" +
		"					:]" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
	"";
	return myHeaderTemplate;
};

/*
	Filter section of data grid
*/
pax.widget.datagrid.templateFilter = function() {
	var myTemplate = "" +
		"	<div class='pGridFilter'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					[: for( var name in response.data[0] ) { " +
		"							if( !pax.util.hasValue( name, theGrid.model.ignoreCols) ) {" +
		"								var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"								colWidth = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"					:]" +
		"					<td class='pGridTD' [:= colWidth :]>" +
		"						[: if( !pax.isIe )colWidth = ''; :]" +
		"						<div class='pGridCell header headerContent'>" +
		"							[: if( pax.util.hasKey( name, theGrid.model.filterCols ) ) { :]" +
		"								[:= push( theGrid.model.filterCols[name], { value: {}, rowTarget: pax.getNextId() } ) :]" +
		"							[: } :]" +
		"						</div>" +
		"					</td>" +
		"					[:		}" +
		"						}" +
		"					:]" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
	"";
	return myTemplate;
};

/*
	Footer of data grid
*/
pax.widget.datagrid.templateFooter = function() {

	//	baseClass

	var myTemplate = "" +
		"	<div class='pGridFooter'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					<td class='pGridTD'>" +
		"						<div class='pGridCell footer'>" +
		"							<table cellpadding='0' cellspacing='1' border='0'><tr>" +
		"								<td style='width: 30px'><span class='firstButton' id='firstButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px'><span class='prevButton' id='prevButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px' class='pGridTD'>&nbsp;Page <input size='3' style='width: 30px' id='offsetBox_[:= theGrid.target.id :]' value='[:= ( theGrid.model.param.offset / theGrid.model.param.limit ) + 1 :]'> of <span id='numPages'>[:= response.pages - 1 :]</span>&nbsp;</td>" +
		"								<td style='width: 30px'><span class='nextButton' id='nextButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px'><span class='lastButton' id='lastButton_[:= theGrid.target.id :]'></span></td>" +
		" 								[:." +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('firstButton_' + theGrid.target.id), {  "+
		"										buttonClass: 'arrowLeftFull', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('prevButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowLeft', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('nextButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowRight', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('lastButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowRightFull', " +
		"										width: '20px' } ) " +
		"									);" +
		"								:]" +		
		"								[: if( theGrid.model.showSearch ) { :]" +
		"								<td><span id='searchBox'><input type='text' id='search_[:= theGrid.target.id :]'><button id='goSearch_[:= theGrid.target.id :]'>Go</button></span></td>" +
		"								[: } :]" +
		"								<td><span id='StatusBox'></span></td>" +
		"							</tr></table>" +
		"						</div>" +
		"					</td>" +
		"				</tr>" +
		"			</table>" +
		"		</div>" +
		"	</div>" +
	"";
	return myTemplate;
};


/*	Private Method: pax.widget.datagrid.filter.alpha
	Returns a template with 2 alpha dropdowns, to be able to set a range. You can add your own, or override these, for custom filter headers
	
	Parameters:
		id - An element id to refer to the alpha dropdowns by.
		Note: The id will be pre and post fixed, so if you pass 'name' as the id, you will get two selects, with ids like this: alpha_name_1 and alpha_name_2

	Returns:
		Template string

	Example:
		(start code)
			<div id='widget.datagrid.fliter.alpha.example1'></div>
			[:.
				pax.template.render( pax.widget.datagrid.filter.alpha( 'name' ), { 
					target: pax.$('widget.datagrid.fliter.alpha.example1')
				} );
			:]
		(end)
		This example sets up a alpha range selector.
*/
pax.widget.datagrid.filter.alpha = function( id ) {
	var myTemplate = "" +
		"[:_ if( ! value.alpha_" + id + "_2 ) { value.alpha_" + id + "_2 = 'z' } :]" +
		"<select id='alpha_" + id + "_1'>[: for( var x = 97; x < 123; x++ ) { ch = String.fromCharCode(x); :]<option [:= (ch == value.alpha_" + id + "_1)? 'selected': '' :]>[:= ch :]</option>[: } :]</select> - " +
		"<select id='alpha_" + id + "_2'>[: for( var x = 97; x < 123; x++ ) { ch = String.fromCharCode(x); :]<option [:= (ch == value.alpha_" + id + "_2)? 'selected': '' :]>[:= ch :]</option>[: } :]</select>" +
	"";	
	
	return myTemplate;
};


/*	Private Method: pax.widget.datagrid.filter.numeric
	Returns a template with 2 numeric dropdowns, to be able to set a range. You can add your own, or override these, for custom filter headers
	
	Parameters:
		id - An element id to refer to the numeric dropdowns by.
		start - starting value, eg: 10
		end - end value, eg: 1000
		Note: The id will be pre and post fixed, so if you pass 'name' as the id, you will get two selects, with ids like this: alpha_name_1 and alpha_name_2

	Returns:
		Template string

	Example:
		(start code)
			<div id='widget.datagrid.fliter.numeric.example1'></div>
			[:.
				pax.template.render( pax.widget.datagrid.filter.numeric( 'num', 0, 10 ), { 
					target: pax.$('widget.datagrid.fliter.numeric.example1')
				} );
			:]
		(end)
		This example sets up a numeric range selector.
*/
pax.widget.datagrid.filter.numeric = function( id, start, end ) {
	start = ( start )? start: 1;
	end = ( end )? end: 11;
	var myTemplate = "" +
		"[:_ if( ! value.numeric_" + id + "_2 ) { value.numeric_" + id + "_2 = 0 } :]" +
		"<select id='numeric_" + id + "_1'>[: for( var x = "+ start +"; x < "+ end +"; x++ ) { :]<option [:= (x == value.numeric_" + id + "_1)? 'selected': '' :]>[:= x :]</option>[: } :]</select> - " +
		"<select id='numeric_" + id + "_2'>[: for( var x = "+ start +"; x < "+ end +"; x++ ) { :]<option [:= (x == value.numeric_" + id + "_2)? 'selected': '' :]>[:= x :]</option>[: } :]</select>" +
	"";
	
	return myTemplate;
};
/* ----------------------------------------------------------------------------

	pax.widget.tabset.js Copyright (C) 2002, 2004, 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.widget.tabset
		This is a widget that sets up a tabset
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
	Note:
		It would appear that FF3 has a bug with global name space evaluation, which is causing the
		tabset demo to fail on FF3:
		
			http://groups.google.com/group/mozilla.dev.tech.js-engine.rhino/msg/bffaa0f603ee167b
*/

/*
		TODO: Create "deleteTab" and "addTab" methods, for dynamic creation of tabs.
		TODO: Add a 'close tab' button on the right side, which should use the new "deleteTab" to "close" it..
*/


var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.tabset = pax.widget.tabset || {};

/*	Property: pax.widget.tabset.defaultTabAttribute
	The attribute we use by default to identify a tab, in static tabs	*/
pax.widget.tabset.defaultTabAttribute = 'TITLE';

/*	Method: pax.widget.tabset.init
	Displays the tabset on a specifed element

	Parameters:
		element - The target element for the output
		tabSpaces - Optionally specify how many spaces per tab, default = 4

	Example:
		(start code)
			<form>
				<div id='pax.widget.tabset.example1'>
					<fieldset class='pFieldset' style='float: left; margin-right: 20px;' alt='Personal details'>
						<label class='pLabel' for="firstName">First name</label>	<input name="firstName" id="firstName" type="text" value=""><br>
						<label class='pLabel' for="lastName">Last name</label>		<input name="lastName" id="lastName" type="text" value=""><br>
						<label class='pLabel' for="logon">Logon</label>				<input name="logon" type="text" value=""><br>
						<label class='pLabel' for="dob">Date of birth</label>		<input name="dob" id="dob" type="text" value="" size="25" maxlength="25"><br>
						<label class='pLabel' for="email">Email</label>				<input name="email" id="email" type="text" size="30" value=""><br>
						<label class='pLabel' for="phone">Phone</label>				<input name="phone" id="phone" type="text" size="30" value=""><br>
					</fieldset>
					<fieldset class='pFieldset' style='float: left; margin-right: 20px;' alt='Notes'>
						<label class='pLabel' for="notes">Notes</label>				<textarea name="notes" id="notes"></textarea><br>
					</fieldset>
				</div>
				<button type='submit' name='SaveButton'>Save</button>
			</form>

			[:.
				pax.load.onloaded( function() {
					pax.widget.tabset.init( pax.$( 'pax.widget.tabset.example1' ), {
						height: 200,
						showToggle: true,
						tabAttribute: 'ALT'
					} );
				} );
			:]
		(end)
		This would show a tabset with two tabs.
	
	
		Note: When we init tabsets contained inside eachother, the surrounding tabset will pick up the inner tabsets title attribute, and create a new tab...
		
		Workaround: use a different tag, such as 'ALT', by setting the tabAttribute.
	
	
*/
pax.widget.tabset.init = function( element, args ) {
	args = ( typeof( args ) != 'undefined' )? args: {};
	args.tabs = ( typeof( args.tabs ) != 'undefined' )? args.tabs: [];
	args.onactivate = ( typeof( args.onactivate ) != 'undefined' )? args.onactivate: {};
	args.dynamicTabsFirst = ( typeof( args.dynamicTabsFirst ) != 'undefined' )? args.dynamicTabsFirst: true;
	
	//	Attribute used to identify a tab, and set the title of it, eg: <div title='My tab'>...</div>
	args.tabAttribute = ( typeof( args.tabAttribute ) != 'undefined' )? args.tabAttribute: pax.widget.tabset.defaultTabAttribute;

	//	Create a unique ID for the tabset
	var uniqueTabsetID = pax.getNextId() + '_tabset';
	
	//	Create a temporary container, and element DIV for our tab-elements
	pax.widget.tabset.elementTabs = [];
	var myTempDiv = pax.util.genElement('DIV', { id: uniqueTabsetID + 'tempContainer' } );
	pax.$(document.body).appendChild( myTempDiv );
	
	//	Iterate through elements in the element, and use the elements that have a predetermined attribute, as tabs.
	var staticTabs = [];
	var staticElements = [];
	var elementList = element.getElementsByTagName('*');
	
	//	Only use elements that have the tabAttribute
	//	TODO: Perhaps just hide the unwanted elements?
	for (var i in elementList) {
		var ele = elementList[i];
		//	We use the specified attribute to identfy a tab, default is 'title'
		if (ele && ele.getAttribute ) {
			var title = ele.getAttribute( args.tabAttribute );
			if( title && title != '' )staticElements.push(ele);
		}
	}
	
	//	Iterate on relevant elements
	for( var i = 0; i < staticElements.length; i++ ) {
		var ele = staticElements[i];
		
		//	Get title, and create the tab, including an onactivate function
		var title = ele.getAttribute( args.tabAttribute );
		var onactivate = ( pax.util.hasKey( title, args.onactivate ) )? args.onactivate[title]: false;
		
		//	Move tab elements into temp element, to allow us to use a template on top of the main element.
		myTempDiv.appendChild( ele );
		
		staticTabs.push( {
			title: title, 
			//	content: ele.innerHTML,
			content: ele,				//	Using the complete element.
			onactivate: onactivate
		} );
	}
	
	//	Add static and dynamic tabs (in the correct order)
	if(	args.dynamicTabsFirst ) {
		for( var t = 0; t < staticTabs.length; t++ )args.tabs.push( staticTabs[t] );
	} else {
		var newTabs = staticTabs;
		for( var t = 0; t < args.tabs.length; t++ )newTabs.push( args.tabs[t] );
		args.tabs = newTabs;
	}
	
	//	The model
	var model = pax.defaultArgs( {
		tabAttribute: pax.widget.tabset.defaultTabAttribute,
		tabs: [],
		tabID: [],
		width: 600,
		height: 400,
		selectedTab: 0,
		onactivate: {},
		showToggle: false,
		toggleAll: false,
		element: element,
		renderTabContent: true,		//	If we should render tabs contents (ie: treat it as a template automatically?)
		uniqueID: uniqueTabsetID,

		bindClasses: {
			pTabsetHeader: {
				click: function(e) {
					var target = e.target || window.event.srcElement;
					if( ! pax.util.hasClassName( target, 'headerContent' ) )return false;
					var tabID = target.id;
					pax.$(tabID).model.showTab( tabID );
				},
				mouseover: function(e) {
					var target = e.target || window.event.srcElement;
					pax.util.addClassName( target, 'pTabsetHover' );
				},
				mouseout: function(e) {
					var target = e.target || window.event.srcElement;
					pax.util.removeClassName( target, 'pTabsetHover' );
				}
			}
		},
		
		//	Internally bound IDs
		internalBindIds: {
			toggle: {
				click: function( e ) {
					if( model.toggleAll ) {
						//	Show the selected tab
						model.showTab( model.tabID[model.selectedTab] );
						model.toggleAll = false;
					} else {
						//	Show all tabs
						for( var i in model.tabID ) {
							model.showTab( model.tabID[i], true );
						}
						model.toggleAll = true;
					}
				}
			}
		},
		
		_show: function( tabID ) {
			pax.$( tabID ).style.display = '';
		},
		
		_hide: function( tabID ) {
			pax.$( tabID ).style.display = 'none';
		},
		
		//	Shows a tab, when header is clicked.
		showTab: function( tabID, toggleMode ) {
			toggleMode = ( toggleMode != null )? toggleMode : false;
			//	Warning: using naming convention here... Hide all tabs, bar the one with tabID
			var targetTabContentID = tabID.replace( '_tab_', '_tabcontent_' );
			var tabContentContainerID = model.uniqueID + '_tabcontainer';
			
			for( var i in this.tabID ) {
				var tabContentID = this.tabID[i].replace( '_tab_', '_tabcontent_' );
				if( this.tabID[i] == tabID ) {
					//	If we're using lazy loading for ajax tabs...
					//	Note: This is loaded in the initial rendring, if not lazy.
					if( pax.util.getType( this.tabs[i].content ) == 'object' ) {
						var con = this.tabs[i].content;
						//	Set overflow to auto on scroll container, as we remove this for iFrames. Also reset class for the content container
						pax.util.addClassName( pax.$( this.uniqueID + '_tabscroll' ), 'pTabsetScroll' );
						pax.util.removeClassName( pax.$( tabContentID ), 'pTabsetContentIFrame' );
						pax.util.addClassName( pax.$( tabContentID ), 'pTabsetContent' );
						
						if( con.lazy ) {
							//	Note: using GET, the browser will cache the content by default
							pax.get( con.url, function( xml, txt, url ) {
								//	Assume we have a template, and render it - not sure where we get data for the template from?
								pax.template.render( txt, { target: pax.$( targetTabContentID ) } );
							}, 'Lazy load ' + con.url, false, false );
						} else if( con.frame ) {
							//	Using a frame to display content, remove scrollbars of container, as the iframe uses 
							//	it's own; We automatically restore the scrollbars, once we go to a different tab.
							if( ! toggleMode )pax.util.removeClassName( pax.$( this.uniqueID + '_tabscroll' ), 'pTabsetScroll' );
							//	Set the class for the container to fit iframe content
							pax.util.swapClassName( pax.$( tabContentID ), 'pTabsetContent', 'pTabsetContentIFrame' );
						}
					}
					if( ! toggleMode ) {
						pax.util.addClassName( pax.$( this.tabID[i] ).parentNode, 'pTabsetHeaderShown' );
						model.selectedTab = i;
					}
					//	onBeforeActivate
					this._show( tabContentID );
					//	onActivate
					if( pax.util.getType( this.tabs[i].onactivate ) == 'function' )this.tabs[i].onactivate( tabID );
				} else {
					pax.util.removeClassName( pax.$( this.tabID[i] ).parentNode, 'pTabsetHeaderShown' );
					if( ! toggleMode )this._hide( tabContentID );
				}
			}
		}
	}, args );

	//	Initialise the widget
	var tabset = pax.widget.init( {
		model: model,
		template: function() { return pax.widget.tabset.template( tabset ) },
		target: pax.$( model.element )
	} );
	
	model.tabset = tabset;
	tabset.render();

	//	Add the model into each tab element
	for( var i in model.tabID )pax.$( model.tabID[i] ).model = tabset.model;

	//	Attach the elements - we only do this to allow iFrames in IE, for HTML editors such as 
	//	TinyMCE and FCKEditor
	for( var i = 0; i < pax.widget.tabset.elementTabs.length; i++ ) {
		var xElement = pax.widget.tabset.elementTabs[i]['element'];
		if( xElement ) {
			var xTarget = pax.$( pax.widget.tabset.elementTabs[i]['tabID'] );
			while (xElement.childNodes.length > 0) 
				xTarget.appendChild(xElement.firstChild);
		}
	}

	//	Reset container and remove element
	pax.util.removeElement( myTempDiv );

	tabset.internalBindIds( element, tabset.model.internalBindIds );	// Bind specific IDs, automatically post-fixed with '_' + grid.model.target.id
	tabset.bindClassNames( element, tabset.model.bindClasses );			// Bind specific class names 
	
	//	Show the selectedTab
	if( model.tabs.length > 0 ) {
		model.showTab( model.tabID[model.selectedTab] );
	} else {
		pax.criticalError( 'pax.widget.tabset.init: No tabs defined for ID: [' + element.id + '], please check your initialisation function.' );
	}
	
	return tabset;
};


/*	Method: pax.widget.tabset.destroy
	This is a private method to clean up the DOM, and any widgets that are used here
*/
pax.widget.tabset.destroy = function( tabset ) {
	pax.box.hide( pax.$(tabset.model.element.id + '_tabset') );
	tabset._destroy();
};

/*	Method: pax.widget.tabset.template
	Returns a PAX template for the tabset
	
*/
pax.widget.tabset.template = function( tabset ) {

	var tabsetTemplate = "" +
		"<div style='width: [:= tabset.model.width :][:= ( typeof( tabset.model.width ) != 'string' )? 'px': '' :]' class='pTabset'>" +
		"	<div class='pTabsetHeader'>" +
		"		<div style='width: [:= tabset.model.width :]'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr width='100%'>" +
		"					<td width='100%'>" +
		"						<table cellpadding='0' cellspacing='0'>" +
		"							<tr>" +
		"								[:	for( var i = 0; i < tabset.model.tabs.length; i++ ) { var tab = tabset.model.tabs[i];" +
		"										if( !pax.util.hasValue( tab.title, tabset.model.ignoreCols) ) {" +
		"											var tabID = tabset.model.uniqueID + '_tab_' + i;" +
		"								:]" +
		"								<td>" +
		"									<div class='pTabsetCell headerContent' unselectable='on' id='[:= tabID :]'>[:= _caps( tab.title ) :]</div>" +
		"								</td>" +
		"								[:	tabset.model.tabID.push( tabID );	" +	//	Add the tab ID into the model
		"										}" +
		"									}" +
		"								:]" +
		"							</tr>" +
		"						</table>" +		
		"					</td>" +
		"					[: if( tabset.model.showToggle ) { :]" +
		"					<td><div id='toggle_[:= element.id :]'>&nbsp;</div>" +	//	Add toggle button
		"					</td>" +
		"					[: } :]" +
		"					[:. " +
		"						if( tabset.model.showToggle ) {" +
		"							tabset.useWidget(pax.widget.button.init( pax.$( 'toggle_' + element.id ), { " +
		"								baseClass: 'pButtonWindow arrowDown', " +
		"								showChrome: false, " +
		"								width: '18px', " +
		"								height: '18px' " +
		"							} ));" +
		"						}" +
		"					:] " +
		"				</tr>" +
		"			</table>" +		
		"		</div>		" +
		"	</div>" +
		"	<div class='pTabsetScroll' id='[:= tabset.model.uniqueID :]_tabscroll' style='height: [:= tabset.model.height :][:= ( typeof( tabset.model.height ) != 'string' )? 'px': '' :]'>" +
		"		<div id='[:= tabset.model.uniqueID :]_tabcontainer'> :]" +
		"		[:	for( var i = 0; i < tabset.model.tabs.length; i++ ) { var con = tabset.model.tabs[i].content; var thisTabId = tabset.model.uniqueID + '_tabcontent_' + i;	:]" +
		"			<div class='pTabsetContent' id='[:= thisTabId :]' style='display: none'>" +
		"			[:	if( pax.util.getType( con ) == 'element' ) { " +	//	If con is element, push into element tabs list
		"					pax.widget.tabset.elementTabs.push( { tabID: thisTabId, element: con } ); " +
		"			} else if( pax.util.getType( con ) == 'object' ) { :]" +	//	If con is obj use ajax or iframe, else render
		"				[: if( con.lazy == true ) {	:]" +
		"					Loading, please wait..." +	//	Lazy loading is done in the showTab method.
		"				[: } else if( con.url ) {	:]" +
		"					[:= load( con.url, 'Loading ajax tab' ) :]" +
		"				[: } else if( con.frame ) {	:]" +
		"					<iframe src='[:= con.frame :]' frameBorder='0' scrolling='auto' style='border: 0; padding: 0; margin: 0; width: [:= tabset.model.width :]; height: [:= tabset.model.height :]px;'></iframe>" +
		"				[: }	:]" +
		"			[:	} else if( pax.util.getType( con ) != false ) {	:]" +
		"				[:= (tabset.model.renderTabContent)? push( con, tabset ): con :]" +
		"			[:	}	:]" +
		"			</div>" +
		"		[:	}	:]" +
		"		</div>" +
		"	</div>" +
		"</div>" +
	"";
	
	return tabsetTemplate;
};
/* ----------------------------------------------------------------------------

	pax.widget.menu.js Copyright (C) 2002, 2004, 2005, 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.widget.menu
		This is a menu widget, that can render in various ways
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
	Note:
		This widget is not ready for use in production yet.

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.menu = pax.widget.menu || {};
pax.widget.menu.dropdown = pax.widget.menu.dropdown || {};
pax.widget.menu.tree = pax.widget.menu.tree || {};
/*
	TODO:
		* Fix the CSS for IE, for both tree and dropdown
*/


/*	Private Method: pax.widget.menu.initModel
	Creates a HTML version of a dropdown menu, from a JSON object
	
	Note: the model is defined as objects within objects, where a string value is an item, and an object value is a submenu
	For example, the following menu will have one item, and one submenu:
	
		var model = {
			'1': 'Item value 1',
			'My submenu': {
				'1': 'Submenu Item value 1',
				'2': 'Submenu Item value 2'
			}
		};
	
	Here is a more extensive example:
	
		var menuModel = {
			'Entre': {
				'0': 'Garlic bread',
				'1': 'Mini Ceasar salad'
			},
			'Mains': {
				'0': 'Roast Lamb',
				'1': 'Grill Chicken',
				'2': 'Sirloin steak',
				'Main specials of the day': {
					'0': 'Fish (ask waiter for details)',
					'1': 'Chicken (a bit like the fish ;o)',
					'Not so specials': {
						'0': 'Yesterdays Tuna melt',
						'1': 'Yesterdays Tuna cheesey',
						'2': 'Yesterdays Tuna gorganzola',
						'3': 'Yesterdays Tuna chunky gorda glut'
					}
				}
			},
			'Specials': {
				'0': 'Tuna melt',
				'1': 'Tuna cheesey',
				'2': 'Tuna gorganzola',
				'3': 'Tuna chunky gorda glut'
			}
		};
		
	Note: simply use hyperlinks to make the items go somewhere
	
*/
pax.widget.menu.initModel = function( model, addAnchor, level ) {
	level = level || 0;
	var tpl = '';
	var styleStr = ( pax.isIe )? ' style="zoom: 1;"': '';
	for( var i in model ) {
		if( typeof( model[i] ) != 'string' ) {
			var menuTitle = ( ( level < 0 || addAnchor ) && i.toLowerCase().indexOf('<a') == -1 )? '<a>' + i + '</a>': i;
			tpl += '<li' + styleStr + '>' + menuTitle + '<ul>'+ pax.widget.menu.initModel( model[i], addAnchor, level + 1 ) + '</ul></li>';
		} else {
			var menuItem = ( model[i].toLowerCase().indexOf('<a') == -1 )? '<a>' + model[i] + '</a>': model[i];
			tpl += '<li' + styleStr + '>' + menuItem + '</li>';
		}
	}
	return tpl;
};


/*	Method: pax.widget.menu.dropdown.init
	Creates a menu from an unordered list
	
	Parameters:
		target - The UL to make into a menu
		args - optional argument object to pass to the menu function, ie: {}
		* type - what type of menu to create, defaults to 'dropdown'
		* vertical - boolean to set orientation of menu, defaults to false, ie: horizontal. This option is only valid for 'dropdown' menu type

	Example:
		(start code)
			<fieldset><legend>dropdown menu</legend>
				<ul id="pax.widget.menu.dropdown.example1">
					<li>Entree
						<ul>
							<li><a>Garlic bread</a></li>
							<li><a>Mini Ceasar salad</a></li>
						</ul>
					</li>
					<li>Mains
						<ul>
							<li><a>Roast Lamb</a></li>
							<li><a>Grill Chicken</a></li>
							<li><a>Sirloin steak</a></li>
							<li>Specials of the day
								<ul>
									<li><a>Fish (ask waiter for details)</a></li>
									<li><a>Chicken (like the fish ;o)</a></li>
								</ul>
							</li>
						</ul>
					</li>
				</ul>
			</fieldset>

			[:.
				pax.widget.menu.dropdown.init( pax.$("pax.widget.menu.dropdown.example1") );
			:]
		(end)
		This will show a simple example of a dropdown menu


	Example:
		(start code)
			<ul id="pax.widget.menu.example2">
				<li>Entree
					<ul>
						<li><a href="#">Garlic bread</a></li>
						<li><a href="#">Oysters kilpatrick</a></li>
						<li><a href="#">Mini Ceasar salad</a></li>
					</ul>
				</li>
				<li>Mains
					<ul>
						<li><a href="#">Roast Lamb</a></li>
						<li><a href="#">Grill Chicken</a></li>
						<li><a href="#">Sirloin steak</a></li>
					</ul>
				</li>
				<li>Dessert
					<ul>
						<li><a href="#">Chocolate mousse</a></li>
						<li><a href="#">Creme brulee</a></li>
						<li>Coffee & tea
							<ul>
								<li><a href="#">Long black</a></li>
								<li><a href="#">Capuccino</a></li>
								<li><a href="#">Latte</a></li>
								<li>Liquor coffes
									<ul>
										<li><a href="#">Kaluah</a></li>
										<li><a href="#">Tia Maria</a></li>
										<li><a href="#">Frangelico</a></li>
										<li><a href="#">Butter Scotch</a></li>
									</ul>
								</li>
								<li><a href="#">English breakfast tea</a></li>
								<li><a href="#">Japanese green tea</a></li>
								<li><a href="#">camomile tea</a></li>
							</ul>
						</li>
						<li><a href="#">Surprise of the day</a></li>
					</ul>
				</li>
			</ul>
			
			[:.
				pax.widget.menu.dropdown.init( pax.$('pax.widget.menu.example2') );
			:]
		(end)
		This renders the UL as a drop down menu
*/
pax.widget.menu.dropdown.init = function( target, args ) {
	args = (typeof(args) != 'undefined' )? args: {};
	var menuIsHorizontal = (typeof(args.vertical) != 'undefined')? ! args.vertical: true;
	
	//	If we have a model, use it as the structure for the menu
	if( args.model ) {
		var tpl = "<ul>" + pax.widget.menu.initModel( args.model, true ) + "</ul>";
		pax.template.render( tpl, { target: target } );
		//	The "actual" target is the first UL in the "target", so we get it.
		var targetUls = target.getElementsByTagName('ul');
		target = targetUls[0];
	}
	
	//	Note: this class name must go on the first UL
	pax.util.addClassName( target, 'paxMenuDropDown' );
	if( menuIsHorizontal )pax.util.swapClassName( target, 'vertical', 'horizontal' );
	else pax.util.swapClassName( target, 'horizontal', 'vertical' );
	
	//	Initialise from the DOM
	var lis = target.getElementsByTagName('li');
	for( var i = 0; i < lis.length; i++ ) {
		var li = lis[i];
		var uls = li.getElementsByTagName('ul');
		if( uls && uls.length != 0 ) {
			var ul = uls[0];
			li.sub = ul;	//	Expando for the sub menu.
			
			//	Workaround for IE - we MUST have layout for this to work
			if( pax.isIe && ! li.currentStyle.hasLayout )li.style.zoom = 1;
			
			//	Bind events
			pax.event.bind( li, 'mouseover', pax.widget.menu.dropdown.showSubMenu );
			pax.event.bind( li, 'mouseout', pax.widget.menu.dropdown.hideSubMenu );

			li.isRootMenu = li.parentNode == target;
			li.isHorizontal = ( menuIsHorizontal && li.isRootMenu );

			//	Make sure this is not the Root menu
			if( li.parentNode != target ) {
				var ahrefs = li.getElementsByTagName('a');
				var ahr = ahrefs[0];
				//	Add the menuSub class name to the anchor
				if( ahr )pax.util.addClassName( ahr, 'menuSub' );
			}
		}
	}
};


/*	Private Method: pax.widget.menu.dropdown.showSubMenu
	Shows a dropdown submenu	
*/
pax.widget.menu.dropdown.showSubMenu = function() {
	pax.util.addClassName( this, 'active' );
	var pos = pax.util.getPosition( this );
	
	if( this.isRootMenu ) {
		this.sub.style.left = ( pos.x + (!this.isHorizontal?pos.width:0) ) + 'px';
		this.sub.style.top =  ( pos.y + (this.isHorizontal?pos.height:0) - (this.isRootMenu? 0: 1) ) + 'px';
	} else {
		this.sub.style.left = this.offsetWidth + 'px';
		this.sub.style.top = this.offsetTop + 'px';
	}
	this.sub.style.visibility = 'visible';
};


/*	Private Method: pax.widget.menu.dropdown.hideSubMenu
	Hides a dropdown submenu	
*/
pax.widget.menu.dropdown.hideSubMenu = function() {	
	this.sub.style.visibility = 'hidden';
	pax.util.removeClassName( this, 'active' );
};


/*	Method: pax.widget.menu.tree.init
	Creates a menu tree from an unordered list
	
	Parameters:
		target - The UL to make into a menu
		args - optional argument object to pass to the menu function, ie: {}
		* type - what type of menu to create, defaults to 'dropdown'
		
	Example:
		(start code)
			<fieldset><legend>Tree menu</legend>
				<ul id="pax.widget.menu.tree.example1">
					<li>Entree
						<ul>
							<li><a>Garlic bread</a></li>
							<li><a>Mini Ceasar salad</a></li>
						</ul>
					</li>
					<li>Mains
						<ul>
							<li><a>Roast Lamb</a></li>
							<li><a>Grill Chicken</a></li>
							<li><a>Sirloin steak</a></li>
							<li>Specials of the day
								<ul>
									<li><a>Fish (ask waiter for details)</a></li>
									<li><a>Chicken (like the fish ;o)</a></li>
								</ul>
							</li>
						</ul>
					</li>
				</ul>
			</fieldset>

			[:.
				pax.widget.menu.tree.init( pax.$("pax.widget.menu.tree.example1") );
			:]
		(end)
		This will show a simple example of a tree menu

	Example:
		(start code)
			<button onclick="pax.widget.menu.tree.openAll(pax.$('pax.widget.menu.tree.example2'))">Open All</button>
			<button onclick="pax.widget.menu.tree.closeAll(pax.$('pax.widget.menu.tree.example2'));">Close All</button>
			<input type="text" id="searchBox" value="patrick roast brulee long scotch green" size="30">
			<button onclick="pax.widget.menu.tree.search(pax.$('pax.widget.menu.tree.example2'), pax.$('searchBox').value)">Search</button>
			<button onclick="pax.widget.menu.tree.clearSearch( pax.$('pax.widget.menu.tree.example2') );">Clear</button>
			<fieldset><legend>Tree menu</legend>
				<ul id="pax.widget.menu.tree.example2">
					<li>Entree
						<ul>
							<li><a>Garlic bread</a></li>
							<li><a>Oysters kilpatrick</a></li>
							<li><a>Mini Ceasar salad</a></li>
						</ul>
					</li>
					<li>Mains
						<ul>
							<li><a>Roast Lamb</a></li>
							<li><a>Grill Chicken</a></li>
							<li><a>Sirloin steak</a></li>
						</ul>
					</li>
					<li>Dessert
						<ul>
							<li><a>Chocolate mousse</a></li>
							<li><a>Creme brulee</a></li>
							<li>Coffee & tea
								<ul>
									<li><a>Long black</a></li>
									<li><a>Capuccino</a></li>
									<li><a>Latte</a></li>
									<li>Liquor coffes
										<ul>
											<li><a>Kaluah</a></li>
											<li><a>Tia Maria</a></li>
											<li><a>Frangelico</a></li>
											<li><a>Butter Scotch</a></li>
										</ul>
									</li>
									<li><a>English breakfast tea</a></li>
									<li><a>Japanese green tea</a></li>
									<li><a>camomile tea</a></li>
								</ul>
							</li>
							<li><a>Surprise of the day</a></li>
						</ul>
					</li>
				</ul>
			</fieldset>

			[:.
				pax.widget.menu.tree.init( pax.$("pax.widget.menu.tree.example2") );
			:]
		(end)
		This will show an extensive example, with all features enabled

*/
pax.widget.menu.tree.init = function( target, args ) {
	args = args || {};
	
	//	If we have a model, use it as the structure for the menu
	if( args.model ) {
		var tpl = "<ul>" + pax.widget.menu.initModel( args.model ) + "</ul>";
		pax.template.render( tpl, { target: target } );
		//	The "actual" target is the first UL in the "target", so we get it.
		var targetUls = target.getElementsByTagName('ul');
		target = targetUls[0];
	}
	
	pax.util.addClassName( target, 'paxMenuTree' );
	
	var uls = target.getElementsByTagName("ul");
	for( var i = 0; i < uls.length; i++ ) {
		pax.util.addClassName( uls[i].parentNode, 'submenu' );
		
		//	Workaround for IE - we MUST have layout for this to work
		if( pax.isIe ) {
			var lis = uls[i].getElementsByTagName("li");
			for( var x = 0; x < lis.length; x++ ) {
				if( ! lis[x].currentStyle.hasLayout )lis[x].style.zoom = 1;
			}
		}
		
		//	Assign a function for clicking on items
		pax.event.bind( uls[i].parentNode, 'click', function( e ) {
			//	Check if the nodeType is an LI, if not, don't do anything.
			var target = e.target || window.event.srcElement;
			if( target.nodeName.toUpperCase() == "LI" ) {
				pax.widget.menu.tree.toggleSubMenu( this );
			}
			pax.event.preventpropagate( e );
		} );

		//	Find last LI item, and add the 'last' classname
		var item = uls[i].lastChild;
		while( !item.tagName || item.tagName.toLowerCase() != "li" )item = item.previousSibling;
		pax.util.addClassName( item, 'last' );
	}

	//	Find last LI item, and add the 'last' classname
	var item = target.lastChild;
	while( !item.tagName || item.tagName.toLowerCase() != "li" )item = item.previousSibling;
	pax.util.addClassName( item, 'last' );
};


/*	Private Method: pax.widget.menu.tree.closeSubMenu
	Closes a sub menu
*/
pax.widget.menu.tree.closeSubMenu = function( branch ) {
	var submenu = branch.getElementsByTagName('ul')[0];
	submenu.style.display = 'none';
	pax.util.swapClassName( submenu, 'subMenuOpen', 'subMenuClosed' );		//	Set the identifying class
	pax.util.swapClassName( branch, 'menuOpen', 'menuClosed' );				//	Set the folder icon class
};


/*	Private Method: pax.widget.menu.tree.openSubMenu
	Opens a sub menu
*/
pax.widget.menu.tree.openSubMenu = function( branch ) {
	var submenu = branch.getElementsByTagName('ul')[0];
	submenu.style.display = 'block';
	pax.util.swapClassName( submenu, 'subMenuClosed', 'subMenuOpen' );		//	Set the identifying class
	pax.util.swapClassName( branch, 'menuClosed', 'menuOpen' );				//	Set the folder icon class
};


/*	Private Method: pax.widget.menu.tree.toggleSubMenu
	Toggles a sub menu
*/
pax.widget.menu.tree.toggleSubMenu = function( branch ) {
	var submenu = branch.getElementsByTagName('ul')[0];
	if( pax.util.hasClassName( submenu, 'subMenuOpen' ) ) {
		pax.widget.menu.tree.closeSubMenu( branch );
	} else {
		pax.widget.menu.tree.openSubMenu( branch );
	}
};


/*	Private Method: pax.widget.menu.tree.openAll
	Opens all sub menus
*/
pax.widget.menu.tree.openAll = function( element ) {
	var uls = element.getElementsByTagName("ul");
	for( var i = 0; i < uls.length; i++ ) {
		pax.widget.menu.tree.openSubMenu( uls[i].parentNode );
	}
};


/*	Private Method: pax.widget.menu.tree.closeAll
	Closes all sub menus
*/
pax.widget.menu.tree.closeAll = function( element ) {
	var uls = element.getElementsByTagName("ul");
	for( var i = 0; i < uls.length; i++ ) {
		pax.widget.menu.tree.closeSubMenu( uls[i].parentNode );
	}
};


/*	Private Method: pax.widget.menu.tree.search
	Searches a menu based on specified text, and changes the nodes that match to include 
	the 'highlight' CSS class
	
	TODO: Search in LIs that contain ULs
	*UPDATE* Turns out that it's actually working, BUT the LI already has a background image, which is blocking the colour
*/
pax.widget.menu.tree.search = function( element, text ) {
	pax.widget.menu.tree.closeAll( element );
	pax.widget.menu.tree.clearSearch( element );
	var texts = text.split(' ');	//	Split by space, so we can match all
	var lis = element.getElementsByTagName("li");
	for( var i = 0; i < lis.length; i++ ) {
		//	We make the string one line only, so indexOf can be used to check for the presence of our search string
		//	Note: this is matching everything, including surrounding LIs!!! <- causes an issue in IE (not FF though, as it's CSS is correct.)
		var liText = lis[i].innerHTML.toLowerCase().split('\n').join('').split('\r').join('');
		
		//	Check if the content matches, ignoring case.
		for( var t = 0; t < texts.length; t++ ) {
			var searchText = texts[t].toLowerCase();
			if( searchText != '' ) {
				if( liText.indexOf( searchText ) != -1 ) {
					pax.util.addClassName( lis[i], 'highlight' );
					if( lis[i].getElementsByTagName("ul").length > 0 )pax.widget.menu.tree.openSubMenu( lis[i] );
					break;
				}
			}
		}
	}
};


/*	Private Method: pax.widget.menu.tree.clearSearch
	Clears the node CSS highlight classes, from the search
*/
pax.widget.menu.tree.clearSearch = function( element ) {
	var lis = element.getElementsByTagName("li");
	for( var i = 0; i < lis.length; i++ ) {
		pax.util.removeClassName( lis[i], 'highlight' );
	}
};
/* ----------------------------------------------------------------------------

	pax.widget.window.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.widget.window
		This is a window widget, that can render windows in various ways
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.window = pax.widget.window || {};

/*	Method: pax.widget.window.init
	Initialises a window
	
	Parameters:		
		target - an element to render the window into
		args - arguments for the grid widget

	Returns:
		nothing

	Example:
		(start code)
			<div id='widget.window.init.example1'>This is my window's content</div>
			[:.
				var myWindow = pax.widget.window.init( pax.$('widget.window.init.example1') );
			:]
		(end)
		This example sets up a simple window, with no options specified.
		
	Example:
		(start code)
			<div id='widget.window.init.example2'>This is my window's content</div>
			[:.
				var myWindow = pax.widget.window.init( pax.$('widget.window.init.example2'), {
					width: 200,
					height: 150
				} );
			:]
		(end)
		This example sets up a simple window, with width and height specified
		
*/
pax.widget.window.init = function( target, args ) {
	args = args || {};
	
	var _target = target;
	target = ( typeof( target ) == 'string' )? pax.$( target ): target;
	
	// Note: this doesn;t work: we MUST have a proper DOM element here, for the model...
	if( target == null )target = { id: _target, blah: 'test' };

	//	Setup the default config values for this widget (used internally for keeping state)
	//	Model of MVC
	var model = pax.defaultArgs( {
		x: 10,										// Position in the window manager defined desktop, still todo
		y: 10,										// Position in the window manager defined desktop, still todo
		width: 500,									// Width of the window (internal - not including chrome)
		height: 200,								// Height of the window - if not specified, window won't scroll
		offsetScrollWidth: 17,						// Pixels to offset for the scrollbar on the right of the grid
		showHeader: true,							// Show the header.
		showFooter: true,							// Show the footer.
		showMinButton: false,						// Show minimise button
		showMaxButton: false,						// Show the maximise button
		showClsButton: true,						// Show the close button
		showOverflow: true,							// Should we allow scrolling on the window content
		draggable: true,							// If we can move the window
		icon: '',									// Image for the icon
		header: '',									// Content of header
		//	TODO: fix the header centering
		headerAlign: 'left',						// The header alignment
		content: target.innerHTML,					// Content of the window.
		padding: 4,									// pixels to pad the window content by
		footer: '',									// Content of footer
		target: target,								// The target DOM element for the window
		targetRight: false,							// This should be an element, if we want the window to appear to the the right of the element
		
		minButtonClick: function( event, args ) {
			// TODO: Hide the window, and notify the window manager
			// If no window manager, we should just display the header of the table.
			console.log( 'min button click - should use window manager to handle this' );
		},
		maxButtonClick: function( event, args ) {
			// TODO: Maximise the window using the window manager
			console.log( 'max button click - should use window manager to handle this' );
		},
		closeButtonClick: function( event, args ) {
			pax.widget.window.cleanup( args.value );
		},
		setHeader: function( heading ) {
			pax.$( this.target.id + '_header' ).innerHTML = heading;
		}
	}, args );

	//	This creates the target
	var windowWidget = pax.widget.init( {
		model: model, 
		template: pax.widget.window.template(),
		target: target,
		render: function() {
			// We override the render method, in order to use the window manager to first display a box, and then render the template into it.
			if( this.model.targetRight ) {
				var myWindow = pax.box.showOnRight( this.target.id, '', 'pWindow', this.model.targetRight );
			} else {
				var myWindow = pax.box.show( this.target.id, '', 'pWindow', this.model.x, this.model.y );
			}
			
			//	Target fix up
			this.target = myWindow;
			this.model.target = myWindow;
			
			pax.template.render( this.template, { target: myWindow, value: this.model } );
			
			return myWindow;
		}
	} );

	//	Set the ID, so it is visible in the model, for rendering in the template, and setting handles, etc...
	windowWidget.model.paxWidgetId = windowWidget.paxWidgetId;
	windowWidget.model.windowWidget = windowWidget;
	windowWidget.render();
	
	return windowWidget;
};


/*	Private method: pax.widget.window.template
	Returns a PAX template for the window
*/
pax.widget.window.cleanup = function( model ) {
	model.windowWidget.destroy();
	pax.box.hide( model.target );
};


/*	Private method: pax.widget.window.template
	Returns a PAX template for the window
*/
pax.widget.window.template = function() {
	var myTemplate = "" +
	// "<div style='width: [:= width :]px'>";
	"<div style='width: [:= (width + 12) :]px'>";	//	Ok, the window is now 12 pixels, plus the border = 14px wider than specified width. 
	
	myTemplate += pax.widget.window.templateHeader();
	
	myTemplate += "" +
		"<div class='pWindowBody' style='width: [:= width + offsetScrollWidth -5 :]'>" +
		// This works for both IE and FF
		"	<div class='pWindowScroll' style='height: [:= height - ( pax.isIe? -2: ( padding * 2) ) :];padding:[:= padding :];[:= !showOverflow ? 'overflow: hidden;': '' :]'>" +
		"		<div id='[:= target.id :]_content'>" +		
		"			[:= content :]" +
		"		</div> " +
		"	</div>" +
		"</div>" +
	"";
		
	myTemplate += pax.widget.window.templateFooter();

	myTemplate += "" +
		"[:. if( draggable ) { " +
		"		windowWidget.addToBindQueue( pax.fx.drag( target, { " +
		"			handle: pax.$( paxWidgetId + '_handle' )";
	if( pax.isIe ) {	//	We do this to insert the hidden iFrame (shim) below the window, so it appears above select boxes
		myTemplate += "" +
		"			," +
		"			callMove: function( args ) { pax.box.setPosition( args.args.element, args.x, args.y ); }," +
		"			callBack: function( args ) { args.args.element.iFrame = null; }";
	}
	myTemplate += "" +
		"		} ) );" +
		"	} " +
		":]" +
	
	"</div>";
	
	
	return myTemplate;
};


pax.widget.window.templateHeader = function() {
	var myTemplate = "" +
		"	<div class='pWindowHeader' style='width: [:= width + offsetScrollWidth -5 :]'>" +
		"		<div style='width: [:= width + offsetScrollWidth -5 :]'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					<td class='pWindowTD header_left' width='5px'>" +
		"						<div class='pWindowCell header_left' unselectable='on'>&nbsp;</div>" +
		"					</td>" +
		"					[: if( icon != '' ) { :]" +
		"					<td class='pWindowTD icon' width='20px'>" +
		"						<div class='pWindowCell header headerContent' unselectable='on'><img src='[:= icon :]'></div>" +
		"					</td>" +
		"					[: } :]" +
		"					<td id='[:= paxWidgetId + '_handle' :]' class='pWindowTD' width='[:= (width + offsetScrollWidth -60 -5 -5) - ( (icon != '')? 20: 0) :]px'>" +
		
		/*
			TODO:
		
			OK, we need to calculate how to center the text - we should extend it to 
			fit however wide we can, and then calculate how much to indent by.
			ie: if we're not showing min / max button, or icon, we need to indent by that 
			much, instead of showing empty containers, as we do with the buttons presently.
		*/
		
		"						<div id='[:= target.id :]_header' style='[:= 'text-align: ' + headerAlign :]' class='pWindowCell header headerContent' unselectable='on'>[:= header :]</div>" +
		"					</td>" +
		"					<td class='pWindowTD' width='60px'>" +
		"						<div class='pWindowCell header headerContent' unselectable='on'>" +
		"							<table cellpadding='0' cellspacing='0'>" +
		"								<tr>" +
		"									<td width='20px'><span id='minButton_[:= target.id :]'></span></td>" +
		"									<td width='20px'><span id='maxButton_[:= target.id :]'></span></td>" +
		"									<td width='20px'><span id='clsButton_[:= target.id :]'></span></td>" +
		" 									[:." +
		"										if( showMinButton )windowWidget.useWidget( pax.widget.button.init( pax.$('minButton_' + target.id), { height: '28px', func: function(e){minButtonClick( e, args )}, content: '&nbsp;', width: '20px', showChrome: false, baseClass: 'pButtonWindow min' } ) );" +
		"										if( showMaxButton )windowWidget.useWidget( pax.widget.button.init( pax.$('maxButton_' + target.id), { height: '28px', func: function(e){maxButtonClick( e, args )}, content: '&nbsp;', width: '20px', showChrome: false, baseClass: 'pButtonWindow max' } ) );" +
		"										if( showClsButton )windowWidget.useWidget( pax.widget.button.init( pax.$('clsButton_' + target.id), { height: '28px', func: function(e){closeButtonClick( e, args )}, content: '&nbsp;', width: '20px', showChrome: false, baseClass: 'pButtonWindow cls' } ) );" +
		" 									:]" +
		"								</tr>" +
		"							</table>" +
		"						</div>" +
		"					</td>" +
		"					<td class='pWindowTD header_right' width='5px'>" +
		"						<div class='pWindowCell header_right' unselectable='on'>&nbsp;</div>" +
		"					</td>" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
	"";
	return myTemplate;
};

pax.widget.window.templateFooter = function() {
	var myTemplate = "" +
		"[: if( showFooter ) { :]" +
		"	<div class='pWindowFooter' style='width: [:= width + offsetScrollWidth -5 :]'>" +
		"		<div style='width: [:= width + offsetScrollWidth -5 :]'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					<td class='pWindowTD footer_left' width='5px'>" +
		"						<div class='pWindowCell footer_left' unselectable='on'>&nbsp;</div>" +
		"					</td>" +
		"					<td class='pWindowTD' width='[:= width + offsetScrollWidth :]'>" +
		"						<div class='pWindowCell footer'>" +
		"							<table cellpadding='0' cellspacing='1' border='0' width='100%'><tr>" +
		"								<td><span id='[:= target.id :]_footer'>[:= push( footer, {} ) :]</span></td>" +
		"							</tr></table>" +
		"						</div>" +
		"					</td>" +
		"					<td class='pWindowTD footer_right' width='5px'>" +
		"						<div class='pWindowCell footer_right' unselectable='on'>&nbsp;</div>" +
		"					</td>" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
		"[: } else { :]" +
		"[: } :]" +
	"";
	return myTemplate;
};
/* ----------------------------------------------------------------------------

	pax.date.js Copyright (C) 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.date
		This is the date parsing and manipulation library; the date formatting 
		methods in this script are from the awesome script by Baron Schwartz,
		originally published here:
	
		http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
	
		Note: The original script has been modified to comply with the PAX namespace
		
	Authors:
		Baron Schwartz <http://www.xaprb.com/>, Mikkel Bergmann <http://www.pointful.com>, 

*/

var pax = pax || {};
pax.date = pax.date || {};

pax.date.parseFunctions = { count: 0 };
pax.date.parseRegexes = [];
pax.date.formatFunctions = { count: 0 };

/*	Method: pax.date.dateFormat
	Sets the expected format for a date object. The format is like the PHP date syntax.
:    
:    Sample date: 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
:    
:    Format  Output      Description
:    ------  ----------  --------------------------------------------------------------
:      d      10         Day of the month, 2 digits with leading zeros
:      D      Wed        A textual representation of a day, three letters
:      j      10         Day of the month without leading zeros
:      l      Wednesday  A full textual representation of the day of the week
:      S      th         English ordinal day of month suffix, 2 chars (use with j)
:      w      3          Numeric representation of the day of the week
:      z      9          The julian date, or day of the year (0-365)
:      W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
:      F      January    A full textual representation of the month
:      m      01         Numeric representation of a month, with leading zeros
:      M      Jan        Month name abbreviation, three letters
:      n      1          Numeric representation of a month, without leading zeros
:      t      31         Number of days in the given month
:      L      0          Whether it's a leap year (1 if it is a leap year, else 0)
:      Y      2007       A full numeric representation of a year, 4 digits
:      y      07         A two digit representation of a year
:      a      pm         Lowercase Ante meridiem and Post meridiem
:      A      PM         Uppercase Ante meridiem and Post meridiem
:      g      3          12-hour format of an hour without leading zeros
:      G      15         24-hour format of an hour without leading zeros
:      h      03         12-hour format of an hour with leading zeros
:      H      15         24-hour format of an hour with leading zeros
:      i      05         Minutes with leading zeros
:      s      01         Seconds, with leading zeros
:      O      -0600      Difference to Greenwich time (GMT) in hours
:      T      CST        Timezone setting of the machine running the code
:      Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)

	Example:
		(start code)
			<div id='date.format.example1'></div>
			[:.
				var myDate = new Date();
				pax.$('date.format.example1').innerHTML = myDate.dateFormat( 'D M Y' );
			:]
		(end)
		This will display todays date as "DDD MMM YYYY"

*/
pax.date.dateFormat = function( date, format ) {
	if( pax.date.formatFunctions[format] == null )pax.date.createNewFormat( date, format );
	var func = pax.date.formatFunctions[format];
	return pax.date[func]( date );
};

pax.date.createNewFormat = function( date, format ) {
	var funcName = "format" + pax.date.formatFunctions.count++;
	pax.date.formatFunctions[format] = funcName;
	
	var code = "pax.date." + funcName + " = function( date ) { return ";
	var special = false;
	var ch = '';
	
	for( var i = 0; i < format.length; ++i ) {
		ch = format.charAt( i );
		if( ! special && ch == "\\" )special = true;
		else if ( special ) {
			special = false;
			code += "'" + pax.date.escape(ch) + "' + ";
		} else code += pax.date.getFormatCode(ch);
	}
	
	eval( code.substring( 0, code.length - 3 ) + ";}" );
};

pax.date.getFormatCode = function(c) {
	return	(c == 'd')? "pax.date.leftPad(date.getDate(), 2, '0') + ":
			(c == 'D')? "pax.date.dayNames[date.getDay()].substring(0, 3) + ":
			(c == 'j')? "date.getDate() + ":
			(c == 'l')? "pax.date.dayNames[date.getDay()] + ":
			(c == 'S')? "pax.date.getSuffix( date ) + ":
			(c == 'w')? "date.getDay() + ":
			(c == 'z')? "pax.date.getDayOfYear( date ) + ":
			(c == 'W')? "pax.date.getWeekOfYear( date ) + ":
			(c == 'F')? "pax.date.monthNames[date.getMonth()] + ":
			(c == 'm')? "pax.date.leftPad(date.getMonth() + 1, 2, '0') + ":
			(c == 'M')? "pax.date.monthNames[date.getMonth()].substring(0, 3) + ":
			(c == 'n')? "(date.getMonth() + 1) + ":
			(c == 't')? "pax.date.getDaysInMonth( date ) + ":
			(c == 'L')? "(pax.date.isLeapYear( date ) ? 1 : 0) + ":
			(c == 'Y')? "date.getFullYear() + ":
			(c == 'y')? "('' + date.getFullYear()).substring(2, 4) + ":
			(c == 'a')? "(date.getHours() < 12 ? 'am' : 'pm') + ":
			(c == 'A')? "(date.getHours() < 12 ? 'AM' : 'PM') + ":
			(c == 'g')? "((date.getHours() %12) ? date.getHours() % 12 : 12) + ":
			(c == 'G')? "date.getHours() + ":
			(c == 'h')? "pax.date.leftPad((date.getHours() %12) ? date.getHours() % 12 : 12, 2, '0') + ":
			(c == 'H')? "pax.date.leftPad(date.getHours(), 2, '0') + ":
			(c == 'i')? "pax.date.leftPad(date.getMinutes(), 2, '0') + ":
			(c == 's')? "pax.date.leftPad(date.getSeconds(), 2, '0') + ":
			(c == 'O')? "pax.date.getGMTOffset( date ) + ":
			(c == 'T')? "pax.date.getTimezone( date ) + ":
			(c == 'Z')? "(date.getTimezoneOffset() * -60) + ":
			"'" + pax.date.escape(c) + "' + ";
};

pax.date.parseDate = function( input, format ) {
	if( pax.date.parseFunctions[format] == null )pax.date.createParser( format );
	var func = pax.date.parseFunctions[format];
	return pax.date[func]( input );
};

pax.date.createParser = function(format) {
	var funcName = "parse" + pax.date.parseFunctions.count++;
	var regexNum = pax.date.parseRegexes.length;
	var currentGroup = 1;
	pax.date.parseFunctions[format] = funcName;

	var code = "pax.date." + funcName + " = function( input ){\n"
		+ "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n"
		+ "var d = new Date();\n"
		+ "y = d.getFullYear();\n"
		+ "m = d.getMonth();\n"
		+ "d = d.getDate();\n"
		+ "var results = input.match(pax.date.parseRegexes[" + regexNum + "]);\n"
		+ "if (results && results.length > 0) {";
	var regex = "";

	var special = false;
	var ch = '';
	for( var i = 0; i < format.length; ++i ) {
		ch = format.charAt( i );
		if( !special && ch == "\\" )special = true;
		else if( special ) {
			special = false;
			regex += pax.date.escape(ch);
		} else {
			obj = pax.date.formatCodeToRegex(ch, currentGroup);
			currentGroup += obj.g;
			regex += obj.s;
			if (obj.g && obj.c)code += obj.c;
		};
	}

	code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
		+ "{return new Date(y, m, d, h, i, s);}\n"
		+ "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
		+ "{return new Date(y, m, d, h, i);}\n"
		+ "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n"
		+ "{return new Date(y, m, d, h);}\n"
		+ "else if (y > 0 && m >= 0 && d > 0)\n"
		+ "{return new Date(y, m, d);}\n"
		+ "else if (y > 0 && m >= 0)\n"
		+ "{return new Date(y, m);}\n"
		+ "else if (y > 0)\n"
		+ "{return new Date(y);}\n"
		+ "}return null;}";

	pax.date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
	eval(code);
};


pax.date.formatCodeToRegex = function(c, currentGroup) {
	return	(c == 'D')? { g: 0, c:null,	s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)" }:
			(c == 'j' || 
			 c == 'd')? { g: 1, c: "d = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{1,2})" }:
			(c == 'l')? { g: 0, c: null, s: "(?:" + pax.date.dayNames.join("|") + ")" }:
			(c == 'S')? { g: 0,	c: null, s: "(?:st|nd|rd|th)" }:
			(c == 'w')? { g: 0, c: null, s: "\\d" }:
			(c == 'z')? { g: 0, c: null, s: "(?:\\d{1,3})" }:
			(c == 'W')? { g: 0, c: null, s: "(?:\\d{2})" }:
			(c == 'F')? { g: 1, c: "m = parseInt(pax.date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",	s: "(" + pax.date.monthNames.join("|") + ")" }:
			(c == 'M')? { g: 1, c: "m = parseInt(pax.date.monthNumbers[results[" + currentGroup + "]], 10);\n", s: "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)" }:
			(c == 'n' ||
			 c == 'm')? { g: 1, c: "m = parseInt(results[" + currentGroup + "], 10) - 1;\n", s: "(\\d{1,2})" }:
			(c == 't')? { g: 0, c: null, s: "\\d{1,2}" }:
			(c == 'L')? { g: 0, c: null, s: "(?:1|0)" }:
			(c == 'Y')? { g: 1, c: "y = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{4})" }:
			(c == 'y')? { g: 1, c: "var ty = parseInt(results[" + currentGroup + "], 10);\n" + "y = ty > pax.date.y2kYear ? 1900 + ty : 2000 + ty;\n", s: "(\\d{1,2})" }:
			(c == 'a')? { g: 1, c: "if (results[" + currentGroup + "] == 'am') {\n"	+ "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s: "(am|pm)" }:
			(c == 'A')? { g: 1, c:"if (results[" + currentGroup + "] == 'AM') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s: "(AM|PM)" }:
			(c == 'g' ||
			 c == 'G' ||
			 c == 'h' ||
			 c == 'H')? { g: 1, c: "h = parseInt(results[" + currentGroup + "], 10);\n", s: "(\\d{1,2})" }:
			(c == 'i')? { g: 1,	c: "i = parseInt(results[" + currentGroup + "], 10);\n", s: "(\\d{2})" }:
			(c == 's')? { g: 1, c: "s = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{2})" }:
			(c == 'O')? { g: 0, c: null, s: "[+-]\\d{4}" }:
			(c == 'T')? { g: 0,	c: null, s: "[A-Z]{3}" }:
			(c == 'Z')? { g: 0, c: null, s: "[+-]\\d{1,5}" }:
			{ g: 0, c: null, s:pax.date.escape(c) };
};

pax.date.getTimezone = function( date ) {
	return date.toString().replace(
		/^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace(
		/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3");
};

pax.date.getGMTOffset = function( date ) {
	return (date.getTimezoneOffset() > 0 ? "-" : "+")
		+ pax.date.leftPad(Math.floor(date.getTimezoneOffset() / 60), 2, "0")
		+ pax.date.leftPad(date.getTimezoneOffset() % 60, 2, "0");
};

pax.date.getDayOfYear = function( date ) {
	var num = 0;
	pax.date.daysInMonth[1] = pax.date.isLeapYear( date ) ? 29 : 28;
	for (var i = 0; i < date.getMonth(); ++i) {
		num += pax.date.daysInMonth[i];
	}
	return num + date.getDate() - 1;
};

pax.date.getWeekOfYear = function( date ) {
	// Skip to Thursday of this week, then find the first Thursday of the year
	var now = pax.date.getDayOfYear( date ) + ( 4 - date.getDay() );
	var jan1 = new Date( date.getFullYear(), 0, 1 );
	var then = ( 7 - jan1.getDay() + 4 );
	return pax.date.leftPad( ( ( now - then ) / 7 ) + 1, 2, "0" );
};

pax.date.isLeapYear = function( date ) {
	var year = date.getFullYear();
	return ( ( year & 3 ) == 0 && ( year % 100 || ( year % 400 == 0 && year ) ) );
};

pax.date.getFirstDayOfMonth = function( date ) {
	var day = ( date.getDay() - ( date.getDate() - 1 ) ) % 7;
	return ( day < 0 ) ? ( day + 7 ) : day;
};

pax.date.getLastDayOfMonth = function( date ) {
	var day = ( date.getDay() + ( pax.date.daysInMonth[ date.getMonth() ] - date.getDate() ) ) % 7;
	return ( day < 0 ) ? ( day + 7 ) : day;
};

pax.date.getDaysInMonth = function( date ) {
	pax.date.daysInMonth[1] = pax.date.isLeapYear( date ) ? 29 : 28;
	return pax.date.daysInMonth[ date.getMonth() ];
};

pax.date.getSuffix = function( date ) {
	var d = date.getDate();
	return	(d == 1 || d == 21 || d == 31 )? "st":
			(d == 2 || d == 22 )? "nd":
			(d == 3 || d == 23 )? "rd":
			"th";
};

pax.date.escape = function( string ) {
	return string.replace(/('|\\)/g, "\\$1");
};

pax.date.leftPad = function ( val, size, ch ) {
	var result = new String( val );
	if( ch == null )ch = " ";
	while( result.length < size ) {
		result = ch + result;
	}
	return result;
};

pax.date.daysInMonth = [
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
];

pax.date.monthNames = [
	"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"
];

pax.date.dayNames = [
	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
];

pax.date.dayNamesShort = [
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
];

pax.date.y2kYear = 50;

pax.date.monthNumbers = {
	Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11
};

pax.date.patterns = {
	ISO8601LongPattern:"Y-m-d H:i:s",
	ISO8601ShortPattern:"Y-m-d",
	ShortDatePattern: "n/j/Y",
	LongDatePattern: "l, F d, Y",
	FullDateTimePattern: "l, F d, Y g:i:s A",
	MonthDayPattern: "F d",
	ShortTimePattern: "g:i A",
	LongTimePattern: "g:i:s A",
	SortableDateTimePattern: "Y-m-d\\TH:i:s",
	UniversalSortableDateTimePattern: "Y-m-d H:i:sO",
	YearMonthPattern: "F, Y"
};

/*	Property: pax.date.guessCfg
	Controls how the date guesser handles matches and keywords, you can override this with a different langauge, English (Australian) is default.
		
	* seperators - Seperators for the date patterns - use '*' in a datePattern, to match a seperator
	* datePatterns - What dates to try and match; order is important; have non-seperated items last. Note that these are php-style date patterns
	* keyWord - Object with keyword as key, list of allowed matches as value
*/
pax.date.guessCfg = {
	seperators: 	[ '/', '-', '.' ],
	datePatterns: 	[ 'd*m*y', 'd*m*Y', 'Y*m*d', 'j*n*y', 'j*n*Y', 'dmy', 'dmY' ],
	keyWord: {
		today: 		[ 't', 'today' ],
		tomorrow: 	[ 'tom', 'tomorrow' ],
		yesterday: 	[ 'yes', 'yesterday' ],
		next: 		[ 'n', 'next' ],
		last: 		[ 'l', 'last' ],
		day: 		{ key: [ 'd', 'day', 'days' ] },
		week:		{ key: [ 'w', 'week', 'weeks' ] },
		month:		{ key: [ 'm', 'month', 'months' ] },
		year: 		{ key: [ 'y', 'year', 'years' ] }
	}
};

//	Add days to config
for( var d = 0; d < pax.date.dayNames.length; d++ ) {
	var day = pax.date.dayNames[d].toLowerCase();
	var shortDay = day.substr(0, 3).toLowerCase();
	pax.date.guessCfg['keyWord'][pax.date.dayNames[d]] = { key: [ day, shortDay ], value: d };
}

//	Add months to config
for( var m = 0; m < pax.date.monthNames.length; m++ ) {
	var month = pax.date.monthNames[m].toLowerCase();
	var shortMonth = month.substr(0, 3).toLowerCase();
	pax.date.guessCfg['keyWord'][pax.date.monthNames[m]] = { key: [ month, shortMonth ], value: m };
}

//	Override May, as length is 3 chars, which means that by default we add the key twice...
//	This will also need to be done, if you override the guess Config with a diffreent langauge, and an item is only 3 chars.
pax.date.guessCfg['keyWord']['May'] = { 
	key: [ 'may' ], 
	value: 4 
};

/*	Method: pax.date.guess
	Populates the date obj based on the string in the input
	
	Parameters:
		input - string to guess from. There are various commands and entities that are valid:
		extraDateFormats - date formats you may want to additionally accept
		
	Input format:
		[date] [command] [entitity]
		
	Commands:
		* "next [entity]" - finds the next occurance of [entity]
		* "last [entity]" - finds the previous occurance of [entity]
		* "+ [number] [entity]" - finds the [number] next occurance of [entity]
		* "- [number] [entity]" - finds the [number] previous occurance of [entity]
		
	Entities:
		* today - gives the current date ('t' also works)
		* tomorrow - gives tomorrows date ('tom' also works)
		* yesterday - gives yesterdays date ('yes' also works)
		* weekdays - both short and long weekdays, eg: 'mon', or 'Monday' both work
		* month names - both short and long month names, eg: 'aug', or 'August' both work
		* day - 'd' and 'days' also work
		* week - 'w' and 'weeks' also work
		* month - 'm' and 'months' also work
		* year - 'y' and 'years' also work
		
	Returns:
		Either a date object populated with the guessed date, or null, if we can't guess the date
		
	Example:
		(start code)
			var myDate = pax.date.guess( 'next friday' );
		(end)
		myDate now contains the date object of next friday
		
		(start code)
			var myDate = pax.date.guess( 't +2weeks' );
		(end)
		myDate now contains the date in 2 weeks time
		(start code)
			var myDate = pax.date.guess( 'tomorrow +1month' );
		(end)
		myDate now contains the date object of 1 month from tomorrow
		(start code)
			var myDate = pax.date.guess( 'last sunday' );
		(end)
		myDate now contains the date object of last sunday
		(start code)
			var myDate = pax.date.guess( '01-01-2001 +2years' );
		(end)
		myDate now contains the date of the 1st of January 2003
		
*/
pax.date.guess = function( input, extraDateFormats ) {
	extraDateFormats = extraDateFormats || [];
	if( pax.util.getType( extraDateFormats ) != 'array' )extraDateFormats = [extraDateFormats];
	//	Split by first space or +, if no space...
	var splitAt = input.indexOf(' ');
	var splitPlus = input.indexOf('+');
	if( splitAt > -1 ) {	//	If the plus is before the space, split at it.
		if( ( splitPlus < splitAt ) && ( splitPlus > -1 ) )splitAt = splitPlus;
	} else splitAt = splitPlus;
	
	var foundDate = null;
	var bits = ( splitAt > -1 )? [input.substr(0,splitAt), input.substr(splitAt)]: [input];
	var datePart = bits[0].toLowerCase();
	var modPart = (bits[1])? bits[1].toLowerCase(): '';

	//	Config object
	var cfg = pax.date.guessCfg;

	//	Add any extra date format to the date pattern config
	if( extraDateFormats != [] ) {
		for( var i = 0; i < extraDateFormats.length; i++ ) {
			var df = extraDateFormats[i];
			if( pax.util.arrayHasValue( df, cfg['datePatterns'] ) === false ) {
				cfg['datePatterns'].push( df );
			}
		}
	}
	
	//	Match a date from the date patterns
	for( var i = 0; i < cfg['datePatterns'].length; i++ ) {
		for( var s = 0; s < cfg['seperators'].length; s++ ) {
			var df = cfg['datePatterns'][i].split('*').join( cfg['seperators'][s] );
			var matchDate = pax.date.parseDate( datePart, df );
			if( matchDate ) {
				foundDate = matchDate;
				break;
			}
		}
		if( foundDate )break;
		else {
			//	Try and match on the WHOLE string, ie: input
			for( var s = 0; s < cfg['seperators'].length; s++ ) {
				var df = cfg['datePatterns'][i].split('*').join( cfg['seperators'][s] );
				var matchDate = pax.date.parseDate( input, df );
				if( matchDate ) {
					foundDate = matchDate;
					break;
				}
			}
			/*
				Note: we took this further, and tried to match on *any* part of the input.
				Ie: break it up by spaces, and try to macth any combination. eg:
				
					'Friday March 14, 2008 08:38:00'
					
				The matches were:
				
					'Friday'
					'Friday March'
					'Friday March 14,'
					...
					'Friday March 14, 2008 08:38:00'
					       'March'
					       'March 14,'
						   'March 14, 2008'
						   ...
					       'March 14, 2008 08:38:00'
						   
				And so on. This lead to proper "guessing", as we had multiple matches.
				It was deemed that this was too complex, and would simply confuse the end user,
				after all, they want you to guess the date for them; hence that code was removed.
			*/
		}
		if( foundDate )break;		
	}
	
	//	If we didn't match a date, we must use the whole string for parsing, so we concat it here.
	if( ! foundDate )modPart = datePart + modPart;
	
	//	Split into matched elements in order.
	var keyWordFound = [];
	
	for( var k in cfg['keyWord'] ) {
		var mod = ( pax.util.getType( cfg['keyWord'][k] ) == 'object' )? cfg['keyWord'][k]['key']: cfg['keyWord'][k];
		for( var i in mod ) {
			m = mod[i];
			//	Iterate on string, and check matches
			for( var mi = 0; mi < modPart.length; mi++ ) {
				var matchStart = (modPart.substring(mi).indexOf( m ) == 0)? mi: -1;
				if( matchStart > -1 ) {
					//	Check if it's preceeded, and followed by whitespace (ie: ' ' or ''), or preceeded by a number.
					var before = (matchStart > 0)? modPart.substr( matchStart - 1, 1 ): '';
					var after  = modPart.substr( matchStart + m.length, 1 );
					if( 	( before.match( /[0-9]/g ) || before == ' ' || before == '' || before == '+' || before == '-' ) 
						&& 	( after == ' ' || after == '' || after == '+' || after == '-' ) ) {
						keyWordFound[matchStart] = k;
					}
				}
			}
		}
	}
	
	//	Numbers are special: they don't need whitespace, and we want to match ALL instances.
	for( var x = 0; x < modPart.length; x++ ) {
		var numTest = modPart.substr( x, 1 );
		// if( modPart[x].match( /[0-9]/ ) ) {
		if( numTest.match( /[0-9]/ ) ) {
			var rest = modPart.substr( x );
			keyWordFound[x] = rest.match( /[0-9]{1,}/ )[0];
			x += keyWordFound[x].length;	//	Advance the pointer by the number of chars in the number we found.
		}
	}
	
	//	Minuses are special: they don't need whitespace, and we want to match ALL instances.
	//	Plus is simply ignored, as it is the default
	for( var x = 0; x < modPart.length; x++ ) {
		if( modPart[x] == '-' )keyWordFound[x] = '-';
	}

	//	"Flatten" the data - remove any 'undefined' entries, caused by the above parsing.
	var keyWordOrder = [];
	for( var m = 0; m < keyWordFound.length; m++ ) {
		if( keyWordFound[m] )keyWordOrder.push( { index: m, value: keyWordFound[m] } );
	}
	keyWordFound = keyWordOrder;
	
	//	Now apply the keyWord
	var func = '';
	for( var i = 0; i < keyWordFound.length; i++ ) {
		var word = keyWordFound[i]['value'];
		var prevWord = [];	//	List of words prior to the current word
		var nextWord = [];	//	List of wrods after the current word
		for( var x = 0; x < i; x++ )prevWord[x] = keyWordFound[x].value;
		for( var x = i; x < keyWordFound.length; x++ )nextWord[x] = keyWordFound[x].value;
		
		if( ! foundDate ) {	//	Look for date words, if no date found yet.
			if( word == 'today' || word == 'tomorrow' || word == 'yesterday' )foundDate = new Date();
			if( word == 'tomorrow' )foundDate.setDate( foundDate.getDate() + 1 );
			else if( word == 'yesterday' )foundDate.setDate( foundDate.getDate() - 1 );
			//	if next or last, set date as 'today', allowing for entries such as 'last april', or 'next saturday'
			if( word == 'last' || word == 'next' )foundDate = new Date();
		} else {	//	Look for mod words - calculate months and days to add / subtract.
			var addDays = 0;
			var addMonths = 0;
			
			//	Check if we have 'next' or 'last' before this; it can be within 3 previous words (allowing for number and minus)
			var hasNext = ( prevWord[i -1] == 'next' || prevWord[i -2] == 'next' || prevWord[i -3] == 'next' );
			var hasLast = ( prevWord[i -1] == 'last' || prevWord[i -2] == 'last' || prevWord[i -3] == 'last' );
			
			//	If we have a day.
			var dayIndex = pax.util.arrayHasValue( word, pax.date.dayNames );
			
			if( ! ( dayIndex === false ) ) {
				//	set the date, based on the difference between the current day and the new day...
				var firstDay = parseInt( foundDate.getDay() );
				var secondDay = parseInt( dayIndex );
				var diff = (firstDay >= secondDay)? (7 - firstDay) + secondDay: secondDay - firstDay;
				
				addDays += diff;
				
				//	Check if we have a number in front, and adjust addDays
				if( ! isNaN( prevWord[i -1] ) ) {
					var myAddDays = parseInt( ( prevWord[i -2] == '-' )? -prevWord[i -1]: prevWord[i -1] ) * 7;
					if( hasLast )myAddDays *= -1;
					addDays += myAddDays;
				} else if( hasLast ) {	//	Check for 'last' - if we're on today's day, last needs to be adjusted by 14 days
					addDays -= ( diff == 7 )? 14: 7;	
				}
			}
			
			//	If we have a month
			var monthIndex = pax.util.hasValue( word, pax.date.monthNames );
			if( monthIndex ) {
				var monthHasPassed = ( foundDate.getMonth() >= monthIndex );
				foundDate.setMonth( monthIndex );
				//	Check if we have a number in front, and set addMonths
				if( ! isNaN( prevWord[i -1] ) ){
					var numMulMonths = parseInt( ( prevWord[i -2] == '-' )? -prevWord[i -1]: prevWord[i -1] );
					if( hasNext )addMonths += (12 * numMulMonths);
					if( hasLast )addMonths -= (12 * numMulMonths);
				}
				//	Check if we have next or last, and add to months, based on if the month has passed.
				if( hasNext && monthHasPassed )addMonths += 12;
				if( hasLast && (! monthHasPassed) )addMonths -= 12;
			}
			
			//	If we have 'day' or 'week'
			if( word == 'day' || word == 'week' ) {
				var multi = ( word == 'week' )? 7: 1;
				if( ! isNaN( prevWord[i -1] ) ) {
					var numAddDays = parseInt( ( prevWord[i -2] == '-' )? -prevWord[i -1]: prevWord[i -1] );
					if( hasLast )numAddDays *= -1;
					addDays += numAddDays * multi;
				} else {
					if( hasLast )addDays -= 1 * multi;
					else addDays += 1 * multi;
				}
			}
			
			//	If we have 'month' or 'year'
			if( word == 'month' || word == 'year' ) {
				var multi = ( word == 'year' )? 12: 1;
				if( ! isNaN( prevWord[i -1] ) ){
					var numAddMonths = parseInt( ( prevWord[i -2] == '-' )? -prevWord[i -1]: prevWord[i -1] );
					if( hasLast )numAddMonths *= -1;
					addMonths += numAddMonths * multi;
				} else {
					if( hasLast )addMonths -= 1 * multi;
					else addMonths += 1 * multi;
				}
			}
			
			foundDate.setDate( foundDate.getDate() + ( addDays ) );
			foundDate.setMonth( foundDate.getMonth() + ( addMonths ) );
		}
	}

	return foundDate;
};

/* ----------------------------------------------------------------------------

	pax.widget.datepick.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.widget.datepick
		This is a widget that allows you to pick a date, plus parse text such as 'next friday'
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
*/

/*
	TODO
	----
	
	. Add time support option; a time selector should appear on the right of the box.
	
		. 	it should be a scrollbox the same height as with widget, and times in 
			1 hr, 1/2 hr or 1/4 hr times (configurable).
			
		.	Also need to update the guesser to allow a time to be entered.

	. Add keyboard support:
	
		arrow keys: to move
		page up / down: month up / down 
		space or enter: select day
		esc: cancel

	. Make it work with Safari

	BUGS
	----

	[done]	. In pax.widget.datePick.init, in FF it is not displaying the icon properly, may have something to do with "display: inline"???
			- Set the width to 200px, that seemed to fix it...
	[done]	. In pax.widget.template, fix rendering issue in IE, where there is a 8px gap on the right side.
	[done]	. In the API examples, the window stretches weirdly...
	[on hold - API to be rewritten, with correct DOCTYPE]	. In the API examples, the heading is at the top displays weirdly...
	
	. The guessing doesn't always observe the dateFormat, I think it may be the way we initialise the guessing.

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.datePick = pax.widget.datePick || {
	dateFormat: 'd-m-Y'		//	Default date format (format as defined in pax.date.js)
};


/*	Method: pax.widget.datePick.init
	Displays a button next to the specified field, that can display the calendar. Simply 

	Parameters:
		displayField - the field we want to attach the date picker to
		args - optional argument object to pass to the menu function, ie: {}
		* dateFormat - Select a date format, as specified in <pax.date>, this is the exact format that the standard PHP date libarry uses
		* selectedDate - dd-mm-yyyy formatted date to show, in case there is no date in the displayField, default is today
		* mondayFirst - boolean to choose if monday is first day of the week, default = true
		* dayNum - the day to start on
		* monthNum - the month number to start on (0 = jan, 11 = dec)
		* yearNum - the full year
		* validate - should we validate the date (using the pax validation library)
		* guess - should we guess the date using the pax date library
		* buttonOffset - object with x, y pixel offset for the button, eq: { x: 6, y: 8 }, would offset the button 6px right, 8px down
		
	Example:
		(start code)
			<input id='pax.widget.datePick.init.example1' type='text'>
			[:.
				var example1 = pax.widget.datePick.init( pax.$('pax.widget.datePick.init.example1') );
				exampleCleanup = function() {
					pax.widget.datePick.destroy( pax.$('pax.widget.datePick.init.example1') );
				};
			:]
		(end)
		This example shows a simple date picker, with all the default settings

	Example:
		(start code)
			<input id='pax.widget.datePick.init.example2' type='text' value='07-07-2007'>
			[:.
				pax.widget.datePick.init( pax.$('pax.widget.datePick.init.example2') );
				exampleCleanup = function() {
					pax.widget.datePick.destroy( pax.$('pax.widget.datePick.init.example2') );
				};
			:]
		(end)
		This example shows a simple date picker, with all the default settings, and a pre-populated field

	Example:
		(start code)
			<input id='pax.widget.datePick.init.example3' type='text'>
			[:.
				pax.widget.datePick.init( pax.$('pax.widget.datePick.init.example3'), { selectedDate: '07-07-1988' } );
				exampleCleanup = function() {
					pax.widget.datePick.destroy( pax.$('pax.widget.datePick.init.example3') );
				};
			:]
		(end)
		This example shows a simple date picker, with a specified startDate

*/
pax.widget.datePick.init = function( displayField, args ) {
	//	Add the dateformat to the field
	var dateFormat = pax.widget.datePick.dateFormat;
	if( args )dateFormat = args.dateFormat;
	if( pax.util.getType( args ) == 'string' )dateFormat = args;	//	Assume by default that we're assigning a date format.
	displayField.dateFormat = dateFormat;

	var validate = false;
	if( args && args.validate )validate = args.validate;
	displayField.validate = validate;
	
	var guess = true;
	if( args && ( typeof( args.guess ) != 'undefined' ) )guess = args.guess;
	displayField.guess = guess;
	
	//	The display field MUST float left for us to position the button correctly (as we append the button)
	var style = displayField.style || '';
	style += (style == '')? '': '; ';
	style += 'float: left;';
	displayField.setAttribute( 'style', style );
	displayField.style.styleFloat = 'left';			//	Use styleFloat, this should work in IE
	
	//	Add the button, to display the calendar.
	var myBox = pax.box.append( displayField.id + '_datePickButton', '', 'datePickButton', displayField, 'E' );
	
	//	We give focus to the field, when the box is clicked or mousedown; this is necessary for FF, as the box overlaps the field.
	pax.event.bindEventList( myBox, ['click', 'mouseDown'], function() {
		displayField.focus();
	} );
	
	//	Get button offsets in pixels
	var offsetRight = (args.buttonOffset && args.buttonOffset.x)? args.buttonOffset.x: 0;
	var offsetDown = (args.buttonOffset && args.buttonOffset.y)? args.buttonOffset.y: 0;
	
	//	Adjust left
	var left = myBox.style.left;
	left = ( left.indexOf( 'px' ) > -1 )? left.substring( 0, left.indexOf( 'px' ) ): left;
	myBox.style.left = (parseInt(left - 23) + offsetRight) + 'px';
	pax.css.opacity( myBox, 50 );	// Set opacity at 50%, so we can see text through it.
	
	//	Adjust top
	var top = myBox.style.top;
	top = ( top.indexOf( 'px' ) > -1 )? top.substring( 0, top.indexOf( 'px' ) ): top;
	myBox.style.top = (parseInt(top + 7) + offsetDown) + 'px';
	
	myBox.style.display = "";

	//	Turn the box into a button
	pax.widget.button.init( myBox, {
		baseClass: 'pButton widget datePick',
		showChrome: false,
		width: '16px',
		height: '14px',
		func: function( e ) {
			pax.widget.datePick.display( displayField, args );
			return false;
		},
		inline: false
	} );
	
	//	Initialise the validation and Date guessing function, we want both keyup and blur events
	pax.event.bindEventList( displayField, ['keyup', 'blur'], function(e) {
		e = e || window.event;
		var format = ( displayField.dateFormat )? displayField.dateFormat: 'l F j, Y';
		
		var myDate = pax.date.guess( displayField.value, format );
		var result = ( myDate )? pax.date.dateFormat( myDate, format ): 'Invalid date';
		
		//	Show guess box, if we're configured to do so.
		if( displayField.guess ) {
			var dims = pax.util.getPosition( displayField );
			var box = pax.box.show( 'datePickerGuess_' + displayField.id, result, 'pAutoCompResult', dims.x, (dims.y + dims.height) );
			pax.css.opacity( box, 75 );
		}
		
		//	If it's blur, we need to clean up the guess box, and set the date and validity 
		if( e.type == 'blur' ) {
			if( displayField.guess )pax.box.hide( 'datePickerGuess_' + displayField.id );
			pax.widget.datePick.setDateAndValidity( displayField );
		}
	} );
};
	
/*	Private Method: pax.widget.datePick.setDateAndValidity
	Uses the Date object to guess the value of the date in the field, then sets the value, and field validity, provided the validation library is available
	
*/
pax.widget.datePick.setDateAndValidity = function( displayField ) {
	//	Guess the date, allowing for the chosen date format.
	var myDate = pax.date.guess( displayField.value, displayField.dateFormat );
	var validateField = ( pax.validate && displayField.validate );
	
	if( myDate ) {
		displayField.value = pax.date.dateFormat( myDate, displayField.dateFormat );
		if( validateField )pax.validate.setValidField( displayField );
	} else {
		if( validateField ) {
			if( displayField.value != '' )pax.validate.setInvalidField( displayField );
			else pax.validate.setValidField( displayField );
		}
	}
};
	
/*	Private Method: pax.widget.datePick.display
	Displays the calendar next to the specified field
*/
pax.widget.datePick.display = function( displayField, args ) {
	args = ( typeof( args ) != 'undefined' )? args: {};
	args.partial = ( args.partial )? args.partial: false;
	var today = new Date();
	var dateFormat = args.dateFormat || pax.widget.datePick.dateFormat;
	//	Get from field value or args value. Assume format is dd-mm-yyyy for now.
	var selectedDate = ( typeof( args.selectedDate ) != 'undefined' )? args.selectedDate : false;
	var myDate = (displayField.guess)? pax.date.guess( displayField.value, dateFormat ): false;
	//	Note: 'd-m-Y' is the internally used date format for the date picker.
	//	TODO: change this to use a date obj...
	selectedDate = ( myDate )? pax.date.dateFormat( myDate, 'd-m-Y' ): selectedDate;

	var dayNum = 		( isNaN(args.dayNum) || args.dayNum == null)? today.getDate() : args.dayNum;
	var monthNum = 		( isNaN(args.monthNum) || args.monthNum == null)? today.getMonth() : args.monthNum;
	var yearNum  = 		( isNaN(args.yearNum)  || args.yearNum == null )? today.getFullYear() : args.yearNum;
	var mondayFirst = 	( typeof(args.mondayFirst) != 'undefined' )? args.mondayFirst: true;
	
	//	Extract the starting date, Note - if we don't specify 10 as the base for parseInt, then '08' and '09' will return 0
	if( selectedDate ) {
		var sMonthNum = parseInt( selectedDate.substring( 3, 5 ), 10 ) - 1;
		var sYearNum = 	parseInt( selectedDate.substring( 6, 10 ), 10 );
		dayNum = 		parseInt( selectedDate.substring( 0, 2 ), 10 );
		monthNum = 		( isNaN(args.monthNum) || args.monthNum == null)? sMonthNum: args.monthNum;
		yearNum = 		( isNaN(args.yearNum)  || args.yearNum == null )? sYearNum: args.yearNum;
	}
	
	var firstDay = new Date( yearNum, monthNum, 0 );
	var startingDay = (firstDay.getDay() > 0)? firstDay.getDay() - 1: 6;	//	Note - we assume Monday as the starting week day here - it can be overidden by setting mondayFirst = false.
	var newDate = new Date( yearNum, monthNum );
	var monthLength = pax.date.getDaysInMonth( newDate );
	var prevDate = ( monthNum == 1 )? new Date( yearNum - 1, 11 ): new Date( yearNum, monthNum - 1 );
	var prevMonthLength = pax.date.getDaysInMonth( prevDate );
	var monthName = pax.date.monthNames[monthNum];
	var dayName = pax.date.dayNamesShort;

	//	Shift the dayNames, if the week starts on a monday (default day is sunday).
	if( mondayFirst ) {
		startingDay = firstDay.getDay();
		dayName = [ dayName[1], dayName[2], dayName[3], dayName[4], dayName[5], dayName[6], dayName[0] ];
	}
	
	//	The model
	var model = pax.defaultArgs( {
		displayField: displayField,		
		dayNum: dayNum,
		monthNum: monthNum,
		monthName: monthName, 
		dateFormat: dateFormat,
		validate: false,
		selectedDate: selectedDate,
		sMonthNum: sMonthNum,
		sYearNum: sYearNum,
		monthLength: monthLength,
		prevMonthLength: prevMonthLength,
		yearNum: yearNum, 
		mondayFirst: mondayFirst,
		startingDay: startingDay,
		dayName: dayName,
		today: { d: today.getDate(), m: today.getMonth(), y: today.getFullYear() },
		datePicker: null,
		datePickerWindow: null,
		
		// Bind IDs post-fixed with '_' + model.target.id
		internalBindIds: {
			prevYear: {
				click: function( e ) {
					pax.widget.datePick.display( datePicker.model.displayField, { 
						monthNum: datePicker.model.monthNum, 
						yearNum: ( (datePicker.model.yearNum > 1)? datePicker.model.yearNum - 1 : 0 ),
						partial: true,
						datePicker: datePicker,
						mondayFirst: datePicker.model.mondayFirst,
						datePickerWindow: datePickerWindow
					} );
				}
			},
			prevMonth: {
				click: function( e ) {
					pax.widget.datePick.display( datePicker.model.displayField, { 
						yearNum: ( datePicker.model.monthNum <= 0 )? datePicker.model.yearNum - 1: datePicker.model.yearNum,
						monthNum: ( (datePicker.model.monthNum >= 1)? datePicker.model.monthNum - 1 : 11 ),
						partial: true,
						datePicker: datePicker,
						mondayFirst: datePicker.model.mondayFirst,
						datePickerWindow: datePickerWindow
					} );
				}
			},
			today: {
				click: function( e ) {
					var today = new Date();
					var day = ( today.getDate() < 10 )? '0' + today.getDate(): today.getDate();
					var month = ( (today.getMonth() + 1) < 10 )? '0' + (today.getMonth() + 1): (today.getMonth() + 1);
					var year = today.getFullYear();
					datePicker.model.displayField.value = ( day + '-' + month + '-' + year );
					pax.widget.datePick.destroy( datePicker );	//	Hide date picker
				}
			},
			nextMonth: {
				click: function( e ) {
					pax.widget.datePick.display( datePicker.model.displayField, { 
						yearNum: ( datePicker.model.monthNum >= 11 )? datePicker.model.yearNum + 1: datePicker.model.yearNum,
						monthNum: ( (datePicker.model.monthNum < 11)? datePicker.model.monthNum + 1 : 0 ),
						partial: true,
						datePicker: datePicker,
						mondayFirst: datePicker.model.mondayFirst,
						datePickerWindow: datePickerWindow
					} );
				}
			},
			nextYear: {
				click: function( e ) {
					pax.widget.datePick.display( datePicker.model.displayField, { 
						monthNum: datePicker.model.monthNum,
						yearNum: datePicker.model.yearNum + 1,
						partial: true,
						datePicker: datePicker,
						mondayFirst: datePicker.model.mondayFirst,
						datePickerWindow: datePickerWindow
					} );
				}
			},
			days: {
				click: function( e ) {
					var clickTarget = e.target || window.event.srcElement;
					var day = clickTarget.innerHTML.replace(/^\s+|\s+$/g,'').replace( '&nbsp;', '');
					if( day.length < 2 )day = '0' + day;
					var year = datePicker.model.yearNum;
					var month = datePicker.model.monthNum;

					// Here we adjust the month and year, the click target is in the previous or next month.
					if( pax.util.hasClassName( clickTarget, 'nextMonth' ) ) {
						month += 1;
						if( month > 11 ) {
							month = 0;
							year += 1;
						}
					}
					if( pax.util.hasClassName( clickTarget, 'prevMonth' ) ) {
						month -= 1;
						if( month < 0 ) {
							month = 11;
							year -= 1;
						}
					}
					
					month = (month >= 9)? (month + 1): '0' + (month + 1);
					datePicker.model.displayField.value = ( day + '-' + month + '-' + year );
					pax.widget.datePick.destroy( datePicker );	// Remove date picker
				},
				mouseover: function( e ) {
					var hoverTarget = e.target || window.event.srcElement;
					if( pax.util.hasClassName( hoverTarget, 'datePickDay' ) ) {
						pax.util.addClassName( hoverTarget, 'hover' );
						function clearCellHover(e) {
							pax.util.removeClassName( hoverTarget, 'hover' );
							pax.event.unbind( hoverTarget, 'mouseout', clearCellHover );
						};
						pax.event.bind( hoverTarget, 'mouseout', clearCellHover );
					}
				}
			}
		}
	}, args );

	/*	For the sake of efficiency, we either show a window and render the widget into it, 
		or simply update the parts we need, such as the date picker and heading.
	*/
	if( ! args.partial ) {	
		//	Attach a window here.
		var datePickerWindow = pax.widget.window.init( displayField.id + '_datePick', {
			width: 180,
			height: 136,
			padding: 0,
			headerAlign: 'right',
			showOverflow: false,
			header: monthName + ' ' + yearNum,
			targetRight: displayField,
			closeButtonClick: function( event, args ) {
				pax.widget.datePick.destroy( datePicker );
			}
		} );
		
		model.datePickerWindow = datePickerWindow;
		
		//	Initialise the widget
		var datePicker = pax.widget.init( {
			model: model,
			template: pax.widget.datePick.template(),
			target: pax.$( datePickerWindow.target.id + '_content' )
		} );

		//	If we're using IE, we MUST update the window, for the iFrame to appear below the window... 
		if( pax.isIe ) {
			var pos = pax.util.getPosition( datePickerWindow.model.target );
			pax.box.setPosition( datePickerWindow.model.target, pos.x, pos.y );
		}
		
		model.datePicker = datePicker;
		
		datePicker.useWidget( datePickerWindow );	// Register widget, so it will be destroyed on cleanup
		datePicker.render();						// Render main date picker

		//	Render the footer
		pax.template.render( pax.widget.datePick.templateFooter(), {
			target: pax.$( datePickerWindow.target.id + '_footer' ),
			value: model
		} );
		
		// Bind specific IDs, (automatically post-fixed with '_' + grid.model.target.id)
		datePicker.internalBindIds( displayField, datePicker.model.internalBindIds );
		
		/*
			Function to hide the date picker, if we mousedown outside it. 
			It MUST be mousedown, and not click, due to IE's event bubbeling order
		*/
		datePicker.bind( document, 'mousedown', function(e) {
			var target = e.target || window.event.srcElement;
			//	Need to iterate on parentNode, till we either hit 'body' or displayField
			if( target ) {
				var hidePicker = true;
				while( target ) {
					target = target.parentNode;
					if( target == datePickerWindow.target || target == pax.$( displayField.id + '_datePickButton' ) )hidePicker = false;
				}
				if( hidePicker )pax.widget.datePick.destroy( datePicker );		//	Hide date picker
			}
		} );
	} else {
		//	We only want to render the days, and set the header
		var datePicker = model.datePicker;
		var myDays = { days: datePicker.model.internalBindIds.days };
		
		datePicker.internalUnbindIds( displayField, myDays );					// Un-bind the days events
		
		pax.template.render( pax.widget.datePick.template(), { 					// Render the days
			value: model,
			partial: 'days_part_' + displayField.id
		} );

		model.datePickerWindow.model.setHeader( monthName + ' ' + yearNum );	// Set the header
		datePicker.model = model;												// Persist the model
		datePicker.internalBindIds( displayField, myDays );						// Bind the days events
	}
	
	return datePicker;
};

/*	Private Method: pax.widget.datePick.destroy
	Kill the date widget
*/
pax.widget.datePick.destroy = function( datePicker ) {
	pax.box.hide( pax.$(datePicker.model.displayField.id + '_datePick') );
	pax.widget.datePick.setDateAndValidity( datePicker.model.displayField );
	datePicker._destroy();
};

/*	Method: pax.widget.datePick.templateFooter
	Returns a PAX template for the buttons on the footer
*/
pax.widget.datePick.templateFooter = function() {
	var datePickerTemplate = "" +
		"<table width='100%'>														" +
		"	<tr>																	" +
		"		<td><div id='prevYear_[:= displayField.id :]'>&nbsp;</div></td>		" +
		"		<td><div id='prevMonth_[:= displayField.id :]'>&nbsp;</div></td>	" +
		"		<td><div id='today_[:= displayField.id :]'>&nbsp;</div></td>		" +
		"		<td><div id='nextMonth_[:= displayField.id :]'>&nbsp;</div></td>	" +
		"		<td><div id='nextYear_[:= displayField.id :]'>&nbsp;</div></td>		" +
		"	</tr>																	" +
		"</table>																	" +
		"[:. " +
		"	datePicker.useWidget(pax.widget.button.init( pax.$( 'prevYear_' + displayField.id ), { " +
		"		baseClass: 'pButtonWindow arrowReverse', " +
		"		showChrome: false, " +
		"		width: '18px', " +
		"		height: '18px' " +
		"	} ));" +
		"	datePicker.useWidget(pax.widget.button.init( pax.$( 'prevMonth_' + displayField.id ), { " +
		"		baseClass: 'pButtonWindow arrowLeft', " +
		"		showChrome: false, " +
		"		width: '18px', " +
		"		height: '18px' " +
		"	} ));" +
		"	datePicker.useWidget(pax.widget.button.init( pax.$( 'today_' + displayField.id ), { content: 'Today' } ));" +
		"	datePicker.useWidget(pax.widget.button.init( pax.$( 'nextMonth_' + displayField.id ), { " +
		"		baseClass: 'pButtonWindow arrowRight', " +
		"		showChrome: false, " +
		"		width: '18px', " +
		"		height: '18px' " +
		"	} ));" +
		"	datePicker.useWidget(pax.widget.button.init( pax.$( 'nextYear_' + displayField.id ), { " +
		"		baseClass: 'pButtonWindow arrowForward', " +
		"		showChrome: false, " +
		"		width: '18px', " +
		"		height: '18px' " +
		"	} )); " +
		":]" +
	"";

	return datePickerTemplate;
};


/*	Method: pax.widget.datePick.template
	Returns a PAX template for the calendar; this template has partial rendering for the days part
	
*/
pax.widget.datePick.template = function() {
	var datePickerTemplate = "" +
		"[:p(days_part_[:= displayField.id :])" +
		"<span id='days_[:= displayField.id :]'>" +
		"	<table class='datePick' cellspacing='0' width='100%'>" +
		"		<tr class='datePickHead'>" +
		"			[: for( var dayCount = 0; dayCount < dayName.length; dayCount++ ) { :]" +
		"			<td class='headDays'>[:= dayName[dayCount] :]</td>	" +
		"			[: } :]" +
		"		</tr>	" +
		"			[:	var day = 1;	" +
		"				for( var weekNum = 0; weekNum < 6; weekNum++ ) {	:]	" +
		"		<tr>	" +
		"			[:		for( var dNum = 0; dNum < 7; dNum++ ) {	:]	" +
		"			[:	if( day <= monthLength && (weekNum > 0 || dNum >= startingDay)) { :]	" +
		"			<td class='datePickDay[: 	if( today.d == day && 	" +
		"											today.m == monthNum &&" +
		"											today.y == yearNum ) { :]	" +
		"												[:= ' today' :]	" +
		"												[: } :]	" +
		"									[: 	if( dayNum == day && 	" +
		"											monthNum == sMonthNum && 	" +
		"											yearNum == sYearNum ) { :]	" +
		"												[:= ' selectedDay' :]	" +
		"												[:  } :]'>" +
		"				[:= (day > 9)? day: '&nbsp;' + day :]	" +
		"			[:		day += 1;	" +
		"				} else { " +
		"					if( day > 1 ) { var pmDay = (pmDay)? pmDay + 1: 1; :]" +
		"			<td class='datePickDay nextMonth'>	" +
		"				[:= '&nbsp;' + pmDay :]	" +
		"				[:	} else { :]	" +
		"			<td class='datePickDay prevMonth'>	" +
		"					[:= prevMonthLength - (startingDay - dNum - 1) :]	" +
		"					[: } :]" +
		"				[: } :]	" +
		"			</td>" +
		"			[:		}  :]" +
		"		</tr>	" +
		"			[:	} :]	" +
		"	</table>	" +
		"</span>" +
		"p:]" +
	"";
	
	return datePickerTemplate;
};

/* ----------------------------------------------------------------------------

	pax.box.js Copyright (C) 2005, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.box
		This class creates boxes for various purposes; it's a base class for window, and widget classes
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


var pax = pax || {};
pax.box = pax.box || {};

/*	Method: pax.box.show
	Shows a box at given position
	
	Parameters:
		id - DOM pointer for the window element
		message - The contents to put into the box (HTML)
		className - The CSS class name to assign to the box
		x - Horizontal (left) position for the box
		y - Vertical (top) position for the box
		target - DOM element to append the box to; if not specifed, we use the body element
		
	Example:
		(start code)
			<input type='text' id='pax.box.show.example'>
			[:.
				var pos = pax.util.getPosition( pax.$('pax.box.show.example') );
				pax.box.show( 'myWindow', '^ Hello world!', 'hintMessage', pos.x, pos.y + pos.height + 5 );
				
				exampleCleanup = function() { pax.box.hide( 'myWindow' ); };
			:]
		(end)
		This will create a box below the input box. It uses <pax.util.getPosition> to find the position of the element.
		Also note that exampleCleanup is just a hook for the example, that hides the window again, when you're done with it.
*/
pax.box.show = function( id, message, className, x, y, target, displayStyle ) {
	//	Only insert the box once.
	if( document.getElementById( id ) == null ) {
		//	Create a DIV
		var div = document.createElement('DIV');
		div.id = id;
		div.className = className;
		div.style.position = 'absolute';
		div.style.left = x;
		div.style.top = y;
		div.innerHTML = message;
		div.style.display = (displayStyle)? displayStyle: 'block';
		
		if( typeof(target) != 'undefined' )target.appendChild( div );
		else document['body'].appendChild( div );
	}
	else div = document.getElementById( id );

	if( div.style.zIndex < 1 )div.style.zIndex = 3;  // Ensure that the box has a positive zIndex.
	div.className = className;
	div.style.position = 'absolute';
	div.style.left = x + 'px';
	div.style.top = y + 'px';
	div.innerHTML = message;
	div.style.display = (displayStyle)? displayStyle: 'block';
	div.innerHTML = message;

	//	Add a transparent iFrame, so that selectboxes don't peep through in IE
	if( pax.isIe6Down ) {
		var pos = pax.util.getPosition( div );
		var frameId = div.id + '_iFrame';
		
		var myFrame = document.getElementById( frameId );
		
		// Only insert iFrame, if it doesn't already exist
		if( myFrame == null ) {
			myZIndex = div.style.zIndex - 1;      // Insert iFrame layer just below Div
			// The ugly string below is an iframe with the correct parameters.
			myFrame = "<iframe src=\"javascript:false;\" id=\"" + frameId + "\" scrolling=\"no\" width=\"" + pos.width + "px\" height=\"" + pos.height + "px\" frameborder=\"0\" style=\"position:absolute;top:" + pos.y + "px;left:" + pos.x + "px;z-index:"+myZIndex+";filter:'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';\"></iframe>";

			if( typeof(target) != 'undefined' )div.insertAdjacentHTML('beforeBegin', myFrame);
			else document.body.insertAdjacentHTML('beforeEnd', myFrame);
			
			div.frameId = frameId;		// Save the frame id
			myFrame = document.getElementById( frameId );
		}

		// Set iFrame properties to be sure!
		if( myFrame ) {
			myFrame.width = div.offsetWidth;
			myFrame.height = div.offsetHeight;
			myFrame.style.top = pos.y + "px";
			myFrame.style.left = pos.x + "px";
			myFrame.style.display = 'block';	//div.style.display;
			myFrame.style.zIndex = div.style.zIndex - 1;
		}
	}
	
	pax.box.setPosition( div, x, y );

	return div;
};


/*	Method: pax.box.append
	Shows a box at given element, appending it into the dom, rather than showing it at a specific position
	
	Parameters:
		id - DOM pointer for the window element
		message - The contents to put into the box (HTML)
		className - The CSS class name to assign to the box
		target - which element to append the box to
		location - one of N, E, S, W
		
	Example:
		(start code)
			<input type='text' id='pax.box.append.example'>
			[:.
				pax.box.append( 'myWindow', 'Hello world!', 'hintMessage',  pax.$('pax.box.append.example'), 'E' );
				exampleCleanup = function() { pax.box.hide( 'myWindow' ); };
			:]
		(end)
		This will create a box to the right of the input box.
		Note that exampleCleanup is just a hook for the example, that hides the box again, when you're done with it.
*/
pax.box.append = function( id, message, className, target, location ) {
	//	Only insert the box once.
	var box = document.getElementById( id );
	if( ! box ) {
		//	Create a DIV
		box = document.createElement('DIV');
		box.id = id;
		box.setAttribute( 'id', id );
		if( typeof(target) != 'undefined' ) {
			if( location == 'N' || location == 'W') {
				target.parentNode.insertBefore( box, target );
			} else {
				//	The div must appear AFTER the target.
				//	We check for IE here, and use it's insertAdjacentHTML method, to make sure we don't get the dreaded "Uknown runtime" error...
				if( pax.isIe ) {
					target.insertAdjacentHTML('AfterEnd', '<span id="' + id + '"></span>' );
					box = pax.$( id );
				} else {
					//	Do it the "long" way
					var span1 = document.createElement('SPAN');
					target.parentNode.insertBefore( span1, target );
					var span2 = document.createElement('SPAN');
					target.parentNode.insertBefore( span2, target );
					target.parentNode.replaceChild( target, span1 );
					target.parentNode.replaceChild( box, span2 );
				}
			}
		} else document['body'].appendChild( box );
	}

	box.className = className;
	box.style.position = 'relative';
	box.innerHTML = message;

	box.style.display = ( location == 'N' || location == 'S' )? 'block': 'inline';

	return box;
};


/*	Method: pax.box.setPosition
	Sets the position and size of a box, plus size & position of the iFrame for IE.
	
*/
pax.box.setPosition = function( box, x, y ) {
	if( box.frameId ) {
		var ifr = document.getElementById( box.frameId );
		ifr.style.left = x + 'px';
		ifr.style.top = y + 'px';
		
		var pos = pax.util.getPosition( box );
		
		ifr.style.width = pos.width;
		ifr.style.height = pos.height;
	}
	box.style.left = x + 'px';
	box.style.top = y + 'px';
};

/*	Method: pax.box.showOnRight
	Shows a box 5 pixels to the left of an element, optionally fading it out after timeout seconds.
	
	Parameters:
		id - DOM pointer to the window element
		message - The contents to put into the box
		className - The CSS class name to assign to the box
		element - The element to place the box near
		timeout - Optionally specify in seconds how long the box is visible, 0 = don't hide
		distance - Optionally specify in pixels how far away, defualt = 5px
		
	Example:
		(start code)
			<input type='text' id='pax.box.showOnRight.example'>
			[:.
				pax.box.showOnRight( 'myBox', '<- Hello world!', 'hintMessage', pax.$('pax.box.showOnRight.example'), 7 );
			:]
		(end)
		
		This will create a box to the right of myElement, and remove it after 7 seconds, (default = 5 seconds).
*/
pax.box.showOnRight = function( id, message, className, element, timeout, distance ) {
	window.clearTimeout( element.hintMessageTimeout );
	var dims = pax.util.getPosition( element );
	distance = ( typeof(distance) != 'undefined' )? distance: 5;
	var box = pax.box.show( id, message, className, dims.x + dims.width + distance, dims.y );
	pax.css.opacity( box, 100 );
	
	if( typeof( timeout ) != 'undefined' && timeout != 0 ) {
		element.hintMessageTimeout = window.setTimeout( function(){ pax.box.hide( id, pax.fx.fadeOut ) }, timeout * 1000 );
	}
	
	return box;
};


/*	Method: pax.box.hide
	This will hide the given box, using the (optional) given function
	
	Parameters:
		id - DOM pointer to the window element
		function - Optionally specify a function to be used to hide a box, such as a fading function, etc...

	Example:
		(start code)
			<input type="button" onclick="pax.box.hide(pax.$('myBox'), pax.fx.fadeOut );" value="Hide!">
			<input type='text' id='pax.box.hide.example1'>
			[:.
				pax.box.showOnRight( 'myBox', '<- Hello world!', 'hintMessage', pax.$('pax.box.hide.example1') );
			:]
		(end)
		
		This will hide a box, using the <pax.fx.fadeOut> method.
*/
pax.box.hide = function( box, func, callBack, duration ) {
	box = pax.$( box );
	if( ! box )return;
	var id = (typeof(box.id) != 'undefined' )? box.id: box;
	var funcPointer = null;
	
	if( box != null ) {
		if( func ) {
			//	Note: this assumes the hide function may have duration and callback
			funcPointer = func( id, duration, callBack );
		} else box.style.display='none';

		if( pax.isIe && pax.$( box.frameId ) )pax.$( box.frameId ).style.display='none';
	}
	return funcPointer;
};


/*	Method: pax.box.destroy
	This will destroy the given box
	
*/
pax.box.destroy = function( box ) {
	box = pax.$( box );
	if( box != null ) {
		box.style.display='none';
		if( pax.isIe && pax.$( box.frameId ) )pax.util.removeElement( pax.$( box.frameId ) );
		pax.util.removeElement( box );
	}
};
/* ----------------------------------------------------------------------------

	pax.window.js Copyright (C) 2005, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.window
		This class creates and manages "windows" for various applications, such as dialogs and draggable windows.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/
var pax = pax || {};
pax.window = pax.window || {};
/*	Property: window.dict
	Window dictionary for tracking windows	*/
pax.window.dict = {};


/*	Method: pax.window.register
	Private method: Registers a window with the window manager
	
	Parameters:
		id - DOM pointer to the window element
*/
pax.window.register = function( id ) {
	pax.window.dict[id] = document.getElementById( id );
};


/*	Method: pax.window.get
	Private method: Returns a window from the window manager
	
	Parameters:
		id - DOM pointer to the window element
*/
pax.window.get = function( id ) {
	return pax.window.dict[id];
};


/*	Method: pax.window.deRegister
	Private method: Each window is removed from the manager, once it is hidden or destroyed
	
	Parameters:
		id - DOM pointer to the window element
*/
pax.window.deRegister = function( id ) {
	delete pax.window.dict[id];
};


/*	Method: pax.window.hideAll
	Hides all windows.
	
	Example:
		(start code)
			<span id='pax.window.hideAll.example' style='position:absolute;left:200px;top:200px;'>X</span>
			[:.
				var pos = pax.util.getPosition( pax.$('pax.window.hideAll.example') );
				pax.box.show( 'myWindow1', '^ X marks the spot!', 'hintMessage', pos.x, pos.y + pos.height + 5 );
				pax.box.show( 'myWindow2', '<- X marks the spot!', 'hintMessage', pos.x + pos.width + 5, pos.y );
				pax.box.show( 'myWindow3', 'X marks the spot! ->', 'hintMessage', pos.x - 135, pos.y );
				
				exampleCleanup = function() { 
					alert('About to hide all windows');
					pax.window.hideAll(); 
					alert('All windows hidden');
				};
			:]
		(end)
		This hides all windows in the window manager, you will note that it is run in the cleanup phase, with alerts to show you what is happening.
*/
pax.window.hideAll = function() {
	for( win in pax.window.dict )pax.box.hide( win );
};

/*	Method: pax.window.add
	Adds a window widget, and registeres it with the window manager
	
	Example:
		(start code)
			<select>
				<option>Drag a window over this in IE!</option>
			</select>
			[:.
				pax.window.add( pax.$('exampleDiv'), 'wid1', 'The window1 title', 'The content of the window', 50, 15 );
				pax.window.add( pax.$('exampleDiv'), 'wid2', 'The window2 title', 'The content of the window', 150, 250 );
				pax.window.add( pax.$('exampleDiv'), 'wid3', 'The window3 title', 'The content of the window', 250, 350 );
				pax.box.show( 'showBoxID', 'message', 'hintMessage', 10, 20, pax.$('exampleDiv') );
			:]
		(end)

*/
pax.window.add = function( target, id, title, content, x, y, args ) {
	//	TODO: Add preloading of the CSS images used in the window...
	
	//	The window template
	var myWindowTemplate = "" +
		"<div id='[:=id:]WindowTitle' class='TitleBar'> 								"+
		"	<span id='[:=id:]WindowCloseButton' class='Button Close'>x</span> 			"+
		"	<span id='[:=id:]WindowResizeButton' class='Button Resize'>=</span> 		"+
		"	<span id='[:=id:]WindowMinimizeButton' class='Button Minimize'>-</span> 	"+
		"	<span id='[:=id:]WindowTitleText' class='Title'>[:=title:]</span> 			"+
		"</div> 																		"+
		"<div class='Middle'> 															"+
		"	<div id='[:=id:]WindowContent' class='Content'>[:=content:]</div> 			"+
		"</div> 																		"+
	"";
	
	var myWindowStatusBarTemplate = "" +
		"<div id='[:=id:]Status' class='Status'> 										"+
		"	<span id='[:=id:]StatusButton' class='StatusButton'>/</span> 				"+		 
		"	<span id='[:=id:]StatusText' class='StatusText'>[:='statusText':]</span>	"+
		"</div> 																		"+
	"";

	args = args || {};
	args.handle = pax.$( id + 'WindowTitle');
	args.resetZIndex = false;
	args.showStatus = ( typeof( args.showStatus ) != 'undefined' )? args.showStatus: false;
	myWindowTemplate += myWindowStatusBarTemplate;

	var data = {
		id: id,
		title: title,
		content: content
	};

	//	Parse the template, and display it
	var myBox = pax.box.show( id, pax.template.parse( myWindowTemplate, data ).html, 'pWin', x, y, target );
	pax.util.setPosition( myBox, { x: 600 } );
	
	// Create a function if we're using IE, to move the iFrame.
	if( pax.isIe ) {
		args.callMove = function( args ) {
			pax.window.setPosition( args.args.element, args.x, args.y );
		};
		//	Method to remove the iframe reference, when the window is dropped.
		args.callBack = function( args ) {
			args.args.element.iFrame = null;
		};
	}

	pax.fx.drag( myBox, args );
	args.x = x;
	args.y = y;
	pax.util.setPosition( myBox, args );
	
	return pax.$(id);
};
/* ----------------------------------------------------------------------------

	pax.load.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.load
		This is library can detect when the complete DOM has been rendered. It can also load PAX scripts on the fly
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


/*	Property: pax.load
		This library OBJ	*/
var pax = pax || {};
pax.load = pax.load || {};

/*	Property: pax.load.docLoaded
		True if the document DOM has loaded (excluding images and sub-documents).	*/
pax.load.docLoaded = false;
/*	Property: pax.load.docLoadedFuncs
		List of functions to run when the document has loaded	*/
pax.load.docLoadedFuncs = [];

//	Loads a script, and cache it, optionally calling the callback function
//	Note: you should use pax.load.script.
pax.load.cacheScript = function( url, callback ) {
	var script = pax.cache.get( url );
	if( script ) {
		if( pax.util.getType( callback ) == 'function' )callback();
	} else {
		var initLibrary = function( xml, txt, url ) {
			if( window.execScript ) { window.execScript( txt ); }	// eval in global scope for IE, otherwise everyone else.
			else if( window.eval ) { window.eval( txt ); }
			else { eval( txt ); }
			pax.cache.set( url, txt, 0 );
			if( pax.util.getType( callback ) == 'function' )callback();
		};
			
		/*
			See if we are in a frame, try and use the parent frames PAX to cache the 
			script, this is VERY beneficial for HTTPS applications, as HTTPS does NOT
			cache javascript, so to use this:
			1. Create an iFrame "shell", in which the parent holds JS in cache
			2. The child frame contains the web application. 
			3. Use pax.load.script to load javascripts, and run the code
		*/
		var topWin = window.top;
		if( topWin && ( topWin != window.self ) && topWin.pax ) {
			//	Get the script from the parent's cache.
			var scriptRO = topWin.pax.load.cacheScript( url, function () {
				script = topWin.pax.cache.get( url );
				initLibrary( '', script, url );
			} );
			
		} else {
			pax.post( url, '', initLibrary, 'pax.load.script', null, null );
		}
	}
};


/*	Method: pax.load.script
	Loads a list of scripts, then calls the callback
	
	Parameters:
		list - list of scripts that we want to load, this can be either the complete or relative path
		callback - a function to run, when the scripts have loaded
		
	Example:
		(start code)
			<span id="pax.script.example1">You should see a message pop up, then disappear after 5 seconds here: </span>
			[:.
				function showWindowAfterLoad() {
					pax.box.showOnRight( 'myBox', 'This box is rendered via an asynchronously loaded library', 'hintMessage', pax.$('pax.script.example1'), 5 );
				}
				
				pax.load.script( '/pax/pax.box.js', showWindowAfterLoad );
			:]
		(end)
		Loads the <pax.box> library via ajax, and uses it to display a message box using <showBox>.
*/
pax.load.script = function( list, callback ) {
	if( pax.util.getType( list ) != 'array' )list = [list];
	if( list.length > 1 ) {
		pax.load.cacheScript( list[0], function() { pax.load.script( list.slice(1), callback ) } );
	} else {
		pax.load.cacheScript( list[0], callback );
	}
};


/*	Method: pax.load.css
	Load CSS via ajax, and runs the callback when done.
	
	Parameters:
		url - The URL of the CSS you want to load
		callBack - The method to run when the CSS has been loaded

	Example:
		(start code)
			[:.
				pax.load.css( '/pax/resource/pax_consolidated.css', function() { alert('Loaded CSS.'); } );
			:]
		(end)
		Loads some CSS, and alerts when done.

*/
pax.load.css = function( url, callBack ) {
	var initCSS = function( xml, txt, url ) {
		if( ( url.indexOf( '.css' ) != -1 ) && ( url.lastIndexOf( '.css' ) == url.length - 4 ) ) {
			myCSS = document.createElement( 'link' );
			myCSS.setAttribute( 'rel', 'stylesheet' );
			myCSS.setAttribute( 'type', 'text/css');
			myCSS.setAttribute( 'href', url );
			document.getElementsByTagName("head").item(0).appendChild( myCSS );
		};
		callBack();
	};
	pax.post( url, '', initCSS, 'pax.load.css', null, null );
};


/*	Method: pax.load.onloaded
	Binds a function to launch when the DOM document is ready (excluding loading images and sub-documents); launches the function immediately if document is ready.
	
	Parameters:
		func - Function that runs when the event fires

	Example:
		(start code)
			var myFunc = function() {
				alert('hello world!');
			}
			pax.load.onloaded( myFunc );
		(end)
		Will alert when the DOM has loaded. Note that the example won't run, as the document has already loaded
*/
pax.load.onloaded = function( func ) {
	if( pax.load.docLoaded )return func.apply( document, [pax] );
	else pax.load.docLoadedFuncs.push( function() { return func.apply( this, [pax]) } );
};

/*	Private Method: pax.load.initOnload
	This method fires when the DOM document is ready (excluding loading images and sub-documents). This is a private method only, it will run every function added using the <pax.load.onloaded> method.
*/
pax.load.initOnload = function() {
	if( ! pax.load.docLoaded ) {
		// kill the timer
		if( pax.load.safariOnloadTimer ) {
			clearInterval( pax.load.safariOnloadTimer );
			pax.load.safariOnloadTimer = null;
		}
		for( var f = 0; f < pax.load.docLoadedFuncs.length; f++ ) {
			pax.load.docLoadedFuncs[f]();
		}
		pax.load.docLoadedFuncs = [];
		pax.load.docLoaded = true;
		if( document.removeEventListener )document.removeEventListener( "DOMContentLoaded", pax.load.initOnload, false );
	}
};

/*
	Based on: http://www.hedgerwow.com/360/dhtml/ie-dom-ondocumentready.html
	
	The following bit of code launches as the browser DOM has loaded, rather 
	than as the whole document (including images) has loaded.
	
	Use pax.load.onloaded to add a function to the list that is executed as this event fires.
*/
(	function() {
		var u =navigator.userAgent;
		var e=/*@cc_on!@*/false;
		if(/webkit/i.test(u)){
			setTimeout(function(){
				var dr=document.readyState;
				if(dr=="loaded"||dr=="complete"){
					pax.load.initOnload(); // call the onload handler
				}else{
					setTimeout(arguments.callee,12);
					pax.load.hasInitialisedOnloaded = true;
			}},12);
		} else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){
			document.addEventListener("DOMContentLoaded",pax.load.initOnload,false);
		} else if(e){
			(function(){
				var t=document.createElement('doc:rdy');
				try{
					t.doScroll('left');
					pax.load.initOnload();
					t=null;
				}catch(e){
					setTimeout(arguments.callee,0);
				}})();
		}else {
			if( ! pax.load.hasInitialisedOnloaded )window.onload=pax.load.initOnload;
		}
	}
)();
