/**  
 *  GenevaJS (c) 2009 Rick waldron
 *
 *  MIT license.
 *
 *  THIS VERSION OF GENEVA IS FOR DEVELOPMENT ONLY
 *  DO NOT USE IN PRODUCTION ENVIRONMENTS -- INCOMPLETE!!
 *--------------------------------------------------------------------------
*/
/*jslint forin: true, laxbreak: true, sub: true */
/*
    NOTICE - THIS IS THE DEVELOPMENT BUILD OF GENEVAJS AND IS FULL OF 
    DEBUGGING STATEMENTS AND CONSOLE.LOG()
    
    YOU'VE BEEN WARNED.
*/
//-------------------------------------
// GenevaJS voodoo
String.prototype._gtrim   = function() { return this.replace( /^\s+|\s+$/g, '' ); };
String.prototype._gclean  = function() { return this.replace(/\t/g, '').replace(/\s+/g, ' '); };
String.prototype._glcase  = function() { return this.toLowerCase(); };
// END

(function (_$) {
  
  //  Prototype objects to copy into GenevaJS
  var 
  _copyset  = [ Element.Methods, Form.Methods, Form.Element.Methods ],
  //  $() Adapter + jQuery support (todo: plugin support)
  Geneva = window.Geneva = jQuery = window.jQuery = window.$ = function( _s ) {
    return new Geneva.fn.init( _s );
  };
  
  Geneva.fn = Geneva.prototype = {
    init : function () {
      
      //  $() is empty
      if ( arguments.length === 0 ) {
        return this.toAdapter(document.body);
      }      

      //  _s = selector = class = id = element = function = document
      var _s = arguments[0];

      //  $(fn) = $().ready =  $.ready() = dom:loaded
      if ( typeof _s == 'function' ) {
        return document.observe('dom:loaded', function () {
          _s.call(this, arguments);
        }.bind(this));
      }
      //  $(extended element[s] || document)  
      if ( typeof _s == 'object' ) {
      
        //if ( document.loaded && Object.isElement(_s) ) {
        //  return _s;
        //}
              
        //  TODO: convert to non-prototype reliant checking
        if ( Object.isElement(_s) && Object.isString(_s) )  {
          return this.toAdapter(_s);
        }

        //  DOM Ready $(document).ready() +Al MacDonald
        if( _s == document ) {
          document.ready = function(){ 
            new Geneva.fn.init(arguments[0]);
          }        
        }
        return _s;
      }    

      //  Expressions jQuery (c) John Resig
      var _strExp = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w\-]+)$/,
          _selExp = /^.[^:#\[\.,]*$/,                     
          _strArr = _strExp.exec(_s),
          _selArr = _selExp.exec(_s);

      //  New Element -- BROKEN
      //if ( _strArr && _strArr[1] && !_strArr[3] ) {
      //  //var _newArr = /^<(\w+)\s*\/?>$/.exec(_strArr[1]);
      //}
      //  #Id
      if ( _strArr && _strArr[3] )      {    
        return this.toAdapter(_strArr[3]);
      } 
      //  .className  
      if ( _selArr && _$(_selArr[0]) ) {
        return this.toAdapter(_selArr[0]);
      }
      //  Selector
      return this.toAdapter(_s);    
    },
    toAdapter: function () {
      return new Adapter(arguments[0]);
    }
  };

  //  Adapter object/class
  //  [mixes: Enumerable (allows array-like object support)]  
  Adapter   = Class.create( Enumerable, {
    initialize: function ( _s ) {
      //  [ $S() || $$() || _$() ]
      this.elements = Object.isString(_s) ? 
                    ( 'Sizzle' in window ? this._sizzle(_s) : $$(_s) ) : 
                    [_$(_s)];
      
      if ( _$(_s) ) {
        // this is a gnarly way to deal with tag==id... 
        this.elements.push( _$(_s) );
      }
      
      //  yuck.
      if ( this.elements.length === 0 ) {
        this.elements = [_$(_s)];
      }        
      this._toArray();
    },
    //  Internal:  _toArray makes object this.elements array compat
    _toArray:   function() {
      Array.prototype.push.apply(this, this.elements);
    },
    //  Internal this._first() clean alias to this.elements[0]
    _first:     function() {
      return this.elements[0];
    },
    //  Internal _each()
    _each:      function(iterFn) {
      this.elements._each(iterFn);
    },
    _sizzle:    function(_s) {
      var _siz  = Sizzle(_s);
      if ( _siz.length > 0 ) {
        return _siz;
      }      
      return $$(_s);    
    }
  });
  
  //  Private Parts
  Adapter.my = {};
  //  Non Prototype reliant
  Adapter.my._emptyfn   = function()  { };
  //  Copy Prototype objects into Geneva scope  
  Adapter.my._copySourceMethods  = function () {
    
    _copyset
    .each( function ( source ) {
    
      for ( var method in source ) {
        if ( !source )  { return; }
        //  Define Geneva compat copies of all methods. 
        Adapter.prototype[method] = (function ( method ) {
          
          return function() { 
            var _elems = [], _args = $A( arguments ), _first = false, _new;

            this.elements.each( function( elem, i )  {
              
              //  _new: the single element is given the method
              _new = source[ method ].apply( null, _args.length ? 
                          [ elem ].concat(_args) : 
                          [ elem ] );
              
              //  Break if method is undefined 
              if ( !Object.isElement(_new) || Object.isUndefined(_new) ) {
                 //console.log(_new);
                 //console.log('_first set true');
                _first = true; throw $break;
              }
              _elems[i] = elem;
            });

            if (_first) { return _new; }
            
            this.elements = _elems;
            return this;
          };
        })(method);
        //  End Adapter.prototype[method]
      }
      //  End for ( var method in source )
    });
  };
  //  Call immediately
  Adapter.my._copySourceMethods();
  
  //  In progress...
  Adapter.my._filter   = function()  {
    var _args = $A(arguments), _ret = '', _elem = _args.shift();
    console.log(_args);
  };
  //  Quick fix for update();
  Adapter.my._eval     = function()  {
    var _args = $A(arguments), _ret = '', _elem = _args.shift();
    for ( var i=0, alen = _args.length; i < alen; i++ ) {
      _tmp  = typeof _args[i] == 'function' ? _args[i].call(_elem, arguments) : _args[i];
      _ret += typeof object == 'number' ? parseInt(_tmp) : _tmp;
    }
    return _ret;
  };
  //  Internal extender
  Adapter.my._extend   = function() {
    var source  = arguments[0];
    for ( var method in source ) {
      if ( !source )  { return; }
      if ( arguments.length > 1 ) {
        target  = arguments[1];
        target[method]  = source[method];        
      } else {
        Adapter.prototype[method] = source[method];
      }
    }      
  };

  //  Event methods
  $w(
  'dragenter dragover dragexit dragdrop draggesture ' +
  'focus blur submit reset change select input ' +
  'keydown keyup keypress ' +
  'load beforeunload unload abort error paint resize scroll ' + 
  'mousedown mouseup click dblclick mouseover mouseout mousemove contextmenu'
  )
  .each( function( type ) {
  
    Adapter.prototype[ type ] = ( function( type ) {
      
      return function( fn ) {
        
        //  If callback undefined, use native
        if ( ( typeof fn == 'undefined' || arguments.length === 0 ) && this.elements.length > 0 ) {
          for( var i = 0, _elen = this.elements.length; i < _elen; i++ ) {
            try {
              this.elements[i][type](); 
            } catch(e) {}
          }
          return this;
        }
        
        //  Force fn() definition
        var evtfn  = fn ? fn : function () {};

        //  Prototype's observe will ensure proper event handling
        return this.observe( type, function (_e) {
          evtfn.call( this, _e );
        }.bind());
        //  Excluding .bind() causes intermittent errors in FF & IE (...)
      };
    })(type);
  });
  
  //  Element.addMethods Adapter
  Element.addMethods = (function(source) {
    var method = function() {
      source.apply( source, arguments );
      Adapter.my._copySourceMethods();
    };
    return method;
  })(Element.addMethods);  
  
  
  //  Copy Adapter methods into Geneva.fn (+jQuery.fn)
  for ( method in Adapter.prototype ) {
    //  Omit Adapters private methods
    if ( method.indexOf('_') !== 0 ) {
      Geneva.fn[method] = Adapter.prototype[method];
    }
  }
  
  Geneva.fn.init.prototype = Geneva.fn; 
  
  //  $g Geneva alias
  window.$g = Adapter;
  //  $g Extender ***REVISIT
  window.$g.extend  = Adapter.my._extend;
  //  $g Argument eval (callback||string)
  window.$g.evalIt  = Adapter.my._eval; 
  //  $F() Adapter
  window.$F = function () {
    return _$(arguments[0]).getValue();
  };
  //  _$ Prototype $
  window._$ = function () {
    return new _$(arguments[0]);
  };

  Element.addMethods();

})( $ ); 

