// General hover classes
var Hover = {
  over: function(evt) {
    evt.element().up('tr').addClassName('hover');
  }.bindAsEventListener(),
  
  out: function(evt) {
    evt.element().up('tr').removeClassName('hover');
  }.bindAsEventListener()
};

var Overlay = function() {
  var clickHandler,
      create;
  
  clickHandler = function(evt) {
    evt.element().remove();
    evt.stop();
  }.bindAsEventListener();
  
  create = function(width, height, handler) {          
    var el = new Element('div', {'class': 'overlay'})
      .setStyle({
        'width': width + 'px',
        'height': height + 'px',
        'opacity': .2,
        'background': 'black',
        'position': 'absolute',
        'top': 0,
        'left': 0,
        'cursor': 'pointer',
        'z-index': 99
      })
      .observe('click', clickHandler);
    
    if (Object.isFunction(handler))
      el.observe('click', handler);
    
    return el;
  };
  
  return {
    drawOverDocument: function(handler) {
      var html = $$('html')[0],
          body = $$('body')[0],
          element;

      element = create(html.getWidth(), html.getHeight(), handler);
      body.insert({top: element});
      
      return element;
    },
    
    drawOver: function(container, handler) {
      var element;
      
      container = $(container);
      
      element = create(container.getWidth(), container.getHeight(), handler);
      container.insert({bottom: element});
      
      return element;
    }
  }
}(); // Execute the function immediately - results in a singleton

Form.Element.Hinter = Class.create({
  options: {
		caption: 'Search',
		emptyClass: 'empty'
	},

	initialize: function(element, options) {
		this.element = $(element);
		this.options = $H(this.options).merge(options || {});
		this.element.value = '';
		this.element.observe('focus', this.handleFocus.bind(this));
		this.element.observe('blur', this.handleBlur.bind(this));
		this.handleBlur();
	},

	handleFocus: function(evt) {
		if (this.element.hasClassName(this.options.get('emptyClass'))) {
			this.element.removeClassName(this.options.get('emptyClass'));
			this.element.value = '';
		}
	},

	handleBlur: function(evt) {
		if ($F(this.element).strip() === '') {
			this.element.addClassName(this.options.get('emptyClass'));
			this.element.value = this.options.get('caption');
		}
	}
});

Ajax.Request.prototype.abort = function() {
  this.transport.onreadystatechange = Prototype.emptyFunction;
  this.transport.abort();
  Ajax.activeRequestCount--;
};

var trackOutgoingLinks = function() {
  if (!Object.isUndefined(pageTracker)) {
    document.observe('dom:loaded', function() {
      $$('a[target="_blank"]').invoke('observe', 'click', function(evt) {
        pageTracker._trackPageview('/outgoing/' + evt.element().readAttribute('href'));
      }.bindAsEventListener());
    });
  }
  if (!Object.isUndefined(englishTracker)) {
    document.observe('dom:loaded', function() {
      $$('a[target="_blank"]').invoke('observe', 'click', function(evt) {
        englishTracker._trackPageview('/outgoing/' + evt.element().readAttribute('href'));
      }.bindAsEventListener());
    });
  }
};

Function.prototype.memoize = function() {
  var func = this;
  return function() {
    var output = func();
    func = function() { return output; }
    return func();
  };
};

Event.delegate = function(rules) {
  return function(e) {
    var element = $(e.element());
    for (var selector in rules)
      if (element.match(selector)) return rules[selector].apply(this, $A(arguments));
  }
};

PeriodicalExecuter.addMethods({
  reset: function() {
    this.stop();
    this.registerCallback();
  }
});


// Function composition
var $C = function() {
  var fs = $A(arguments);
  return function() {
    var f,
        result = fs.pop().apply(null, $A(arguments));
    
    while (f = fs.pop()) {
      result = f(result);
    }
    
    return result;
  };
};