/** 
 *  Zenexity client-side tools collection file
 *  
 *  @author Victor Bartel (vba [a] zenexity.fr)
 */
(function() {
  
  if(!window.zenexity) window.zenexity = {tools : {}};
  else window.zenexity.tools = {};
  
  var zt = zenexity.tools;
  
  zt.cache = {};

  zt.getSelectedSortValue = function() {
    var selectedValue = "";
    $("input[name=sort]").each(function() {
      if($(this).attr("checked")) {
        selectedValue = $(this).attr("value");
      }
    });
    return selectedValue;
  };

  zt.hasParentClass = function(parents, clazz) {
    for(var i=0; i<parents.length; i++) {
      if($(parents[i]).hasClass(clazz)) {
        return true;
      }
    }
    return false;
  };

  zt.getParent = function(parents, clazz) {
    for(var i=0; i<parents.length; i++) {
      if($(parents[i]).hasClass(clazz)) {
        return $(parents[i]);
      }
    }
    return false;
  };

  /**
   * @description Returns the namespace specified and creates it if it doesn't exist
   * @method namespace
   * @namespace sematic.tools
   * @param arguments {String*} 1-n namespaces to create
   * @return {Object} A reference to the last namespace object created
   *
   */
  zt.namespace = function() {
    var a = arguments, o = null, i, j, d;
    for (i = 0; i < a.length; i = i + 1) {
      d = a[i].split(".");
      o = window;
      for (j = 0; j < d.length; j = j + 1) {
        o[d[j]] = o[d[j]] || {};
        o = o[d[j]];
      }
    }
    return o;
  };

  /**
   * @description Display an alert window which show the object in params
   * @method alertObject
   * @namespace zenexity.tools
   * @param object to display
   *
   */
  zt.alertObject = function(object) {
    if(zt.guessType(object)!="String") {
      var str = "";
      var key;
      for(key in object) {
        str += key + "=" + object[key] + "\n";
      }
      alert(str);
    }
    else {
      alert(object);
    }
  };

  /**
   * Tries to guess object type.
   * @param o {Object} target to get type
   * @return {String|Undefined} return object type name or undefined
   */
  zt.guessType = function(o) {
    if (typeof o == 'undefined') return undefined;
    else return Object.prototype.toString.call(o).replace(/(\[|\]|object|\s+)/g, "");
  }

  /**
   * @description Capitalizes entered world/sentence
   * @param s {String} world to be capitalized
   * @returns {String} Capitalized string
   * 
   */
  zt.capitalize = function(s){
    return [s.charAt(0).toUpperCase(), s.substring(1)].join('');
  };
  
  /**
   * @description Escapes html symbols in passed string
   * @param str {String} string to be escaped
   * @return {String} escaped string
   */
  zt.escapeHTML = function(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  };
  
  /**
   * @description Unescapes html symbols in passed string
   * @param str {String} string to be unescaped
   * @return {String} unescaped string
   */
  zt.unescapeHTML = function(str) {
    return str.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  };
  
  /**
   * @description Invokes a function from indicated namespace and returns its result
   * @namespace sematic.tools
   * @param namespace {String} method name & namespace
   * @param context {Object} invocation context 
   * @param args {Array} invocation arguments
   * @return {Object|Undefined} function invocation result
   */
  zt.invoke = function(namespace,context,args) {
    var result, l = namespace.split('.');
    
    if( l.length == 1 && $.isFunction(window[namespace]) ) {
      result = window[namespace].apply(context,args);
    } else {
      var scope = window;
      $.each(l,function(i,el){
        if((i+1) == l.length && $.isFunction(scope[el])) {
          result = scope[el].apply(context,args);
        } else {
          scope = scope[el];
        }
      });
    }
    return result;
  };
  
  /**
   * @description Namespace objects getter
   * @param namespace {String} passed namespace
   * @param start {Object} start scope object
   * @return {Object} undefined if namespace doesn't exists and an object found in the namespace otherwise
   */
  zt.getNamespace = function(namespace,start) {
    var l = namespace.split('.'), scope = start || window;
    if( l.length == 1) return typeof scope[namespace] != 'undefined';
    
    for ( var i = 0, length = l.length; i < length; i++ ) {
      scope = scope[l[i]];
      if(typeof scope == 'undefined') break;
    }
    
    return scope;
  };
  
  /**
   * @description Returns reference function to passed namespace method
   * @namespace sematic.tools
   * @param namespace {String} method complete path
   * @param context {Object} execution context 
   * @param args {Array} execution arguments
   * @return {Object|Undefined} function calling result
   */
  zt.ref = function(namespace,context,args) {
    return function() {
      return zt.invoke(namespace,context, $.merge($.makeArray(arguments),args||[]));
    };
  };
  
  /**
   * @description Dynamically load remote script using url/namespace
   * @param desc {Object} Dependency describer
   * @param callback {Function} callback function to be called after script loading
   */
  zt.using = function(desc,callback) {
    var conf = {};
    
    conf.url = desc.url;
    conf.ref = desc.namespace;
    
    if(desc.object) conf.ref = desc.object;
    
    var obj = zt.getNamespace(conf.ref);
    
    if(desc.namespace && /^(?:\w+\.)*(?:\w+)$/gi.test(desc.namespace)) 
      conf.url = [desc.prefix,'/',desc.namespace.replace(/\./gi,'/'),'/',desc.suffix].join('');
    
    if(!obj)
      $.getScript(conf.url,function(){
        var object = zt.getNamespace(conf.ref);
        if(object && desc.init && $.isFunction(object.init)) 
          try {object.init.call(window);} catch(m){} 
        if($.isFunction(callback)) callback.call(window,object);
      });
    else
      if($.isFunction(callback)) callback.call(window,obj);
  };
  /**
   * @description Tries to call a function safely
   * @method tryToCall
   * @param f {Function*} function object to be called
   * @param c {Object*} context in which function is called
   * @param params {Array | Object*} parameters to be supplied to function call
   * @returns {Object | Undefined} function execution result if this one had place
   * 
   */
  zt.tryToCall = function(){
    var a = arguments;
    var f = a[0], c = a[1];
    
    if ($.isFunction(f)) return f.apply(c, $.makeArray(a).slice(2));
    else return false;
  };
  
  /**
   * @description Dispatch handled event to scope
   * @namespace sematic.tools
   * @param e {Event} Handled event(normalized by jQuery)
   *
   */
  zt.dispatchEvent = function(e) {
    var that = this;
    
    e.preventDefault();
    if($.nodeName(e.target,'img') && $.nodeName(e.target.parentNode,'a'))
      e.target = e.target.parentNode;
    
    var id = e.target.id;
    
    if(id && /\.+/.test(id.split('_')[0])) {
      var obj = {}, arr = id.match(/(?:[^\.]+\.)*[a-zA-Z]+/i)[0].split('.');
      obj.m = arr.splice(arr.length-1,1)[0];
      obj.o = zt.getNamespace(arr.join('.'),that);
      if($.isFunction(obj.o[obj.m])) obj.o[obj.m].call(obj.o,e);
    } 
    else if(id && that !== window) {
      var method = that[id.split("_")[0]];
      if($.isFunction(method)) method.call(that,e);
    }
    
    return false;
  };
  
  /**
   * @description Extends an object with properties of another, that match pattern
   * @namespace sematic.tools
   * @param target {Object} target object
   * @param source {Object} source object
   * @param pattern {String|RegExp} selection pattern
   */
  zt.extendObject = function(target, source, pattern) {
    var src = {};
    if(Object.prototype.toString.call(pattern) === '[object String]')
      pattern = new RegExp(pattern);
    
    $.each(source,function(k,v){
      if(pattern.test(k)) src[k] = v;
    });
    
    $.extend(target,src);
  };
  
  /**
   * @see http://ejohn.org/blog/javascript-micro-templating/
   * @description Simple JavaScript Templating (John Resig)
   * @param str {String|HTMLElement} template source / container element
   * @param data {Object} Template data
   */
  zt.tpl = function tpl (str, data){
    data.__empty = '';
    var fn = !/\W/.test(str) ?
      zt.cache[str] = zt.cache[str] || tpl(document.getElementById(str).innerHTML) :
      function() {
        str = str.replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "', obj[$.trim('$1')] || obj['__empty'],'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'");

        var body = zt.sb("var p=[],print=function(){p.push.apply(p,arguments);};")
          .append("with(obj){p.push('").append(str)
          .append("');}return p.join('');");
        
        return new Function("obj",body.toString());
      }();
    return data ? fn( data ) : fn;
  };

  zt.isArray = function(value) {
    return value &&
      typeof value === 'object' &&
      value.constructor === Array;
  }
  
  /**
   * @description Extended string buffer/helper
   * @namespace sematic.tools
   *
   * @author vba@zenexity.fr
   */
  zt.StrBuffer = zt.sb = function(string) {
    return new zt.StrBuffer.fn.init(string);
  };
  
  zt.StrBuffer.fn = zt.StrBuffer.prototype = {
    init : function(string) {
      this.setArray(string || "");
      return this;
    },
    /**
     * @description Appends new string part to current buffer
     * @param string {String} string to append
     * @return {StrBuffer} current buffer
     */
    append : function(string) {
      Array.prototype.push.call(this, string);
      return this;
    },
    /**
     * @description Appends new string part to current buffer and break it
     * @param string {String} string to append
     * @return {StrBuffer} current buffer
     */
    appendln : function(string) {
      Array.prototype.push.call(this, string);
      Array.prototype.push.call(this, '\n');
      return this;
    },
    /**
     * @description Converts current buffer to string
     * @return {String} buffer string representation
     */
    toString : function() {
      return Array.prototype.join.call(this, "");
    },
    get : function(index) {
      return this[index];
    },
    size : function() {
      return this.length;
    },
    setArray : function(el) {
      this.length = 0;
      Array.prototype.push.apply(this, [el]);

      return this;
    }
  };
  
  zt.StrBuffer.fn.init.prototype = zt.StrBuffer.fn;
    
}());