//  update() Adapter
Element.addMethods({
  _update:    function(elem, arg) {
    elem  = $(elem);
    if (elem && elem.value) {
      return elem.value = $g.evalIt(elem, arg);
    }
    return elem.update( $g.evalIt(elem, arg) );
  }
});

//  serialize() Adapter
Form.Element.Methods.serialize = function() {
  elem = _$(arguments[0]);
  if ( !elem.disabled && elem.name )  {
    return elem.serialize();
  }    
  return '';
};

Object.extend(Function.prototype, {
  ready: function (fn) {
    return document.observe('dom:loaded', function () {
      fn.call(this, arguments);
    }.bind(this));
  }
});

$g.extend({
  //  DOM-Ready $().ready()
  ready:  Function.prototype.ready,
  
  //  Interactions
  hover:      function( overFn, outFn ) {
    this.mouseover(overFn)
      .mouseout(outFn);
  },
  //TODO:
  mouseenter: function() {
  },
  mouseleave: function() {
  }  

});


//  Attributes : val, html
Element.addMethods({
  val:    function (elem, arg) {
    elem  = $(elem);
    if ( arg !== undefined ) {
      return elem._update(arg);
    }      
    return $F(elem)._gtrim();
  },
  html:   function (elem, arg) {
    elem  = $(elem);
    if ( arg !== undefined ) {
      return elem._update(arg);
    }      
    return ( elem.innerHTML )._gclean()._gtrim();
  },
  text:   function (elem, arg) {
    elem  = $(elem);
    if ( arg !== undefined )  {
      return elem._update(arg);
    }      
    return  elem.innerHTML.stripTags()._gclean()._gtrim();
    //return ( elem.innerText || elem.textContent || elem.nodeValue || "" ).stripTags()._gclean()._gtrim();
  }  
});

