/*
Minibox.js - A hoverbox that's as simple as it gets. No insane CSS, no HTML
             gymnastics to get some janky system to fire events properly, no
             weird JavaScript just to specify a freaking size. Give it an
             element to contain and it'll contain it without stripping events,
             IDs, etc. Close button optional.
             
             By: Tyson Tate
             Copyright: 2010 CrowdFlower, San Francisco, CA
*/

var Minibox = new Class({

  Implements: [Options, Events],
  
  Binds: [
    "overlayEffectDidFinish", "containerEffectDidFinish",
    "updatePosition", "open", "close", "escKeyClose", "closeButtonClick"
  ],
  
  options: {
    // Events
    onLoad: $empty,
    onOpenStart: $empty,
    onOpenFinish: $empty,
    onCloseStart: $empty,
    onCloseFinish: $empty,
    
    // Options
    closeButton: null,
    effectDuration: 200,
    height: 500,
    width: 400,
    overlayOpacity: 0.5,
    overlayColor: '#aaa',
    escCloses: true,
    overlayClickCloses: true,
    keepElementOnClose: false
  },

  initialize: function(content, options) {
    this.attachElement( content );
    this.setOptions(options);
    
    // ==================
    // = Setup elements =
    // ==================
    
    this.overlay = new Element('div', {
      'class':'minibox_overlay',
      'styles': {
        'position': 'fixed',
        'z-index': 77777 * ($$(".minibox_overlay").length + 1),
        'top': 0,
        'left': 0,
        'width': '100%',
        'height': '100%',
        'background-color': this.options.overlayColor
      }
    }).inject(document.body);
    
    if (this.options.overlayClickCloses) {
      this.overlay.setStyle('cursor', 'pointer');
      this.overlay.addEvent('click', this.close);
    }
    
    var height = this.options.height;
    if (parseInt(height, 10)) {
      height = height - 20;
    }
    
    this.container = new Element('div', {
      'class': 'minibox_container',
      'styles': {
        'position': 'fixed',
        'padding': '10px 0',
        'height': height,
        'width': this.options.width,
        'overflow': 'auto',
        'background-color': 'white',
        'z-index': 88888 * ($$(".minibox_container").length + 1),
        '-moz-border-radius': '10px',
        '-webkit-border-radius': '10px',
        'border-radius': '10px',
        '-moz-box-shadow': '0px 10px 40px rgba(0,0,0,0.70)',
        '-webkit-box-shadow': '0px 10px 40px rgba(0,0,0,0.70)',
        'box-shadow': '0px 10px 40px rgba(0,0,0,0.70)'
      }
    }).inject(document.body);
    
    if (this.options.closeButton) {
      this.options.closeButton.addEvent('click', this.closeButtonClick);
    }
    
    this.setupEffects();
    
    this.container.adopt(this.element.setStyle('display', 'block'));
    
    // ===============
    // = Finish load =
    // ===============
    
    this.setDisplayProperty('none');
    this.fireEvent('load', this);
  },

  // =========================
  // = Attach/Detach Element =
  // =========================

  attachElement: function( content ){
    this.element = $(content).store('minibox', this);
    this.elementContainer = this.element.getParent();
  },

  detachElement: function(){
    if ( this.elementContainer && this.options.keepElementOnClose ){
      this.element.setStyle('display', 'none');
      this.elementContainer.adopt( this.element );
    }
  },
  
  // ==========
  // = Events =
  // ==========
  
  attachEvents: function() {
    window.addEvent('resize', this.updatePosition);
    if (this.options.escCloses) {
      window.addEvent('keydown', this.escKeyClose);
    }
  },
  
  detachEvents: function() {
    window.removeEvent('resize', this.updatePosition);
    if (this.options.escCloses) {
      window.removeEvent('keydown', this.escKeyClose);
    }
  },
  
  updatePosition: function() {
    var containerSize = this.container.getSize();
    var windowSize = window.getSize();
    
    this.container.setStyles({
      'top': (windowSize.y - containerSize.y)/2,
      'left': (windowSize.x - containerSize.x)/2
    });
  },
  
  escKeyClose: function(e) {
    if (e.key == "esc") {
      this.close();
    }
  },
  
  closeButtonClick: function(e) {
    this.close();
    return false;
  },
  
  // ===========
  // = Effects =
  // ===========
  
  setupEffects: function() {
    this.effects = {
      overlay: new Fx.Tween(this.overlay, {
        property: 'opacity',
        duration: this.options.effectDuration,
        onComplete: this.overlayEffectDidFinish
      }).set(0),
      container: new Fx.Tween(this.container, {
        property: 'opacity',
        duration: this.options.effectDuration,
        onComplete: this.containerEffectDidFinish
      }).set(0)
    };
  },
  
  overlayEffectDidFinish: function(overlay) {
    if (overlay.getStyle(this.effects.overlay.property) == 0) {
      // Finish close
      this.setDisplayProperty('none');
      this.fireEvent('closeFinish', this);
    } else {
      // Finish open
      this.attachEvents();
      this.effects.container.start(1);
    }
  },
  
  containerEffectDidFinish: function(container) {
    if (container.getStyle(this.effects.container.property) == 0) {
      // Finish close
      this.detachEvents();
      this.effects.overlay.start(0);
    } else {
      // Finish open
      this.fireEvent('openFinish', this);
    }
  },
  
  // ==================
  // = Showing/hiding =
  // ==================
  
  // Showing
  
  open: function() { this.display(this.options.effectDuration); },
  show: function() { this.display(0); },
  display: function(duration) {
    this.fireEvent('openStart');
    this.setDisplayProperty('block');
    this.updatePosition();
    this.effects.overlay.start(this.options.overlayOpacity);
  },
  
  // Hiding
  
  close: function() { this.undisplay(this.options.effectDuration); },
  hide: function() { this.undisplay(0); },
  undisplay: function(duration) {
    this.fireEvent('closeStart');
    this.effects.container.start(0);
  },
  
  // ========
  // = Misc =
  // ========
  
  setDisplayProperty: function(display) {
    this.overlay.setStyle('display', display);
    this.container.setStyle('display', display);
  },
  
  dispose: function() {
    this.detachEvents();
    this.detachElement();
    this.container.destroy();
    this.overlay.destroy();
    this.effects = null;
  }
});

