/**
 *
 * Application core javascript
 *
 * Based on javascript Drupal code
 *
 * @author Mishal.cz <mishal at mishal dot cz>
 * @author Drupal team http://www.drupal.org
 * @version SVN:$Id$
 */

var Application = Application || {'behaviors': {}};

/**
 * Set the variable that indicates if JavaScript behaviors should be applied
 */
Application.jsEnabled = document.getElementsByTagName && document.createElement && document.createTextNode && document.documentElement && document.getElementById;

/**
 * Attach all registered behaviors to a page element.
 *
 * Behaviors are event-triggered actions that attach to page elements, enhancing
 * default non-Javascript UIs. Behaviors are registered in the Application.behaviors
 * object as follows:
 * @code
 *    Application.behaviors.behaviorName = function () {
 *      ...
 *    };
 * @endcode
 *
 * Application.attachBehaviors is added below to the jQuery ready event and so
 * runs on initial page load. Developers implementing AHAH/AJAX in their
 * solutions should also call this function after new page content has been
 * loaded, feeding in an element to be processed, in order to attach all
 * behaviors to the new content.
 *
 * Behaviors should use a class in the form behaviorName-processed to ensure
 * the behavior is attached only once to a given element. (Doing so enables
 * the reprocessing of given elements, which may be needed on occasion despite
 * the ability to limit behavior attachment to a particular element.)
 *
 * @param context
 *   An element to attach behaviors to. If none is given, the document element
 *   is used.
 */
Application.attachBehaviors = function(context) {
  context = context || document;
  if (Application.jsEnabled) {
    // Execute all of them.
    jQuery.each(Application.behaviors, function() {
      this(context);
    });
  }
};

/**
 * Get window width minus scrollbars width
 *
 */
Application.getWindowWidth = function() {

  var width = $(window).width();  
  // firefox substracts scrollbar automatically
  // var is_chrome = /chrome/.test( navigator.userAgent.toLowerCase());

  if(!$.browser.mozilla/* && !is_chrome*/)
  {
    width -= Application.getScrollbarWidth();
  }
  return width;
};

/**
 * Get window height minus scrollbars height
 *
 */
Application.getWindowHeight = function() {
  var height = $(window).height();
  if(!$.browser.mozilla)
  {
    height -= Application.getScrollbarWidth();
  }
  return height;
};

Application.getHorizontalScrollbarHeight = function()
{
  var elementHeight = $(document).height();
  var scrollPosition = $(document).height() + $(document).scrollTop();
  return (elementHeight == scrollPosition);
}

var scrollbarWidth = 0;

/**
 * Returns scrollbar width
 *
 * @copyright (c) 2008 Brandon Aaron (brandon.aaron@gmail.com)
 * @see http://brandonaaron.net)
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 */
Application.getScrollbarWidth = function() {
  if(!scrollbarWidth ) {
			if ( $.browser.msie ) {
				var $textarea1 = $('<textarea cols="10" rows="2"></textarea>')
						.css({position: 'absolute', top: -1000, left: -1000}).appendTo('body'),
					$textarea2 = $('<textarea cols="10" rows="2" style="overflow: hidden;"></textarea>')
						.css({position: 'absolute', top: -1000, left: -1000}).appendTo('body');
				scrollbarWidth = $textarea1.width() - $textarea2.width();
				$textarea1.add($textarea2).remove();
			} else {
				var $div = $('<div />')
					.css({width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000})
					.prependTo('body').append('<div />').find('div')
						.css({width: '100%', height: 200});
				scrollbarWidth = 100 - $div.width();
				$div.parent().remove();
			}
		}
		return scrollbarWidth;
};

/**
 * Encode special characters in a plain-text string for display as HTML.
 */
Application.checkPlain = function(str) {
  str = String(str);
  var replace = {'&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;'};
  for (var character in replace) {
    var regex = new RegExp(character, 'g');
    str = str.replace(regex, replace[character]);
  }
  return str;
};

/**
 * Freeze the current body height (as minimum height). Used to prevent
 * unnecessary upwards scrolling when doing DOM manipulations.
 */
Application.freezeHeight = function () {
  Application.unfreezeHeight();
  var div = document.createElement('div');
  $(div).css({
    position: 'absolute',
    top: '0px',
    left: '0px',
    width: '1px',
    height: $('body').css('height')
  }).attr('id', 'freeze-height');
  $('body').append(div);
};

