/*
  Proto!MultiSelect 0.2
  - Prototype version required: 6.0
  
  Credits:
  - Idea: Facebook + Apple Mail
  - Caret position method: Diego Perini <http://javascript.nwbox.com/cursor_position/cursor.js>
  - Guillermo Rauch: Original MooTools script
  - Ran Grushkowsky/InteRiders Inc. : Porting into Prototype and further development
*/

/* Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message! */

var TextboxList = Class.create({ 
  initialize: function(el, options) {
    this.options = Object.extend({
      className: 'bit',
      separator: ', ',
      hideEmpty: true,
      structures: 10,
      maxChoices: 10,
      onMaximum: null
    }, options || {});
    
    this.el = $(el);
    this.bits = new Hash();
    this.bitCount = 0;

    this.current = false;    
    this.mainInput = this.createInput({});

    this.events = new Hash();
    
    this.holder = new Element('ul', {'class': 'holder'}).insert(this.mainInput);
    this.el.insert({'before':this.holder});
    this.holder.observe('click', function(event){
      event.stop();
      if(this.mainInput != this.current)
        this.focus(this.mainInput);     
    }.bind(this));
    
    this.el.hide();
    this.setEvents();
  },
  
  setEvents: function() {
    document.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) {      
      if(!this.current)
        return;
      if(this.current.retrieveData('type') == 'box' && e.keyCode == Event.KEY_BACKSPACE)
        e.stop();
    }.bind(this));      
         
    document.observe('keyup', function(e) {
      e.stop();
      if(! this.current)
        return;
      switch(e.keyCode){
        case Event.KEY_LEFT:
          return this.move('left');
        case Event.KEY_RIGHT:
          return this.move('right');
        case Event.KEY_DELETE:
        case Event.KEY_BACKSPACE:
          return this.moveDispose();
      }
    }.bind(this)).observe('click', function() {
      document.fire('blur');
    }.bindAsEventListener(this));
  },
  
  add: function(datum) {
    if (this.bitCount >= this.options.maxChoices) {
      if (this.options.onMaximum)
        this.options.onMaximum();
      return;
    }
      
    var id = this.options.className + '-' + this.bitCount++;
    var el = this.createBox(datum, {'id': id});
    
    (this.current || this.mainInput).insert({'before': el});
    
    el.observe('click', function(e) {
      e.stop();
      this.focus(el);
    }.bind(this));
    
    this.bits.set(id, datum.value);
    
    return el;
  },
  
  dispose: function(el) {
    this.bitCount--;
    this.bits.unset(el.id);
    
    if(this.current == el) 
      this.focus(el.next());
    if(el.retrieveData('type') == 'box') el.onBoxDispose(this);
      el.remove();    
      
    return this;
  },
  
  focus: function(el, noFocus) {
    if(! this.current)
      el.fire('focus');
    else if(this.current == el)
      return this;
      
    this.blur();
    el.addClassName(this.options.className + '-' + el.retrieveData('type') + '-focus');
          
    if(el.retrieveData('type') == 'input') {
      el.onInputFocus(this);      
      if(!noFocus)
        this.callEvent(el.retrieveData('input'), 'focus');
    }
    else el.fire('onBoxFocus');
    
    this.current = el;    
    return this;
  },
  
  blur: function(noBlur) {
    if(! this.current)
      return this;
    if(this.current.retrieveData('type') == 'input') {
      var input = this.current.retrieveData('input');
      if(!noBlur)
        this.callEvent(input, 'blur');   
      
      input.onInputBlur(this);
    }
    else this.current.fire('onBoxBlur');
      
    this.current.removeClassName(this.options.className + '-' + this.current.retrieveData('type') + '-focus');
    this.current = false;
    
    return this;
  },
  
  createBox: function(datum, options) {
    var el = new Element('li', options).addClassName(this.options.className + '-box').cacheData('type', 'box');
    el.innerHTML = datum.caption;
    
    return el;
  },
  
  createInput: function(options) {
    var li = new Element('li', {'class': this.options.className + '-input'});
    var el = new Element('input', Object.extend(options, {'type': 'text'}));
    
    el.observe('click', function(e) { e.stop(); }).observe('focus', function(e) {
      if(!this.isSelfEvent('focus'))
        this.focus(li, true);
    }.bind(this))
    
    el.observe('blur', function() {
      if(! this.isSelfEvent('blur'))
        this.blur(true);
    }.bind(this))
    
    el.observe('keydown', function(e) {
      this.cacheData('lastValue', this.value).cacheData('lastCaret', this.getCaretPosition());
    });
    
    var ret = li.cacheData('type', 'input').cacheData('input', el).insert(el);
    return ret;
  },
  
  callEvent: function(el, type) {
    this.events[type] = el;
    el[type]();
  },
  
  isSelfEvent: function(type) {
    var evt;
    if (evt = this.events[type])
    {
      this.events[type] = null;
      return !!evt;
    }
    else
      return false
  },
  
  checkInput: function() {
    var input = this.current.retrieveData('input');
    return !input.retrieveData('lastValue' || (input.getCaretPosition() === 0 && input.retrieveData('lastCaret') === 0));
  },
  
  move: function(direction) {
    var el = this.current[(direction == 'left' ? 'previous' : 'next')]();
    if(el && (! this.current.retrieveData('input') || ((this.checkInput() || direction == 'right'))))
      this.focus(el);
    return this;
  },
  
  moveDispose: function() {  
    if(this.current.retrieveData('type') == 'box')
      return this.dispose(this.current);
  
    if(this.checkInput() && this.bits.keys().length && this.current.previous())
      return this.focus(this.current.previous());
  }
  
});

