/*
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
    Daniel Steigerwald
 
*/
 
Element.implement({
 
  measure: function(fn){
    var restore = this.expose();
    var result = fn.apply(this);
    restore();
    return result;
  },
 
  expose: function(){
    if (this.getStyle('display') != 'none') return $empty;
    var before = {};
    var styles = { visibility: 'hidden', display: 'block', position:'absolute' };
    $each(styles, function(value, style){
      before[style] = this.style[style]||'';
    }, this);
    this.setStyles(styles);
    return (function(){ this.setStyles(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 = this.getStyles.apply(this, getStyles);
    var subtracted = [];
    $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
      size['total' + key.capitalize()] = 0;
      size['computed' + key.capitalize()] = 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(); //styles['padding-left'] = 5;
            if (isNaN(styles[style]))styles[style] = 0;
            size['total' + key.capitalize()] = size['total' + key.capitalize()] + 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' + key.capitalize()] = size['computed' + key.capitalize()]-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);
  }
 
});