//  Attributes : attr, removeAttr, [ add, has, remove, toggle ]Class, 
Element.addMethods({

  attr:         function  (elem, prop, arg) {
    elem  = $(elem);
    if ( typeof prop == 'string' && typeof arg == 'undefined' ) {
      return elem.readAttribute(prop);
    }      
  
    if ( typeof prop == 'object'  ) {
      for ( var key in prop  ) {
        if ( key && prop ) { 
          elem.writeAttribute( key, prop[ key ] );
        }          
      }
      return elem;
    }
    return elem.writeAttribute( prop, $g.evalIt(elem, arg) );
  }, 
  removeAttr:   function  (elem, prop) {
    elem  = $(elem);
    
    return ( prop != 'undefined' 
              ? elem.removeAttribute(prop)
              : elem );
  },
  addClass:     function  (elem, arg) {
    return $(elem).addClassName(arg);
  },
  hasClass:     function  (elem, arg) {
    return $(elem).hasClassName(arg);
  },
  removeClass:  function  (elem, arg) {
    return $(elem).removeClassName(arg);
  },
  toggleClass:  function  (elem, arg) {
    elem  = $(elem);
    
    if (elem.hasClassName(arg) ) {
      return elem.removeClassName(arg);
    }      
    return elem.addClassName($g.evalIt(elem, arg));
  }
});

// CSS

Element.addMethods({
  
  //  Broken????
  css:        function (elem, arg) {
    elem =  $(elem);
    //  TODO: revisit
    if ( typeof arg == 'object' ) {
      return elem.setStyle(arg);
    }
    return elem.getStyle(arg);            
  },
  offset:     function (elem) {
    return $(elem).cumulativeOffset();
  },
  position:   function (elem) {
    return $(elem).positionedOffset();
  },
  //  TODO: height(val)
  height:     function (elem) {
    elem = _$(arguments[0]);
    return elem.getHeight();
  },
  //  TODO: width(val)
  width:      function (elem) {
    console.info(arguments);
    return $(elem).getWidth();
  },
  //  TODO: scrollTop(val)
  scrollTop:  function (elem) {
    return $(elem).viewportOffset().top;
  },
  //  TODO: scrollLeft(val)
  scrollLeft:  function (elem) {
    return $(elem).viewportOffset().left;
  }
});