/**
 * Unfreeze the body height
 */
Application.unfreezeHeight = function () {
  $('#freeze-height').remove();
};

/**
 * Get the text selection in a textarea.
 */
Application.getSelection = function (element) {
  if (typeof(element.selectionStart) != 'number' && document.selection) {
    // The current selection
    var range1 = document.selection.createRange();
    var range2 = range1.duplicate();
    // Select all text.
    range2.moveToElementText(element);
    // Now move 'dummy' end point to end point of original range.
    range2.setEndPoint('EndToEnd', range1);
    // Now we can calculate start and end points.
    var start = range2.text.length - range1.text.length;
    var end = start + range1.text.length;
    return {'start': start, 'end': end};
  }
  return {'start': element.selectionStart, 'end': element.selectionEnd};
};

/**
 * Configuration utility object
 *
 * Retrieves configuration properties
 * defined in Configuration array
 *
 */
var Config = function() {

  var configuration = {
    debug: false,
    log: false
  };
  
  return {
  
    /**
     * Adds configuration settings
     *
     * @param {Object} data
     */
    add: function(data) {
      jQuery.extend(configuration, data);
    },
    
    /**
     * Set configuration setting
     *
     * @param {Object} name
     * @param {Object} value
     */
    set: function(name, value) {
      configuration[name] = value;
    },
    
    /**
     * Retrieve configuration setting
     *
     * @param {Object} name
     * @param {Object} default_value
     */
    get: function(name, default_value) {
      if (configuration && configuration[name]) 
      {
        return configuration[name];
      }
      return default_value;
    }
  }
  
}();

/**
 * Logger
 *
 */
var Logger = function() {

  var isEnabled = function() {
    if (Config.get('debug')) 
    {
      return true;
    }
    return false;
  };
  
  var hasConsole = function() {
    // do we have console available?
    if (typeof console != 'undefined') 
    {
      return true;
    }
    return false;
  };
  
  var isLogToServerEnabled = function() {
    if (Config.get('log') && Config.get('log_url')) 
    {
      return true;
    }
    return false;
  };
  
  var logToServer = function(msg) {
    var url = Config.get('log_url');
    if (!url) 
    {
      return;
    }
    // make post request
    $.post(url, {
      msg: msg,
      url: window.location.href
    }, logToServerCallback, 'json');
  };
  
  var logToServerCallback = function(response) {
    // what to do ?
    // window.location = '/error.html';  
  };
  
  return {
    /**
     * Returns icon if type found, otherwise returns default icon
     *
     * @param {String} type
     */
    info: function(msg) {
      if(isEnabled() && hasConsole()) 
      {
        console.log(msg);
      }
    },
    
    error: function(msg) {
      if(isEnabled() && hasConsole()) 
      {
        console.error(msg);
      }
      if (isLogToServerEnabled()) 
      {
        logToServer(msg);
      }
    },
    
    varDump: function(obj) {
      if(typeof obj == "object") 
      {
        return "Type: " + typeof(obj) + ((obj.constructor) ? "\nConstructor: " + obj.constructor : "") + "\nValue: " + obj;
      } else 
      {
        return "Type: " + typeof(obj) + "\nValue: " + obj;
      }
    }
    
  }
}();


/**
 * Tools
 *
 * various utility functions
 *
 */
var Tools = function() {

  return {
  
    /**
     * Build url (directory based)
     *
     * @param {Object} params
     * @param {String} argument separator (default is '/')
     * @param {String} join string (default is '/')
     */
    buildQuery: function(params, separator, join) {

      Logger.info(Logger.varDump(params));
      
      if (separator == undefined)
      {
        separator = '/';
      }
      if(join == undefined)
      {
        join = '/';
      }
      var i = 0, tmp_arr = [];
      jQuery.each(params, function(key, val) {
        var key = encodeURIComponent(key);
        var val = encodeURIComponent(val.toString());
        tmp_arr[i++] = key + join + val;
      });
      return tmp_arr.join(separator);
    },

    // http://stackoverflow.com/questions/619240
    encodeHashComponent: function(x) {
      return encodeURIComponent(x).split('%').join('+');
    },

    decodeHashComponent: function (x) {
      return decodeURIComponent(x.split('+').join('%'));
    },

    getHashParameters : function(separator) {
      if(!separator)
      {
        separator = '&';
      }
      var parts = location.hash.substring(1).split(separator);
      var pars= {};
      for(var i= parts.length; i-->0;) {
        var kv = parts[i].split('=');
        var k = kv[0];
        if(k == '') continue;
        var v = kv.slice(1).join('=');
        pars[Tools.decodeHashComponent(k)]= Tools.decodeHashComponent(v);
      }
      return pars;
    }
  }  
}();


