(function() {
	
  var ElementDimensionsMethods = {

	    getInnerWidth: function element_innerWidth(element) {
	        
	        return element.getWidth() 
	            + parseInt(element.getStyle('paddingLeft'), 10)
	            + parseInt(element.getStyle('paddingRight'), 10);
	    },
	    
	    getInnerHeight: function element_innerHeight(element) {
	        
	        return element.getHeight() 
	            + parseInt(element.getStyle('paddingTop'), 10)
	            + parseInt(element.getStyle('paddingBottom'), 10);
	    },
	    
	    getOuterWidth: function element_outerWidth(element, margin) {
	        
	        return element.getInnerWidth() 
	            + parseInt(element.getStyle('borderLeftWidth'), 10)
	            + parseInt(element.getStyle('borderRightWidth'), 10)
	            + (margin 
	                ? parseInt(element.getStyle('marginLeft'), 10) + parseInt(element.getStyle('marginRight'), 10) 
	                : 0);
	    },
	    
	    getOuterHeight: function element_outerHeight(element, margin) {
	        
	        return element.getInnerHeight() 
	            + parseInt(element.getStyle('borderTopWidth'), 10)
	            + parseInt(element.getStyle('borderBottomWidth'), 10)
	            + (margin 
	                ? parseInt(element.getStyle('marginTop'), 10) + parseInt(element.getStyle('marginBottom'), 10) 
	                : 0);
	    },
	    
	    setDimensions: function element_setDimensions(element, width, height) {
	        
	    	if (!width && !height) {
	    		return;
	    	}
	    	
	        var styles = {};
	        
	        if (width != null) {
	            styles.width = width + 'px';
	        }
	        
	        if (height != null) {
	            styles.height = height + 'px';
	        }
	        
	        element.setStyle(styles);
	        
	        (function() {
	        	element.fire('element:resized', { width: width, height: height });
	        }).defer();
	    }
	    
	};

	Element.addMethods(ElementDimensionsMethods);
  
})();

(function() {
	
    // there are probably other characters that need to be unescaped
  var ElementTemplatingMethods = {
    toTemplate: function( element, pattern )
    {
      return new Template($(element).innerHTML.gsub('%7B', '{').gsub('%7D', '}'), pattern);
    }
  };
  
  Element.addMethods(ElementTemplatingMethods);
  
})();

(function(){
	
  var ElementScrollingMethods = {
		  
    scrollToTop: function element_scrollToTop(element)
    {
      element = $(element);
      element.scrollTop = 0;
      return element;
    }
    ,
    scrollToBottom: function element_scrollToBottom(element)
    {
      element = $(element);
	  element.scrollTop = element.scrollHeight;  
      return element;
    }
    , 
    scrollToElement: function( element, targetElement, options )
    {
      element = $(element);
      targetElement = $(targetElement);
    
      options = Object.extend({  
        top: true
      , left: false
      }, options || {});
    
      if( targetElement.descendantOf(element) )
      {
        var targetPosition = targetElement.positionedOffset();      
        if( options.top ) element.scrollTop = targetPosition.top;
        if( options.left ) element.scrollLeft = targetPosition.left;
      }
    
      return element;
    }
    ,
    // todo: rename this to isScrollNearBottom
    scrollNearBottom: function( element, threshold )
    {
      element = $(element);
    
      var height = element.getHeight();
      var scrollTop = element.scrollTop;
      var scrollHeight = element.scrollHeight;
      var offset = scrollHeight - scrollTop - height;
      return ( offset <= threshold );
    }
    ,
    // todo: rename this to isScrollNearTop
    scrollNearTop: function( element, threshold )
    {
      element = $(element);
      return ( element.scrollTop <= threshold );
    }
    ,
    scrollOffset: function( element )
    {
      element = $(element);
      var offset = Element._returnOffset( element.scrollLeft, element.scrollTop );
      return offset;
    }
    ,
    scrollToOffset: function( element, offset )
    {
      if( !offset ) return element;
      if( offset.top ) element.scrollTop = offset.top;
      if( offset.left ) element.scrollLeft = offset.left;
      return element;
    }
    ,
    scrollToLeft: function( element )
    {
      element = $(element);
      element.scrollLeft = 0;
      return element;
    }
    ,
    scrollToRight: function( element )
    {
      element = $(element);
      element.scrollLeft = element.scrollWidth;
      return element;   
    }
  };
  
  Element.addMethods(ElementScrollingMethods);
  
})();

(function() {
	
    Element.addMethods({
        caret: function(element, begin, end) {
            // setting the caret position
            if (typeof begin == 'number') {
                end = (typeof end == 'number') ? end : begin;
                if (element.setSelectionRange) {
                    element.focus();
                    element.setSelectionRange(begin, end);
                } else if (element.createTextRange) {
                    var range = element.createTextRange();
                    range.collapse(true);
                    range.moveEnd('character', end);
                    range.moveStart('character', begin);
                    range.select();
                }
            }
            // getting the caret position
            else {
                if (element.setSelectionRange) {
                    begin = element.selectionStart;
                    end = element.selectionEnd;
                } else if (document.selection && document.selection.createRange) {
                    var range = document.selection.createRange();
                    begin = 0 - range.duplicate().moveStart('character', -100000);
                    end = begin + range.text.length;
                }
                return {begin: begin, end: end};
            }
        }
    });
    
})();

(function() {
	
	Element.addMethods({
		
		// event delegation
        // usage example:
        //  $('blog').delegate('click', {
        //      '.delete_entry': function(event) {
        //          AWC.confirm('Are you sure you want to delete this entry?', function(result) {
        //              if (result) {
        //                  // call dwr ...
        //              };
        //          });
        //      }
        //  }, {
        //      stop: true, // will call event.stop() if any of the selectors matched
        //      scope: null, 
        //      before: function(event) { return event; },
        //      after: function(event) {};
        //  });
        
        delegate: function(element, eventType, rules, options) {
            options = options || {};
            
            // convert the rules to a Hash for convenience
            rules = $H(rules);
            
            // bind scope
            rules.each(function(pair) {
              rules.set(pair.key, pair.value.bind(options.scope || element));
            });
            
            if (options.before) {
              options.before = options.before.bind(options.scope || element);
            };
            
            if (options.after) {
              options.after = options.after.bind(options.scope || element);
            };
            
            element.observe(eventType, function(event) {
              var element = event.element();
              var selectors = rules.keys();
              
              if (options.before) {
                event = options.before(event);
                if (!event) return;
              };
              
              var matched = false;
              selectors.each(function(selector) {
            	// fixme: this is inefficient -- thanks drew
                if (element.match(selector)) {
                  matched = true;
                  var handler = rules.get(selector);
                  handler(event, element);
                } else if (options.matchElementIfDescendantOfSelector) {
                	var ancestor = element.up(selector);
                	if (ancestor) {
	                    matched = true;
	                    var handler = rules.get(selector);
	                    handler(event, ancestor);               
                	}
                };
              });
              
              if (options.after) {
                options.after(event);
              };
              
              if (matched && options.stop) {
                event.stop();
              };
            });
        }
	});
	
})();