//  TODO: combine the methods above with the methods below into one object
Element.addMethods( (function () {
  
  var _cssMethods = {},
      _direction  = {};

  $w('Height Width').each(function (type) {

      var _t  = type.toLowerCase();
          _direction[type]  = type=='Height' ? { _x : 'Top', _y: 'Bottom' } : { _x: 'Left', _y: 'Right' };
           
      _cssMethods['inner' + type] = function (elem) {
      
        console.info(arguments);
        var elem  = $(elem),
            _d    = elem.getDimensions();
        return _d[_t] + ( ( elem.style['padding'+_direction[type]] == '' ? 0 : parseInt(elem.style['padding'+_direction[type]._x] )) + ( elem.style['padding'+_direction[type]] == '' ? 0 : parseInt(elem.style['padding'+_direction[type]._y]) ) );
        
      }
      _cssMethods['outer' + type] = function (elem) {
      
        console.info(arguments);
        var elem  = $(elem),
            _d    = elem.getDimensions(),
            _i    = _d[_t] + ( ( elem.style['padding'+_direction[type]] == '' ? 0 : parseInt(elem.style['padding'+_direction[type]._x] )) + ( elem.style['padding'+_direction[type]] == '' ? 0 : parseInt(elem.style['padding'+_direction[type]._y]) ) );
        return  _i + ( parseInt( elem.getStyle('border'+_direction[type]._x+'Width') ) + parseInt( elem.getStyle('border'+_direction[type]._y+'Width') ) );
      }
  });
  
  return _cssMethods;
})());

//  Traversing
//  Convenience Aliases to Prototype methods
$g.extend({
  eq:       function (i) {
    return this.elements[i];
  },
  is:       function (exp) {
    return !!exp && $g._filter();
  },
  //  TODO:
  map:      function (fn) {
  },
  not:      function (exp) {
  },
  slice:    function (start, end) {
  }
});

Element.addMethods({
  //  Filtering
  filter:   function (arg) {
    // arg can be either expression or function
  },
  //  Finding
  children:     Element.Methods.immediateDescendants,
  offsetParent: Element.Methods.getOffsetParent,

  add:      function (exp) {
  },
  closest:  function (elem) {
    return  $(elem).parentNode;
  },
  contents: function (elem) {
    //return  $(elem).select('*');
    var args = Array.prototype.slice.call(arguments, 1);
    return  Selector.findChildElements(elem, args);    
  },
  nextAll:  function (elem) {
    //return  $(elem).recursivelyCollect('nextSibling');
    return  Element.recursivelyCollect(elem, 'nextSibling'); 
  },
  parent:   function (elem) {
    return  $(elem).parentNode;
  },
  parents:  function (elem) {
    //return  $(elem).recursivelyCollect('parentNode');
    return  Element.recursivelyCollect(elem, 'parentNode');
  },
  prev:     function (elem, exp, index ) {
    return  $(elem).previous(exp, index);          
  },
  prevAll:  function (elem) {
    return  Element.recursivelyCollect(elem, 'previousSibling');
  },
  //  Chaining
  andSelf:  function () {
  },
  end:      function () {
  }
});

//  Manipulation
//  these are terribly limited and well... just plain terrible.
Element.addMethods({
  //BROKEN
  appendTo:   function (elem, context) {
    $(context).insert(elem);
    return elem;
  },
  append:     function (elem, arg) {
    return $(elem).insert(arg);
  },
  //BROKEN
  prependTo:  function (elem, context) {
    $(context).insert({ before : elem });
    return elem;
  },
  prepend:    function (elem, arg) {
    return $(elem).insert({ before : arg } );
  }
  //,
  //before: function () {
  //}, 
  //after:  function () {
  //}
  //,
  //before: Element._insertionTranslations.before,
  //after:  Element._insertionTranslations.after,
  //bottom: function (elem, arg) {
  //  return $(elem).insert(arg);
  //}
});





Element.addMethods();
