var Utils = {
    /**
     * Indica si el dato recibido esta vacio o no, OJO, si la variable no existe peta
     * @method isEmpty
     * @param  {any} data Dato a comprobar, los numeros son siempre true
     * @return {boolean}  True si esta vacio y false si no lo esta
     */
    is_empty: function(data) {
        var count = 0, i;

        if (typeof data === 'number') {
            return false;
        }

        if (typeof data === 'boolean') {
            return !data;
        }

        if (data === undefined || data === null) {
            return true;
        }

        if (data.length !== undefined) {
            return data.length === 0;
        }
        
        if (data === '') {
            return true;
        }

        for (i in data) {
            if (data.hasOwnProperty(i)) {
                count += 1;
            }
        }

        return count === 0;
    },
    /**
     * Redondea a dos decimales
     * @method round2Decimals
     * @param  {number} num Numero decimal
     * @return {number} Retorna numero tipo 0.00
     */
    round2Decimals: function(num = 0.00) {
        Math.round(num * 100) / 100
    },
    /**
     * Elimina el html de una cadena
     * @method  stripHtml
     * @param  {string} html Html a convertir
     * @return {string}      String sin html
     */
    stripHtml: function(html_string = '') {
        var tmp = document.createElement("div");
        tmp.innerHTML = html;
        return tmp.textContent || tmp.innerText || "";
    },
    /**
     * Emula un click en una posicion concreta de un elemento dado
     * @method simulateClickOnCustomPosition
     * @param  {String} [selector]  Selector CSS3
     * @param  {Object} [optionals] Offset & debug
     * @return {Boolean} True si ok y false if manejador del evento usa prevenDefault
     */
    simulateClickOnCustomPosition: function(selector = '', optionals = {}) {
        var element = document.querySelector(selector),
        offset = $(selector).offset(),
        positionX = offset.left + 0,
        positionY = offset.top + 0,
        eventClick;

        if (optionals.hasOwnProperty("x") && optionals.x > 0) {
            positionX += optionals.x;
        }

        if (optionals.hasOwnProperty("y") && optionals.y > 0) {
            positionX += optionals.y;
        }

        eventClick = new MouseEvent('click', {
            'view': window,
            "clientX": positionX,
            "clientY": positionY,
            'bubbles': false
        });

        if (optionals.hasOwnProperty(debug) && optionals.debug) {
            console.log("Left offset:" + positionX + ", Top offset: " + positionY);
        }

        return element.dispatchEvent(eventClick);
    },
    sameElementsHeight: function(selector) {
        try {
            var elements = document.querySelectorAll(selector),
            max = 0,
            i = 0;

            if (this.is_empty(elements)) {
                throw "No matched selector";
            }

            for (i = 0; i < elements.length; i++) {
                if (elements.hasOwnProperty(i)) {
                    if (elements[i].offsetHeight > max) {
                        max = elements[i].offsetHeight;
                    }
                }
            }

            for (i = 0; i < elements.length; i++) {
                if (elements.hasOwnProperty(i)) {
                    if (elements[i].offsetHeight < max) {
                        elements[i].style.height = max + "px";
                    }
                }
            }
        } catch (e) {
            window.console.error("Same Elements Height: " + e.message);
        }
    },
    setFocus: function(element) {
        focusAttribute = "data-focus";

        if (! Utils.is_empty(element) && element.hasAttribute(focusAttribute)) {
            document.querySelector(element.getAttribute(focusAttribute)).focus();
        }
    },
    /**
     * Implementacion del debounce de underscore v1.8.3
     * @method debounce
     * @param  {function} func      Funcion a ejecutar
     * @param  {number}   wait      Tiempo de espera entre el que no se ejecutara de nuevo la funcion
     * @param  {boolean}  immediate (Opcional) Ejecucion inmediata de func
     * @return {function}           Devuelve la funcion con el delay
     */
    debounce: function(func, wait, immediate) {
        var timeout, args, context, timestamp, result;

        var later = function() {
            var last = new Date().getTime() - timestamp;

            if (last < wait && last >= 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };

        return function() {
            context = this;
            args = arguments;
            timestamp = new Date().getTime();
            var callNow = immediate && !timeout;
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }

            return result;
        };
    }
};

// Modo de empleo
var foo = "";
window.console.log(Utils.is_empty(foo)); // true

foo = 0;
window.console.log(Utils.is_empty(foo)); // false
// Los numeros siempre los evalua como true

foo = false;
window.console.log(Utils.is_empty(foo)); // true

foo = {};
window.console.log(Utils.is_empty(foo)); // true

foo = [];
window.console.log(Utils.is_empty(foo)); // true

foo = {"foo": "", "bar": ""};
window.console.log(Utils.is_empty(foo)); // false
// Aunque el JSON no contenga valores, si contiene claves por
// lo tanto es un dato valido y no esta vacio. En dicho
// caso deberia recorrer el json elemento a elemento y comprobarlo