Fx.Opacity = Fx.Style.extend({
  initialize: function(el, options){
    this.now = 1;
    this.parent(el, 'opacity', options);
  },
  isDisplayed: function() {
    return (this.now > 0);
  },
  toggle: function() {
    if (this.now > 0) return this.start(1, 0);
    else return this.start(0, 1);
  },
  show: function(){
    return this.set(1);
  }
});

var Countable = new Class({
  defaultOptions: {
    maximum: 30,
    offset: 20
  },
  initialize: function(inputId, options) {
    // Merges the default options with the ones given as parameters
    this.setOptions($merge(this.defaultOptions, options));
 
    // Retrieves the textarea element based on its name
    input = $(inputId);
 
    if (input) {
      // Creates an element to serve as a handle:
      // 
      //   <div class="count"></div>
      // 
      this.handle = new Element("div").addClass("fValidator-msg");
 
      // Injects this element just after the textarea
      this.handle.setHTML('&nbsp;').injectAfter(input);
 
      // Setups effects
      this.opacityEffect = new Fx.Opacity(this.handle, {duration: 600, wait: false});
 
      // Initializes effects on each element
      this.opacityEffect.hide();
 
      // Fires action upon events
      input.addEvent('keydown', this.onKeyPress.bindWithEvent(this));
      input.addEvent('keyup', this.onKeyPress.bindWithEvent(this));
      input.addEvent('blur', this.reset.bindWithEvent(this));
 
      // Keeps a reference to the handle
      this.input = input;
      this.origInputStyles = this.input.getStyles('border-color','background-color');
    }
  },
  onKeyPress: function(event) {
    event = new Event(event);
 
    if(!event.shift && !event.control && !event.alt && !event.meta) {
      this.update();
    }
  },
  reset: function() {
    this.opacityEffect.toggle();
    this.input.setStyles(this.origInputStyles);
  },
  update: function() {
    // Removes the character entered as the maximum has been reached
    if (this.input.value.length > this.options.maximum) {
      this.input.value = this.input.value.substring(0, this.options.maximum);
    }
 
    // Handles the display of the number of remaining characters
    if (this.input.value.length > (this.options.maximum - this.options.offset)) {
      // Calculates the number of characters left
      var count = this.options.maximum - this.input.value.length;
 
      // Processes message
      if (count == 0) {
        var string = "<span class=\"error\">Nombre maximum de caractères atteint !</span>";
      } else if (count == 1) {
        var string = "Plus que 1 caractère avant d'atteindre le maximum";
      } else {
        var string = "Plus que <strong>" + count + "</strong> caractères avant d'atteindre le maximum (max." + this.options.maximum + " caractères)";
      }
 
      // Displays information message
      this.handle.setHTML(string);
 
      // Shows the message if necessary
      if (!this.opacityEffect.isDisplayed()) {
        this.opacityEffect.toggle();
      }
 
      // Calculates the background color based on the number of remaining
      // characters
      var white = new Color('#FFCCCC');
      var color = white.mix('#FFFFFF', count / this.options.offset * 100);
 
      // Updates the background color of the textarea
      this.input.setStyle('background-color', color);
    } else if (this.opacityEffect.isDisplayed()) {
      // Hides message
      this.opacityEffect.toggle();
 
      // Resets textarea background color
      if (this.input.getStyle('background-color') != '#FFFFFF') {
        this.input.setStyle('background-color', '#FFFFFF');
      }
    }
  }
});
 
// Adds options management support
Countable.implement(Options.prototype);
