/* JSget Version 1.0
 * Copyright (c) 2010 Julian Rosenblum
 */

var jsget = (function () {
  var elements = document.getElementsByTagName('*'), // all HTML elements
  i; // counter
  for (i = 0; i < elements.length; i += 1) {
    elements[i].origDisp = elements[i].style.display === 'none' ? '' : elements[i].style.display; // adds origDisp property to elements
  }
  return {
    version : '1.0' // jsget version
  };
}());

var get = function get(by, val /*, elements*/) {
  var result, // primitive value to return
  i, // counter
  node, // node cache
  selectors = { // converts user input into node property
    'class' : 'className',
    'tag' : 'tagName',
    'tagname' : 'tagName'
  },
  trim = function trim(str) { // removes extra whitespace from beginning or end of string
    if (typeof str !== 'string') { // make sure "str" is a string"
      return str;
    }
    if (str.indexOf(' ') === 0) { // trim beginning
      return trim(str.slice(1));
    }
    if (str.lastIndexOf(' ') === str.length - 1) { // trim end
      return trim(str.slice(0, -1));
    }
    return str;
  },
  elements = arguments[2] || document.getElementsByTagName('*'); // if the optional "elements" argument is not defined, set it to all HTML elements
  by = trim(by); // trim "by"
  if (by === '*') { // handles "*": "all elements" selector
    result = elements;
  }
  else if (by.nodeType) { // if by is a single node
    result = by; // return an object with that node as the primitive value
  }
  else if (by.length && (by.splice || by.item)) { // if by is an array or nodelist
    for (i = 0; i < by.length; i += 1) { // loop through by
      if (!by[i].nodeType) { // if an element of by is not a node, break the loop
        break;
      }
    }
    if (i === by.length) { // if the loop has not been broken, then we know "by" is a nodelist
      result = by; // return an object with that nodelist as the primitive value
    }
    else { // assume each element of array is an "and" clause
      result = get(by[0]);
      for (i = 1; i < by.length; i += 1) {
        result = result.and(by[i]);
      }
      return result;
    }
  }
  else if (val && typeof val !== 'string' && val.length) { // if val is an array, assume each element of array is an "and" clause
    result = get(by, val[0]);
    for (i = 1; i < val.length; i += 1) {
      result = result.and(by, val[i]);
    }
    return result;
  }
  else {
    if (!val) { // handles shorthand notation
      val = by;
      if (val.indexOf('.') > -1) { // CSS class shorthand notation
        val = val.slice(val.indexOf('.') + 1).toLowerCase();
        by = "class";
      }
      else if (val.indexOf('#') > -1) { // CSS id shorthand notation
        val = val.slice(val.indexOf('#')).toLowerCase();
        by = "id";
      }
      else { // if no CSS shorthands are found, assume "id"
        by = "id";
      }
    }
    else {
      by = by.toLowerCase(); // make sure "by" is lowercase because node.tagName returns capital
      val = trim(val); // once we've clarified "val" exists, trim "val"
    }
    if (by === 'id') { // handles by id
      if (!arguments[2]) { // if search is global
        result = document.getElementById(val);
      }
      else { // fallback in case multiple ids are present or id is not present within certain bounds
        for (i = 0, node; (node = elements[i]) && i < elements.length; i += 1) {
          if (node.nodeType === 1 && node.id === val) {
            result = node;
            break;
          }
        }
      }
    }
    else if (selectors.hasOwnProperty(by)) { // if selector is stated in 'selectors' object
      result = [];
      for (i = 0, node; (node = elements[i]) && i < elements.length; i += 1) {
        if (node.nodeType === 1 && node[selectors[by]].toLowerCase() === val) {
          result[result.length] = node;
        }
      }
    }
    else { // if not, we assume selector refers to attribute name
      result = [];
      for (i = 0, node; (node = elements[i]) && i < elements.length; i += 1) {
        if (node.nodeType === 1 && node.attributes[by] && node.attributes[by].value === val) {
          result[result.length] = node;
        }
      }
    }
  }
  return { // return object literal
    value : result, // primitive value returned by "get"
    DOM : result, // allows direct access to DOM methods if result is a node or nodelist
    each : function (func) { // performs function for each returned node
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        func(node);
      }
    },
    innerHTML : function (html, pluseq) { // assigns/modifies innerHTML property for each returned node
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        node.innerHTML = pluseq ? node.innerHTML + html : node.innerHTML = html; // "pluseq" indicates using node.innerHTML +=
      }
    },
    style : function (prop, val) { // modifies javascript style property for each returned node
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        node.style[prop] = val;
      }
    },
    on : function (handler, func) { // performs function on event handler for each returned node
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        node['on' + handler] = (function (res) { // closure
          return function () {
            func(res);
          };
        }(node));
      }
    },
    ch : function (by, val) { // indicates selection must be direct child of previous selection
      var elements = [],
      i,
      j;
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        for (j = 0; j < node.childNodes.length; j += 1) {
          elements[elements.length] = node.childNodes[j];
        }
      }
      return get(by, val, elements);
    },
    where : function (by, val) { // adds condition to previous selector
      result = result instanceof Array ? result : [].concat(result);
      return get(by, val, result);
    },
    and : function (by, val) { // combine with previous selection
      result = result instanceof Array ? result : [].concat(result);
      return get('*', '', result.concat(get(by, val).value));
    },
    show : function () { // show element
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        node.style.display = node.origDisp; // change value of display property to original value
      }
    },
    hide : function () { // hide element
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        node.style.display = 'none';
      }
    },
    toggle : function () { // show or hide depending on value of display property
      result = result instanceof Array ? result : [].concat(result);
      for (i = 0, node; (node = result[i]) && i < result.length; i += 1) {
        if (node.style.display === 'none') { // show
          node.style.display = result[i].origDisp;
        }
        else { // hide
          node.style.display = 'none';
        }
      }
    }
  };
};