//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.

MooTools.More = {
  'version': '1.2.3.1'
};

/*
Script: Element.Measure.js
  Extends the Element native object to include methods useful in measuring dimensions.

  Element.measure / .expose methods by Daniel Steigerwald
  License: MIT-style license.
  Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz

  License:
    MIT-style license.

  Authors:
    Aaron Newton

*/

Element.implement({

  measure: function(fn){
    var vis = function(el) {
      return !!(!el || el.offsetHeight || el.offsetWidth);
    };
    if (vis(this)) return fn.apply(this);
    var parent = this.getParent(),
      toMeasure = [], 
      restorers = [];
    while (!vis(parent) && parent != document.body) {
      toMeasure.push(parent.expose());
      parent = parent.getParent();
    }
    var restore = this.expose();
    var result = fn.apply(this);
    restore();
    toMeasure.each(function(restore){
      restore();
    });
    return result;
  },

  expose: function(){
    if (this.getStyle('display') != 'none') return $empty;
    var before = this.style.cssText;
    this.setStyles({
      display: 'block',
      position: 'absolute',
      visibility: 'hidden'
    });
    return function(){
      this.style.cssText = before;
    }.bind(this);
  },

  getDimensions: function(options){
    options = $merge({computeSize: false},options);
    var dim = {};
    var getSize = function(el, options){
      return (options.computeSize)?el.getComputedSize(options):el.getSize();
    };
    if (this.getStyle('display') == 'none'){
      dim = this.measure(function(){
        return getSize(this, options);
      });
    } else {
      try { //safari sometimes crashes here, so catch it
        dim = getSize(this, options);
      }catch(e){}
    }
    return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
  },

  getComputedSize: function(options){
    options = $merge({
      styles: ['padding','border'],
      plains: {
        height: ['top','bottom'],
        width: ['left','right']
      },
      mode: 'both'
    }, options);
    var size = {width: 0,height: 0};
    switch (options.mode){
      case 'vertical':
        delete size.width;
        delete options.plains.width;
        break;
      case 'horizontal':
        delete size.height;
        delete options.plains.height;
        break;
    }
    var getStyles = [];
    //this function might be useful in other places; perhaps it should be outside this function?
    $each(options.plains, function(plain, key){
      plain.each(function(edge){
        options.styles.each(function(style){
          getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
        });
      });
    });
    var styles = {};
    getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
    var subtracted = [];
    $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
      var capitalized = key.capitalize();
      size['total' + capitalized] = 0;
      size['computed' + capitalized] = 0;
      plain.each(function(edge){ //top, left, right, bottom
        size['computed' + edge.capitalize()] = 0;
        getStyles.each(function(style, i){ //padding, border, etc.
          //'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
          if (style.test(edge)){
            styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
            size['total' + capitalized] = size['total' + capitalized] + styles[style];
            size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
          }
          //if width != width (so, padding-left, for instance), then subtract that from the total
          if (style.test(edge) && key != style &&
            (style.test('border') || style.test('padding')) && !subtracted.contains(style)){
            subtracted.push(style);
            size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
          }
        });
      });
    });

    ['Width', 'Height'].each(function(value){
      var lower = value.toLowerCase();
      if(!$chk(size[lower])) return;

      size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
      size['total' + value] = size[lower] + size['total' + value];
      delete size['computed' + value];
    }, this);

    return $extend(styles, size);
  }

});

/*
Script: Element.Position.js
  Extends the Element native object to include methods useful positioning elements relative to others.

  License:
    MIT-style license.

  Authors:
    Aaron Newton
*/