/**
 * I18n classs
 *
 * Handles string translations
 *
 */
var I18n = function() {

  var version = '1.1';

  /**
   * Translations holder
   */
  var I18nTranslations = [];

  var replaceArgs = function(str, from, to) {
    // replace all occurencies of 'from' by 'to'
    // g: replace all
    // possible parammeters 
    //   i: ignorecase
    return str.replace(from, to, 'g');
  };

  return {
  
    /**
     * Returns translation, if translation dictionary
     * exists and has a translation.
     */
    translate: function(str, params) {
      
      if(I18nTranslations && I18nTranslations[str])
      {
        var str = I18nTranslations[str];
      }
      
      if(params)
      {
        for(param in params)
        {
          var value = params[param];
          str = replaceArgs(str, param, value);
        }
      }
      return str;
    },

    /**
     * Sets translation
     * 
     */
    setTranslation: function(translations)
    {      
      I18nTranslations = translations;
    },

    addTranslation: function(translations)
    {      
      jQuery.extend(I18nTranslations, translations);      
    }

  };
}();

/**
 * User notifier
 * 
 */
var Notifier = function() {

  return {
    
    error: function(msg) 
    {
      alert(msg);
    },
    
    notify: function(msg)
    {
      alert(msg);
    },

    confirm: function(msg, params)
    {
      return confirm(msg);
    }
    
  };
  
}();

/**
 * Cookie
 *
 */
var Cookie = function() {
  
  var version = '1.1';
  
  return {

    /**
     * Sets cookie
     *
     */
    set: function(name, value, daysToExpire, path, domain, secure) {
      var expire = '';

      if (daysToExpire != undefined) {       
        var d = new Date();
        d.setTime(d.getTime() + (86400000 * parseInt(daysToExpire)));
        expire = '; expires=' + d.toGMTString();
      }
      
      if(path == undefined)
      {
        path = '/';
      }

      if(domain == undefined)
      {
        domain = Config.get('cookie_domain');
      }

      return (document.cookie = escape(name) + '=' + escape(value || '') + expire +
      ((path) ? "; path=" + path : "") +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : ""));
    },
  
    get: function(name)
    {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
      }
      return null;
    },

    erase: function(name, path, domain, secure) {
      try {
        var date = new Date();
        date.setTime(date.getTime() - (3600 * 1000));
        var expire = '; expires=' + date.toGMTString();
        document.cookie = escape(name) + '=' + expire +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
      } catch (e) {
        return false;
      }
      return true;
    },

    remove: function(name, path, domain, secure)
    {
      if(path == undefined)
      {
        path = '/';
      }
      if(domain == undefined)
      {
        domain = Config.get('cookie_domain');
      }
      return Cookie.erase(name, path, domain, secure);
    },

    accept: function() {
      if (typeof navigator.cookieEnabled == 'boolean') {
        return navigator.cookieEnabled;
      }
      Cookie.set('_test', '1');
      return (Cookie.erase('_test') === true);
    },

    /**
     * Return cookie utility version
     */
    getVersion: function()
    {
      return version;
    }
    
  };
  
}();

/**
 * Translation function
 *
 * @param {String} str
 * @param {Array} params
 */
function __(str, params)
{
  return I18n.translate(str, params);
};

// Global Killswitch on the <html> element
if(Application.jsEnabled) {

  // Global Killswitch on the <html> element
  $(document.documentElement).addClass('js');
  
  // Attach all behaviors.
  $(document).ready(function() {

    var domain = Config.get('cookie_domain');
    var path   = Config.get('cookie_path');

    // set cookie
    Cookie.set('has_js', 1, 365, path, domain);

    // attach behaviours
    Application.attachBehaviors(this);
    
  });
  
};

