/*

* Element Traversal methods from Juriy Zaytsev (kangax)
* used to emulate Prototype up/down/previous/next methods
*/

(function(D){

// TODO: all of this needs tests
var match = D.match, select = D.select, root = document.documentElement,

// Use the Element Traversal API if available.
nextElement = 'nextElementSibling',
previousElement = 'previousElementSibling',
parentElement = 'parentElement';

// Fall back to the DOM Level 1 API.
if (!(nextElement in root)) nextElement = 'nextSibling';
if (!(previousElement in root)) previousElement = 'previousSibling';
if (!(parentElement in root)) parentElement = 'parentNode';

function walkElements(property, element, expr) {
  var i = 0, isIndex = typeof expr == 'number';
  if (typeof expr == 'undefined') {
    isIndex = true;
    expr = 0;
  }
  while ((element = element[property])) {
    if (element.nodeType != 1) continue;
    if (isIndex) {
      ++i;
      if (i == expr) return element;
    } else if (match(element, expr)) {
      return element;
    }
  }
  return null;
}

/**
 * @method up
 * @param {HTMLElement} element element to walk from
 * @param {String | Number} expr CSS expression or an index
 * @return {HTMLElement | null}
 */
function up(element, expr) {
  return walkElements(parentElement, element, expr);
}
/**
 * @method next
 * @param {HTMLElement} element element to walk from
 * @param {String | Number} expr CSS expression or an index
 * @return {HTMLElement | null}
 */
function next(element, expr) {
  return walkElements(nextElement, element, expr);
}
/**
 * @method previous
 * @param {HTMLElement} element element to walk from
 * @param {String | Number} expr CSS expression or an index
 * @return {HTMLElement | null}
 */
function previous(element, expr) {
  return walkElements(previousElement, element, expr);
}
/**
 * @method down
 * @param {HTMLElement} element element to walk from
 * @param {String | Number} expr CSS expression or an index
 * @return {HTMLElement | null}
 */
function down(element, expr) {
  var isIndex = typeof expr == 'number', descendants, index, descendant;
  if (expr === null) {
    element = element.firstChild;
    while (element && element.nodeType != 1) element = element[nextElement];
    return element;
  }
  if (!isIndex && match(element, expr) || isIndex && expr === 0) return element;
  descendants = select('*', element);
  if (isIndex) return descendants[expr] || null;
  index = 0;
  while ((descendant = descendants[index]) && !match(descendant, expr)) { ++index; }
  return descendant || null;
}
D.up = up;
D.down = down;
D.next = next;
D.previous = previous;

})(NW.Dom);