(function(){

var original = Element.prototype.position;

Element.implement({

  position: function(options){
    //call original position if the options are x/y values
    if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
    $each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
    options = $merge({
      relativeTo: document.body,
      position: {
        x: 'center', //left, center, right
        y: 'center' //top, center, bottom
      },
      edge: false,
      offset: {x: 0, y: 0},
      returnPos: false,
      relFixedPosition: false,
      ignoreMargins: false,
      allowNegative: false
    }, options);
    //compute the offset of the parent positioned element if this element is in one
    var parentOffset = {x: 0, y: 0};
    var parentPositioned = false;
    /* dollar around getOffsetParent should not be necessary, but as it does not return
     * a mootools extended element in IE, an error occurs on the call to expose. See:
     * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
    var offsetParent = this.measure(function(){
      return document.id(this.getOffsetParent());
    });
    if (offsetParent && offsetParent != this.getDocument().body){
      parentOffset = offsetParent.measure(function(){
        return this.getPosition();
      });
      parentPositioned = true;
      options.offset.x = options.offset.x - parentOffset.x;
      options.offset.y = options.offset.y - parentOffset.y;
    }
    //upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
    //topRight, topLeft, centerTop, centerBottom, center
    var fixValue = function(option){
      if ($type(option) != 'string') return option;
      option = option.toLowerCase();
      var val = {};
      if (option.test('left')) val.x = 'left';
      else if (option.test('right')) val.x = 'right';
      else val.x = 'center';
      if (option.test('upper') || option.test('top')) val.y = 'top';
      else if (option.test('bottom')) val.y = 'bottom';
      else val.y = 'center';
      return val;
    };
    options.edge = fixValue(options.edge);
    options.position = fixValue(options.position);
    if (!options.edge){
      if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
      else options.edge = {x:'left', y:'top'};
    }

    this.setStyle('position', 'absolute');
    var rel = document.id(options.relativeTo) || document.body;
    var calc = rel == document.body ? window.getScroll() : rel.getPosition();
    var top = calc.y;
    var left = calc.x;

    if (Browser.Engine.trident){
      var scrolls = rel.getScrolls();
      top += scrolls.y;
      left += scrolls.x;
    }

    var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
    if (options.ignoreMargins){
      options.offset.x = options.offset.x - dim['margin-left'];
      options.offset.y = options.offset.y - dim['margin-top'];
    }
    var pos = {};
    var prefY = options.offset.y;
    var prefX = options.offset.x;
    var winSize = window.getSize();
    switch(options.position.x){
      case 'left':
        pos.x = left + prefX;
        break;
      case 'right':
        pos.x = left + prefX + rel.offsetWidth;
        break;
      default: //center
        pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
        break;
    }
    switch(options.position.y){
      case 'top':
        pos.y = top + prefY;
        break;
      case 'bottom':
        pos.y = top + prefY + rel.offsetHeight;
        break;
      default: //center
        pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
        break;
    }

    if (options.edge){
      var edgeOffset = {};

      switch(options.edge.x){
        case 'left':
          edgeOffset.x = 0;
          break;
        case 'right':
          edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
          break;
        default: //center
          edgeOffset.x = -(dim.x/2);
          break;
      }
      switch(options.edge.y){
        case 'top':
          edgeOffset.y = 0;
          break;
        case 'bottom':
          edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
          break;
        default: //center
          edgeOffset.y = -(dim.y/2);
          break;
      }
      pos.x = pos.x + edgeOffset.x;
      pos.y = pos.y + edgeOffset.y;
    }
    pos = {
      left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
      top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
    };
    if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
      var winScroll = window.getScroll();
      pos.top = pos.top.toInt() + winScroll.y;
      pos.left = pos.left.toInt() + winScroll.x;
    }

    if (options.returnPos) return pos;
    else this.setStyles(pos);
    return this;
  }

});

})();

/*
Script: Element.Shortcuts.js
  Extends the Element native object to include some shortcut methods.

  License:
    MIT-style license.

  Authors:
    Aaron Newton

*/

Element.implement({

  isDisplayed: function(){
    return this.getStyle('display') != 'none';
  },

  toggle: function(){
    return this[this.isDisplayed() ? 'hide' : 'show']();
  },

  hide: function(){
    var d;
    try {
      //IE fails here if the element is not in the dom
      if ('none' != this.getStyle('display')) d = this.getStyle('display');
    } catch(e){}

    return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
  },

  show: function(display){
    return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
  },

  swapClass: function(remove, add){
    return this.removeClass(remove).addClass(add);
  }

});


/*
Script: Fx.Elements.js
  Effect to change any number of CSS properties of any number of Elements.

  License:
    MIT-style license.

  Authors:
    Valerio Proietti
*/

Fx.Elements = new Class({

  Extends: Fx.CSS,

  initialize: function(elements, options){
    this.elements = this.subject = $$(elements);
    this.parent(options);
  },

  compute: function(from, to, delta){
    var now = {};
    for (var i in from){
      var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
      for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
    }
    return now;
  },

  set: function(now){
    for (var i in now){
      var iNow = now[i];
      for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
    }
    return this;
  },

  start: function(obj){
    if (!this.check(obj)) return this;
    var from = {}, to = {};
    for (var i in obj){
      var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
      for (var p in iProps){
        var parsed = this.prepare(this.elements[i], p, iProps[p]);
        iFrom[p] = parsed.from;
        iTo[p] = parsed.to;
      }
    }
    return this.parent(from, to);
  }

});

/*
Script: Fx.Accordion.js
  An Fx.Elements extension which allows you to easily create accordion type controls.

  License:
    MIT-style license.

  Authors:
    Valerio Proietti
*/

var Accordion = Fx.Accordion = new Class({

  Extends: Fx.Elements,

  options: {/*
    onActive: $empty(toggler, section),
    onBackground: $empty(toggler, section),*/
    display: 0,
    show: false,
    height: true,
    width: false,
    opacity: true,
    fixedHeight: false,
    fixedWidth: false,
    wait: false,
    alwaysHide: false,
    trigger: 'click',
    initialDisplayFx: true
  },

  initialize: function(){
    var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
    this.parent(params.elements, params.options);
    this.togglers = $$(params.togglers);
    this.container = document.id(params.container);
    this.previous = -1;
    if (this.options.alwaysHide) this.options.wait = true;
    if ($chk(this.options.show)){
      this.options.display = false;
      this.previous = this.options.show;
    }
    if (this.options.start){
      this.options.display = false;
      this.options.show = false;
    }
    this.effects = {};
    if (this.options.opacity) this.effects.opacity = 'fullOpacity';
    if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
    if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
    for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
    this.elements.each(function(el, i){
      if (this.options.show === i){
        this.fireEvent('active', [this.togglers[i], el]);
      } else {
        for (var fx in this.effects) el.setStyle(fx, 0);
      }
    }, this);
    if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
  },

  addSection: function(toggler, element){
    toggler = document.id(toggler);
    element = document.id(element);
    var test = this.togglers.contains(toggler);
    this.togglers.include(toggler);
    this.elements.include(element);
    var idx = this.togglers.indexOf(toggler);
    toggler.addEvent(this.options.trigger, this.display.bind(this, idx));
    if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
    if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
    element.fullOpacity = 1;
    if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
    if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
    element.setStyle('overflow', 'hidden');
    if (!test){
      for (var fx in this.effects) element.setStyle(fx, 0);
    }
    return this;
  },

  display: function(index, useFx){
    useFx = $pick(useFx, true);
    index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
    if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
    this.previous = index;
    var obj = {};
    this.elements.each(function(el, i){
      obj[i] = {};
      var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
      this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
      for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
    }, this);
    return useFx ? this.start(obj) : this.set(obj);
  }

});

/*
Script: Fx.Move.js
  Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.

  License:
    MIT-style license.

  Authors:
    Aaron Newton

*/

Fx.Move = new Class({

  Extends: Fx.Morph,

  options: {
    relativeTo: document.body,
    position: 'center',
    edge: false,
    offset: {x: 0, y: 0}
  },

  start: function(destination){
    return this.parent(this.element.position($merge(this.options, destination, {returnPos: true})));
  }

});

Element.Properties.move = {

  set: function(options){
    var morph = this.retrieve('move');
    if (morph) morph.cancel();
    return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options));
  },

  get: function(options){
    if (options || !this.retrieve('move')){
      if (options || !this.retrieve('move:options')) this.set('move', options);
      this.store('move', new Fx.Move(this, this.retrieve('move:options')));
    }
    return this.retrieve('move');
  }

};

Element.implement({

  move: function(options){
    this.get('move').start(options);
    return this;
  }

});


/*
Script: Fx.Reveal.js
  Defines Fx.Reveal, a class that shows and hides elements with a transition.

  License:
    MIT-style license.

  Authors:
    Aaron Newton

*/

Fx.Reveal = new Class({

  Extends: Fx.Morph,

  options: {/*    
    onShow: $empty(thisElement),
    onHide: $empty(thisElement),
    onComplete: $empty(thisElement),
    heightOverride: null,
    widthOverride: null, */
    styles: ['padding', 'border', 'margin'],
    transitionOpacity: !Browser.Engine.trident4,
    mode: 'vertical',
    display: 'block',
    hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
  },

  dissolve: function(){
    try {
      if (!this.hiding && !this.showing){
        if (this.element.getStyle('display') != 'none'){
          this.hiding = true;
          this.showing = false;
          this.hidden = true;
          var startStyles = this.element.getComputedSize({
            styles: this.options.styles,
            mode: this.options.mode
          });
          var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
          this.element.setStyle('display', 'block');
          if (this.options.transitionOpacity) startStyles.opacity = 1;
          var zero = {};
          $each(startStyles, function(style, name){
            zero[name] = [style, 0];
          }, this);
          var overflowBefore = this.element.getStyle('overflow');
          this.element.setStyle('overflow', 'hidden');
          var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
          this.$chain.unshift(function(){
            if (this.hidden){
              this.hiding = false;
              $each(startStyles, function(style, name){
                startStyles[name] = style;
              }, this);
              this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
              if (setToAuto){
                if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
                if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
              }
              if (hideThese) hideThese.setStyle('visibility', 'visible');
            }
            this.fireEvent('hide', this.element);
            this.callChain();
          }.bind(this));
          if (hideThese) hideThese.setStyle('visibility', 'hidden');
          this.start(zero);
        } else {
          this.callChain.delay(10, this);
          this.fireEvent('complete', this.element);
          this.fireEvent('hide', this.element);
        }
      } else if (this.options.link == 'chain'){
        this.chain(this.dissolve.bind(this));
      } else if (this.options.link == 'cancel' && !this.hiding){
        this.cancel();
        this.dissolve();
      }
    } catch(e){
      this.hiding = false;
      this.element.setStyle('display', 'none');
      this.callChain.delay(10, this);
      this.fireEvent('complete', this.element);
      this.fireEvent('hide', this.element);
    }
    return this;
  },

  reveal: function(){
    try {
      if (!this.showing && !this.hiding){
        if (this.element.getStyle('display') == 'none' ||
           this.element.getStyle('visiblity') == 'hidden' ||
           this.element.getStyle('opacity') == 0){
          this.showing = true;
          this.hiding = false;
          this.hidden = false;
          var setToAuto, startStyles;
          //toggle display, but hide it
          this.element.measure(function(){
            setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
            //create the styles for the opened/visible state
            startStyles = this.element.getComputedSize({
              styles: this.options.styles,
              mode: this.options.mode
            });
          }.bind(this));
          $each(startStyles, function(style, name){
            startStyles[name] = style;
          });
          //if we're overridding height/width
          if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
          if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
          if (this.options.transitionOpacity) {
            this.element.setStyle('opacity', 0);
            startStyles.opacity = 1;
          }
          //create the zero state for the beginning of the transition
          var zero = {
            height: 0,
            display: this.options.display
          };
          $each(startStyles, function(style, name){ zero[name] = 0; });
          var overflowBefore = this.element.getStyle('overflow');
          //set to zero
          this.element.setStyles($merge(zero, {overflow: 'hidden'}));
          //hide inputs
          var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
          if (hideThese) hideThese.setStyle('visibility', 'hidden');
          //start the effect
          this.start(startStyles);
          this.$chain.unshift(function(){
            this.element.setStyle('overflow', overflowBefore);
            if (!this.options.heightOverride && setToAuto){
              if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
              if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
            }
            if (!this.hidden) this.showing = false;
            if (hideThese) hideThese.setStyle('visibility', 'visible');
            this.callChain();
            this.fireEvent('show', this.element);
          }.bind(this));
        } else {
          this.callChain();
          this.fireEvent('complete', this.element);
          this.fireEvent('show', this.element);
        }
      } else if (this.options.link == 'chain'){
        this.chain(this.reveal.bind(this));
      } else if (this.options.link == 'cancel' && !this.showing){
        this.cancel();
        this.reveal();
      }
    } catch(e){
      this.element.setStyles({
        display: this.options.display,
        visiblity: 'visible',
        opacity: 1
      });
      this.showing = false;
      this.callChain.delay(10, this);
      this.fireEvent('complete', this.element);
      this.fireEvent('show', this.element);
    }
    return this;
  },

  toggle: function(){
    if (this.element.getStyle('display') == 'none' ||
       this.element.getStyle('visiblity') == 'hidden' ||
       this.element.getStyle('opacity') == 0){
      this.reveal();
    } else {
      this.dissolve();
    }
    return this;
  }

});

Element.Properties.reveal = {

  set: function(options){
    var reveal = this.retrieve('reveal');
    if (reveal) reveal.cancel();
    return this.eliminate('reveal').store('reveal:options', $extend({link: 'cancel'}, options));
  },

  get: function(options){
    if (options || !this.retrieve('reveal')){
      if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
      this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
    }
    return this.retrieve('reveal');
  }

};

Element.Properties.dissolve = Element.Properties.reveal;

Element.implement({

  reveal: function(options){
    this.get('reveal', options).reveal();
    return this;
  },

  dissolve: function(options){
    this.get('reveal', options).dissolve();
    return this;
  },

  nix: function(){
    var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
    this.get('reveal', params.options).dissolve().chain(function(){
      this[params.destroy ? 'destroy' : 'dispose']();
    }.bind(this));
    return this;
  },

  wink: function(){
    var params = Array.link(arguments, {duration: Number.type, options: Object.type});
    var reveal = this.get('reveal', params.options);
    reveal.reveal().chain(function(){
      (function(){
        reveal.dissolve();
      }).delay(params.duration || 2000);
    });
  }


});

/*
Script: Fx.Scroll.js
  Effect to smoothly scroll any element, including the window.

  License:
    MIT-style license.

  Authors:
    Valerio Proietti
*/

Fx.Scroll = new Class({

  Extends: Fx,

  options: {
    offset: {x: 0, y: 0},
    wheelStops: true
  },

  initialize: function(element, options){
    this.element = this.subject = document.id(element);
    this.parent(options);
    var cancel = this.cancel.bind(this, false);

    if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);

    var stopper = this.element;

    if (this.options.wheelStops){
      this.addEvent('start', function(){
        stopper.addEvent('mousewheel', cancel);
      }, true);
      this.addEvent('complete', function(){
        stopper.removeEvent('mousewheel', cancel);
      }, true);
    }
  },

  set: function(){
    var now = Array.flatten(arguments);
    this.element.scrollTo(now[0], now[1]);
  },

  compute: function(from, to, delta){
    return [0, 1].map(function(i){
      return Fx.compute(from[i], to[i], delta);
    });
  },

  start: function(x, y){
    if (!this.check(x, y)) return this;
    var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
    var scroll = this.element.getScroll(), values = {x: x, y: y};
    for (var z in values){
      var max = scrollSize[z] - offsetSize[z];
      if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
      else values[z] = scroll[z];
      values[z] += this.options.offset[z];
    }
    return this.parent([scroll.x, scroll.y], [values.x, values.y]);
  },

  toTop: function(){
    return this.start(false, 0);
  },

  toLeft: function(){
    return this.start(0, false);
  },

  toRight: function(){
    return this.start('right', false);
  },

  toBottom: function(){
    return this.start(false, 'bottom');
  },

  toElement: function(el){
    var position = document.id(el).getPosition(this.element);
    return this.start(position.x, position.y);
  },

  scrollIntoView: function(el, axes, offset){
    axes = axes ? $splat(axes) : ['x','y'];
    var to = {};
    el = document.id(el);
    var pos = el.getPosition(this.element);
    var size = el.getSize();
    var scroll = this.element.getScroll();
    var containerSize = this.element.getSize();
    var edge = {
      x: pos.x + size.x,
      y: pos.y + size.y
    };
    ['x','y'].each(function(axis) {
      if (axes.contains(axis)) {
        if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
        if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
      }
      if (to[axis] == null) to[axis] = scroll[axis];
      if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
    }, this);
    if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
    return this;
  }

});


/*
Script: Fx.Slide.js
  Effect to slide an element in and out of view.

  License:
    MIT-style license.

  Authors:
    Valerio Proietti
*/

Fx.Slide = new Class({

  Extends: Fx,

  options: {
    mode: 'vertical'
  },

  initialize: function(element, options){
    this.addEvent('complete', function(){
      this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
      if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
    }, true);
    this.element = this.subject = document.id(element);
    this.parent(options);
    var wrapper = this.element.retrieve('wrapper');
    this.wrapper = wrapper || new Element('div', {
      styles: $extend(this.element.getStyles('margin', 'position'), {overflow: 'hidden'})
    }).wraps(this.element);
    this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
    this.now = [];
    this.open = true;
  },

  vertical: function(){
    this.margin = 'margin-top';
    this.layout = 'height';
    this.offset = this.element.offsetHeight;
  },

  horizontal: function(){
    this.margin = 'margin-left';
    this.layout = 'width';
    this.offset = this.element.offsetWidth;
  },

  set: function(now){
    this.element.setStyle(this.margin, now[0]);
    this.wrapper.setStyle(this.layout, now[1]);
    return this;
  },

  compute: function(from, to, delta){
    return [0, 1].map(function(i){
      return Fx.compute(from[i], to[i], delta);
    });
  },

  start: function(how, mode){
    if (!this.check(how, mode)) return this;
    this[mode || this.options.mode]();
    var margin = this.element.getStyle(this.margin).toInt();
    var layout = this.wrapper.getStyle(this.layout).toInt();
    var caseIn = [[margin, layout], [0, this.offset]];
    var caseOut = [[margin, layout], [-this.offset, 0]];
    var start;
    switch (how){
      case 'in': start = caseIn; break;
      case 'out': start = caseOut; break;
      case 'toggle': start = (layout == 0) ? caseIn : caseOut;
    }
    return this.parent(start[0], start[1]);
  },

  slideIn: function(mode){
    return this.start('in', mode);
  },

  slideOut: function(mode){
    return this.start('out', mode);
  },

  hide: function(mode){
    this[mode || this.options.mode]();
    this.open = false;
    return this.set([-this.offset, 0]);
  },

  show: function(mode){
    this[mode || this.options.mode]();
    this.open = true;
    return this.set([0, this.offset]);
  },

  toggle: function(mode){
    return this.start('toggle', mode);
  }

});

Element.Properties.slide = {

  set: function(options){
    var slide = this.retrieve('slide');
    if (slide) slide.cancel();
    return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
  },

  get: function(options){
    if (options || !this.retrieve('slide')){
      if (options || !this.retrieve('slide:options')) this.set('slide', options);
      this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
    }
    return this.retrieve('slide');
  }

};

Element.implement({

  slide: function(how, mode){
    how = how || 'toggle';
    var slide = this.get('slide'), toggle;
    switch (how){
      case 'hide': slide.hide(mode); break;
      case 'show': slide.show(mode); break;
      case 'toggle':
        var flag = this.retrieve('slide:flag', slide.open);
        slide[flag ? 'slideOut' : 'slideIn'](mode);
        this.store('slide:flag', !flag);
        toggle = true;
      break;
      default: slide.start(how, mode);
    }
    if (!toggle) this.eliminate('slide:flag');
    return this;
  }

});


/*
Script: Fx.SmoothScroll.js
  Class for creating a smooth scrolling effect to all internal links on the page.

  License:
    MIT-style license.

  Authors:
    Valerio Proietti
*/

var SmoothScroll = Fx.SmoothScroll = new Class({

  Extends: Fx.Scroll,

  initialize: function(options, context){
    context = context || document;
    this.doc = context.getDocument();
    var win = context.getWindow();
    this.parent(this.doc, options);
    this.links = this.options.links ? $$(this.options.links) : $$(this.doc.links);
    var location = win.location.href.match(/^[^#]*/)[0] + '#';
    this.links.each(function(link){
      if (link.href.indexOf(location) != 0) {return;}
      var anchor = link.href.substr(location.length);
      if (anchor) this.useLink(link, anchor);
    }, this);
    if (!Browser.Engine.webkit419) {
      this.addEvent('complete', function(){
        win.location.hash = this.anchor;
      }, true);
    }
  },

  useLink: function(link, anchor){
    var el;
    link.addEvent('click', function(event){
      if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
      if (el) {
        event.preventDefault();
        this.anchor = anchor;
        this.toElement(el);
        link.blur();
      }
    }.bind(this));
  }

});