/*
 * Selection: Simple gui selection using Prototype
 * Written by Andrew Kaspick (andrew@redlinesoftware.com)
 * Version: 0.1
 * http://creativecommons.org/licenses/by-sa/3.0/
 */

/* Usage:
 * 
 * 1. Create the selection object passing in the selector for eligible elements.
 *    eg.) Create a selection object for all 'a' tags (any valid prototype selector will work)
 *    var s = new Selection('a');
 * 
 * 2. Start the selection process (via key press, button press, link click, etc.)
 *    This will create the overlay and the mouse will now be used for selection.
 *    s.start();
 *
 * 3. Stop the selection process (via key press, button press, link click, etc.)
 *    Pressing the ESC key stops the selection by default.
 *
 * 4. Retrieve your selected elements.  The selection member contains all of the selected elements.
 *    var selected_stuff = s.selection;
 */
var Selection = Class.create();

Selection.prototype = {
  initialize: function(selector) {
    this.selector = selector;
    this.selection = new Array();

    this.options = Object.extend({
      stopAfterSelect : false,
      selectionClass : 'selection_box',
      selectClass : 'selected_element',
      overlay_id : 'selection_overlay',
      selectionFinish : Prototype.emptyFunction
    }, arguments[1] || {});

    this.drag = false;
  },

  onKeyPress: function(event) {
    switch(event.keyCode) {
      case Event.KEY_ESC:
        Event.stop(event);
        this.stop();
        return;
    }
  },

  onMouseDown: function(event) {
    if (Event.isLeftClick(event)) {
      this.selectionBox = $(Builder.node('div', {'class':this.options.selectionClass}));
      this.selectionBox.setOpacity(0.2);

      this.startPoints = [Event.pointerX(event), Event.pointerY(event)];
      this.selectionBox.setStyle({top:this.startPoints[1]+'px', left:this.startPoints[0]+'px', width:'0px', height:'0px'})
      document.body.appendChild(this.selectionBox);
      this.refresh();
      this.selection = new Array();
      this.drag = true;
      this.onMouseMove(event);
    }
  },

  onMouseMove: function(event) {
    if (this.drag) {
      var points = [Event.pointerX(event), Event.pointerY(event)];

      var top, left;
      var width  = points[0]-this.startPoints[0];
      var height = points[1]-this.startPoints[1];

      if (width < 0) {
        width = -width;
        left = points[0];
      }
      else {
        left = this.startPoints[0];
      }

      if (height < 0) {
        height = -height;
        top = points[1];
      }
      else {
        top = this.startPoints[1];
      }
      
      this.selectionBox._top = top;
      this.selectionBox._left = left;
      this.selectionBox._bottom = top + height;
      this.selectionBox._right = left + width;
      
      this.selectionBox.setStyle({top:top+'px', left:left+'px', height:height+'px', width:width+'px'});

      this.selection = new Array();
      this.elements.each(
        function(elem) {
          if (this.overlap(elem)) {
            this.select(elem);
            this.selection.push(elem);
          }
          else {
            this.deselect(elem);
          }
        }.bind(this)
      )
    }
  },

  onMouseUp: function(event) {
    if (this.drag) {
      this.selectionBox.remove();
      this.drag = false;

      if (this.options.stopAfterSelect) this.stop();

      this.options.selectionFinish(this.selection);
    }
  },

  select: function(elem) {
    elem.addClassName(this.options.selectClass);
  },

  deselect: function(elem) {
    if (elem.hasClassName(this.options.selectClass)) {
      elem.removeClassName(this.options.selectClass);
    }
  },

  overlap: function(elem) {
    var x0 = this.selectionBox._left;
    var y0 = this.selectionBox._top;
    var x1 = this.selectionBox._right;
    var y1 = this.selectionBox._bottom;
    
    var x2 = elem._left;
    var y2 = elem._top;
    var x3 = elem._right;
    var y3 = elem._bottom;

    return !( (x0 < x2 && x1 < x2) || (x0 > x3 && x1 > x3) || (y0 < y2 && y1 < y2) || (y0 > y3 && y1 > y3) )
  },

  // start the selection process
  start: function() {
    var div = $(Builder.node('div', {id:this.options.overlay_id}));
    div.setOpacity(0.5);
    document.body.appendChild(div)

    this.onkeypressListener = this.onKeyPress.bindAsEventListener(this);
    Event.observe(document, 'keypress', this.onkeypressListener);

    this.onmousedownListener = this.onMouseDown.bindAsEventListener(this);
    Event.observe(div, 'mousedown', this.onmousedownListener);
    this.onmousemoveListener = this.onMouseMove.bindAsEventListener(this);
    Event.observe(div, 'mousemove', this.onmousemoveListener);
    this.onmouseupListener = this.onMouseUp.bindAsEventListener(this);
    Event.observe(div, 'mouseup', this.onmouseupListener);
  },

  // stop the selection process
  stop: function() {
    this.selection.each(function(elem) { this.deselect(elem) }.bind(this));
          
    var overlay = $(this.options.overlay_id);
    
    Event.stopObserving(document, 'keypress', this.onkeypressListener);
    Event.stopObserving(overlay, 'mousedown', this.onmousedownListener);
    Event.stopObserving(overlay, 'mousemove', this.onmousemoveListener);
    Event.stopObserving(overlay, 'mouseup', this.onmouseupListener);
        
    overlay.remove();
  },

  // refresh the elements eligible for selection
  refresh: function() {
    this.elements = $$(this.selector);

    this.elements.each(
      function(elem) {
        var pos = Position.cumulativeOffset(elem);

        var dims = elem.getDimensions();
        elem._top = pos[1];
        elem._left = pos[0];
        elem._bottom = pos[1] + dims.height
        elem._right  = pos[0] + dims.width
      }
    );
  }
}