//helper functions 
Element.addMethods({
  getCaretPosition: function() {
    if (this.createdatumRange) {
      var r = document.selection.createRange().duplicate();
        r.moveEnd('character', this.value.length);
        if (r.datum === '') return this.value.length;
        return this.value.lastIndexOf(r.datum);
    } else return this.selectionStart;
  },
  cacheData: function(element, key, value) { 
    if (Object.isUndefined(this[$(element).identify()]) || !Object.isHash(this[$(element).identify()]))
        this[$(element).identify()] = $H();
    this[$(element).identify()].set(key,value);
    return element;
  },
  retrieveData: function(element,key) {
    return this[$(element).identify()].get(key);
  }
});

/*
  Proto!MultiSelect 0.2
  - Prototype version required: 6.0
  
  Credits:
  - Idea: Facebook
  - Guillermo Rauch: Original MooTools script
  - Ran Grushkowsky/InteRiders Inc. : Porting into Prototype and further development
*/

/* Copyright: InteRiders <http://interiders.com/> - Distributed under MIT - Keep this message! */

var FacebookList = Class.create(TextboxList, {   
  initialize: function($super, el, autoHolder, options) {
    this.options = Object.extend({
		  opacity: 0.9,
		  maxResults: 10,
		  minChars: 1,
		  maxChoices: 10,
		  onAdd: null,
		  onDispose: null,
		  onMaximum: null
    }, options || {});
    
    $super(el, this.options);
        
    this.data = [];    
    
    this.autoHolder = $(autoHolder).setOpacity(this.options.opacity); 
    
    this.autoHolder.observe('mouseover',function() { this.curOn = true; }.bind(this));
    this.autoHolder.observe('mouseout', function() { this.curOn = false; }.bind(this));
    
    this.autoResults = this.autoHolder.select('ul').first();
  },
  
  autoShow: function(search) {
    if (search)
      search = search.replace(/\s+/, "\\s+");
    this.autoHolder.setStyle({'display': 'block'});
    this.autoHolder.descendants().invoke('hide');
    
    var count = 0;
    
    if(!search || !search.strip() || (!search.length || search.length < this.options.minChars)) 
    {
      this.autoHolder.select('.default').first().setStyle({'display': 'block'});
      this.resultsShown = false;
    } else {
      this.resultsShown = true;
      this.autoResults.setStyle({'display': 'block'}).innerHTML = '';
      
      var regexp = new RegExp(search,'i');
      
      this.data.filter(function(datum) {
        return (datum && datum.caption) ? regexp.test(datum.caption) : false;
      }).each(function(structure, index) {
        if(index >= this.options.maxResults)
          return;
        count++;
        
        var parent = this;
        var el = new Element('li');
        el.observe('click',function(e) { 
            e.stop();
            parent.autoAdd(this); 
        }).observe('mouseover',function() { 
            parent.autoFocus(this);
        }).innerHTML = this.autoHighlight(structure.caption, search);
        
        this.autoResults.insert(el);
        el.cacheData('structure', structure);
        
        if(index == 0)
          this.autoFocus(el);
      }.bind(this), this);
    }
    
    if (!count)
      this.autoCurrent = null;

    this.autoResults.setStyle({'height': (count * 26) + 'px'});
    
    return this;
  },
  
  autoHighlight: function(html, highlight) {
    return html.gsub(new RegExp(highlight,'i'), function(match) {
      return '<em>' + match[0] + '</em>';
    });
  },
  
  autoHide: function() {    
    this.resultsShown = false;
    this.autoHolder.hide();    
    return this;
  },
  
  autoFocus: function(el) {
    if(!el) return;
    if(this.autoCurrent) this.autoCurrent.removeClassName('auto-focus');
    this.autoCurrent = el.addClassName('auto-focus');
    return this;
  },
  
  autoMove: function(direction) {    
    if(!this.resultsShown) return;
    this.autoFocus(this.autoCurrent[(direction == 'up' ? 'previous' : 'next')]());
    this.autoResults.scrollTop = this.autoCurrent.positionedOffset()[1] - this.autoCurrent.getHeight();         
    return this;
  },
  
  autoFeed: function(hash) {
    this.data.push(hash);
    return this;
  },
  
  autoAdd: function(el) {
    if(!el || ! el.retrieveData('structure')) return;
    var box = this.add(el.retrieveData('structure'));
    
    if (this.options.onAdd)
      this.options.onAdd(el.retrieveData('structure'), true, box);
      
    delete this.data[this.data.indexOf(el.retrieveData('structure'))];
    this.autoHide();
    
    var input = this.lastinput || this.current.retrieveData('input');
    input.clear().focus();
    
    return this;
  },
  
  createInput: function($super,options) {
    var li = $super(options);
    var input = li.retrieveData('input');
    
    input.observe('keydown', function(e) {
        this.doSearch = false;
        
        switch(e.keyCode) {
          case Event.KEY_UP: e.stop(); return this.autoMove('up');
          case Event.KEY_DOWN: e.stop(); return this.autoMove('down');        
          case Event.KEY_RETURN:
            e.stop();
            if(! this.autoCurrent) break;
            this.autoAdd(this.autoCurrent);
            this.autoCurrent = false;
            this.autoEnter = true;
            break;
          case Event.KEY_ESC: 
            this.autoHide();
            if(this.current && this.current.retrieveData('input'))
              this.current.retrieveData('input').clear();
            break;
          default: this.doSearch = true;
        }
    }.bind(this));
    
    input.observe('keyup',function(e) {    
        switch(e.keyCode) {
          case Event.KEY_UP: 
          case Event.KEY_DOWN: 
          case Event.KEY_RETURN:
          case Event.KEY_ESC: 
            break;              
          default: 
            if(this.doSearch)
              this.autoShow(input.value);          
        }        
    }.bind(this));
    
    input.observe(Prototype.Browser.IE ? 'keydown' : 'keypress', function(e) { 
      if(this.autoEnter) e.stop();
      this.autoEnter = false;
    }.bind(this));
    return li;
  },
  
  createBox: function($super, datum, options) {
    var li = $super(datum, options);
    
    li.observe('mouseover',function() { 
        this.addClassName('bit-hover');
    }).observe('mouseout',function() { 
        this.removeClassName('bit-hover') 
    });
    
    var a = new Element('a', {
      'href': '#',
      'class': 'closebutton'
      }
    );
    a.observe('click',function(e) {
      e.stop();
      if(! this.current) this.focus(this.mainInput);
      this.dispose(li);
    }.bind(this));
    
    li.insert(a).cacheData('datum', datum);
    return li;
  }
  
});

Element.addMethods({
    onBoxDispose: function(item, obj) {
      obj.autoFeed(item.retrieveData('datum'));
      if (obj.options.onDispose)
        obj.options.onDispose(item.retrieveData('datum'), false);
    },
    onInputFocus: function(el,obj) { obj.autoShow(); },    
    onInputBlur: function(el,obj) { 
        obj.lastinput = el;
        if(!obj.curOn) {
            obj.blurHide = obj.autoHide.bind(obj).delay(0.1);
        }
    }
});