/*
 * Copyright (c) 2009 Alexander Staubo <alex@bengler.no>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 * 3. Neither the name of Alexander Staubo nor the names of its contributors 
 * may be used to endorse or promote products derived from this software 
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Dynamic resizing of text areas. Requires Prototype. Example usage:
 *
 *   new DynamicTextAreaResizing("my_text_area");
 *
 */
var DynamicTextAreaResizing = Class.create({
  
  /**
   * Initialize dynamic text area resizing. Options:
   *
   * * minHeight: Specify the minimum height, in pixels. By default this is
   *   the size specified by the text area element's "rows" attribute.
   * * maxHeight: Specify the maximum height, in pixels.
   */
  initialize: function(fieldElement, options) {
    this.fieldElement = $(fieldElement);
    if (options) {
      this.minHeight = options.minHeight;
      this.maxHeight = options.maxHeight;
    }
    window.setTimeout(this.setup.bind(this), 50);  // Delay for page to render a bit
  },

  setup: function() {
    this.fieldElement.style.resize = "none";  // Hide resize handle in Safari
    this.fieldElement.observe("change", this.resize.bind(this));
    this.fieldElement.observe("keydown", this.resize.bind(this));
    this.fieldElement.observe("keyup", this.resize.bind(this));
    this.fieldElement.observe("focus", this.resize.bind(this));
    this.fieldElement.observe("blur", this.resize.bind(this));
    if (this.fieldElement.rows) {
      this.minHeight = this.minHeight || this._measureHeightByRows(this.fieldElement.rows);
    }
    this.minHeight = this.minHeight || 33;
    this.resize();
    window.setInterval(this.resize.bind(this), 500);
  },

  resize: function() {
    var width = this.fieldElement.getDimensions().width;
    if (!this.hiddenElement && width) {
      this.hiddenElement = this._cloneStyles(this.fieldElement, $(document.createElement("textarea")));
      this.hiddenElement.style.visibility = "hidden";
      this.hiddenElement.style.position = "absolute";
      this.hiddenElement.style.left = "0px";
      this.hiddenElement.style.top = "0px";
      this.hiddenElement.style.width = width + "px";
      this.hiddenElement.style.height = "1px";
      document.body.appendChild(this.hiddenElement);
    }
    if (this.hiddenElement) {
      var content = this.fieldElement.value;
      this.hiddenElement.value = content;
      this.hiddenElement.scrollTop = 0;
      var height = this.hiddenElement.scrollHeight;
      height += 30;  // Make room for horizontal scrollbar
      if (this.maxHeight) {
        height = Math.min(height, this.maxHeight);
      }
      if (this.minHeight) {
        height = Math.max(height, this.minHeight);
      }
      this.fieldElement.style.height = height + "px";
    }
  },

  /**
   * Measure pixel height given a number of rows, by temporarily inserting an
   * element with the given number of empty rows.
   */
  _measureHeightByRows: function(rows) {
    var element = this._cloneStyles(this.fieldElement, $(document.createElement("div")));
    var text = "";
    for (var i = 0; i < rows; i++) {
      text += "<br/>";
    }
    element.innerHTML = text;
    var parent = this.fieldElement.parentNode;
    parent.appendChild(element);
    var height = element.getDimensions().height;
    parent.removeChild(element);
    return height;
  },

  _cloneStyles: function(field, element) {
    element.style.fontFamily = field.getStyle("font-family");
    element.style.fontSize = field.getStyle("font-size");
    element.style.lineHeight = field.getStyle("line-height");
    element.style.paddingTop = field.getStyle("padding-top");
    element.style.paddingRight = field.getStyle("padding-right");
    element.style.paddingBottom = field.getStyle("padding-bottom");
    element.style.paddingLeft = field.getStyle("padding-left");
    element.style.borderTopWidth = field.getStyle("border-top-width");
    element.style.borderRightWidth = field.getStyle("border-right-width");
    element.style.borderBottomWidth = field.getStyle("border-bottom-width");
    element.style.borderLeftWidth = field.getStyle("border-left-width");
    element.style.borderTopColor = field.getStyle("border-top-color");
    element.style.borderRightColor = field.getStyle("border-right-color");
    element.style.borderBottomColor = field.getStyle("border-bottom-color");
    element.style.borderLeftColor = field.getStyle("border-left-color");
    element.style.borderTopStyle = field.getStyle("border-top-style");
    element.style.borderRightStyle = field.getStyle("border-right-style");
    element.style.borderBottomStyle = field.getStyle("border-bottom-style");
    element.style.borderLeftStyle = field.getStyle("border-left-style");
    element.style.overflow = field.getStyle("overflow");
    element.style.resize = "none";  // Safari
    return element;
  }
});
