/* -----------------------------------------------------------------------------
* app.js
* Copyright 2009 wollzelle GmbH (http://wollzelle.com). All rights reserved.
* ------------------------------------------------------------------------------ */

(function(){
  
  var $ = document.id;
  
  /**
   * Function#debounce(delay) -> Function
   * 
   * Based on http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
   * 
   * Debounce the function execution with delay (ms). 
   * This means, the debounced function will be executed only once after delay if no new calls of that function being happen in the meantime.
   * This is useful in certain situations, like scrolling or mousemove events, when function being called too many times.
   * Example usage: 
   *  
   *    myFuncDebounced = myFunc.bind(this).debounce(250); // This will debounce myFunc for 250ms and set the scope to this Object.
   *    Event.observe(document, 'scroll', myFuncDebounced); // This will set debounced function as an observer. Function myFunc will be called only once after scrolling is completed.
   *  
   * Important note: Functions should be defined as variables, since no anonymous functions are allowed.
   *  
   * - delay (Number): Delay in milliseconds  
   * 
   **/
  Function.implement({
    debounce: function(delay) {
      var func = this, timeout;
      return function debounced() {
        var obj = this, args = arguments;
        function delayed () {
          func.apply(obj, args);
          timeout = null; 
        };

        if (timeout) clearTimeout(timeout);
        timeout = setTimeout(delayed, delay || 100); 
      }
    }
  });
  
  Element.implement({
    /**
     * Element#enhanceWithHtml5() -> Element
     * 
     * Adds support for "placeholder" attribute in non-supported yet browsers.
     * Takes input element as parameter.
     * 
     **/
    enhanceWithHtml5: function(){
      if(Browser.Engine.webkit) return this; // Webkit has a native placeholders implementation for inputs. Do nothing.
      if(!this.nodeName.test('input', 'i')) return this; // This isn't an input element. Do nothing; 
    
      // Define a local variable to eliminate extra binds.
      var input = this;
    
      // Otherwise, create label element which will containt the placeholder text.
      var label = new Element('label', { 
        'class' : 'placeholder',
        'for'   : input.get('id')
      }).set('text', input.get('placeholder')).inject(input, 'after');
  
      // On initialize, check is no value given and then make the placeholder text visible.
      if(this.get('value').length == 0) label.addClass('visible');
    
      // Add events
      input.addEvents({
        'focus': function(e){
          label.removeClass('visible');
        },
        'blur': function(e){
          var isEmpty = (input.get('value').length == 0);
          if(isEmpty) label.addClass('visible');
        },
        'change': function(e){
          var isEmpty = (input.get('value').length == 0);
          if(isEmpty)
            label.addClass('visible');
          else
            label.removeClass('visible');
        }
      });
    
      // Return element;
      return this;
    }
  });
  
  // Init application and set observers
  window.addEvent('domready', function(){
    
    // Enchance defined scroll areas
    $$('.b-scrollable').makeScrollable();
    
    // Enhance HTML5 placeholder attribute for non-supported browsers.
    $$('input[placeholder]').enhanceWithHtml5();

    // Global click observer
    document.addEvent('click', function(e){
      var el = $(e.target);
      var action = el.get('rel');
      
      if($defined(action)) e.preventDefault();
      
      // Close dropdown if any
      new Dropdown(document).close();

      switch (action) {
        case 'dropdown:open':
          new Dropdown(document).open(el);
        break;

        case 'dropdown:close':
          new Dropdown(document).close();
        break;

        case 'overlay:open':
          var id = new URI(el.href).parsed.fragment;
          var el = $(id);
          if(!$defined(el)) break;
          if(Browser.Engine.trident)
            el.setStyles({
              'display': 'block',
              'visibility': 'visible'
            });
          el.addClass('b-overlay-active');
        break;

        case 'overlay:close':
          var el = el.getParent('.b-overlay');
          if(Browser.Engine.webkit) el.setStyle('visibility', 'visible');
          if(Browser.Engine.trident)
            el.setStyles({
              'display': 'none',
              'visibility': 'visible'
            });
          
          el.removeClass('b-overlay-active');
        break;
        
        case 'target:blank':
          window.open(el.href);
        break;

        default:
          
        break;
      }
    });
    
    // Hide dropdowns on window resize
    var hideDropdowns = function(){ new Dropdown(document).close(); };
    var hideDropdownsDebounced = hideDropdowns.debounce(25);
    window.addEvent('resize', hideDropdownsDebounced);
    
    // Dropdown shold be hidden if element is scrolled
    $$('.b-scrollable').addEvents({
      'scroll': hideDropdowns,
      'scroller:scrolled': hideDropdowns
    });
  });
})();

/**
 * class StringBuffer < Native
 * 
 * Implement super fast string concatenations, which will be used to generate HTML. Also supports chainability.
 * 
 * Usage:
 *  
 *  var str = new StringBuffer();
 *  str.append('Hello ');
 *  str.append('World!');
 *  
 *  str.toString(); -> "Hello World!"
 *  
 **/
function StringBuffer() { 
  this.buffer = []; 
} 
/**
 * StringBuffer#append(string) -> StringBuffer
 * 
 * Append a given string to the StringBuffer object. Return StringBuffer object.
 * 
 **/
StringBuffer.prototype.append = function append(string) { 
  this.buffer.push(string); 
  return this; 
}; 

/**
 * StringBuffer#toString() -> String
 * 
 * Converts StringBuffer object into real String.
 * 
 **/
StringBuffer.prototype.toString = function toString() { 
  return this.buffer.join(""); 
};
