/**
 * @uses JQuery 3.1+, JQuery.UI
 */

/**
 * Extiende el objeto Function
 * Delega un objeto a un metodo
 * @param {Object} instance
 * @param {Function} method
 * @return {Function}
 */
Function.delegate = function $delegate(instance, method) {
	return function() {
		return method.apply(instance, arguments);
	}
}

/**
 * Extiende el objeto String
 * Reemplaza todas las ocurrencias de una cadena dentro de la original, por otra cadena
 * @param {String} that
 * @param {String} with_this
 * @return {String}
 */
String.prototype.replaceAll = function(that, with_this){
	var retorno = this;
	while(retorno.indexOf(that) != -1){
		retorno = retorno.replace(that, with_this);
	}
	return retorno;
}

/**
 * Extiende el objeto String
 * Reemplaza todas las ocurrencias de "#{nombre_variable}" basandose en las propiedades del objeto pasado por parametro
 * @param {Object} o
 * @return {String}
 * @example
 * var tpl = '<div id="#{id}">#{content}</div>';
 * var obj = {id:'peteco', content:'<span>Peteco</span>'};
 * var result = tpl.evalTemplate(obj);
 * alert(result);
 * //Output: '<div id="peteco"><span>Peteco</span></div>';
 */
String.prototype.evalTemplate = function(o){
	return this.replace(/#{([^{}]*)}/g,
		function (a, b) {
			var r = o[b];
			return typeof r === 'string' || typeof r === 'number' ? r : a;
		}
	);
}

/**
 * Extiende el objeto String
 * Convierte una cadena de texto en un Elemento
 * La cadena debe ser un template html ("<div>peteco</div>")
 * @return {Element}
 */
String.prototype.toElement = function(){
	var div = document.createElement("div");
	div.innerHTML = this;
	return div.childNodes[0];
}

/**
 * Extiende el objeto String
 * Convierte una cadena de texto en un Object (json decode)
 * La cadena debe tener el formato correcto ("{'uno':'1', 'dos':'2'}")
 * @return {Element}
 */
String.prototype.toObject = function(){
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var j;
    var text = this;
    cx.lastIndex = 0;
    if (cx.test(text)) {
        text = text.replace(cx, function (a) {
            return '\\u' +
            ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        });
    }
    if (/^[\],:{}\s]*$/.
        test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
            replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
            replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
        j = eval('(' + text + ')');
        return j;
    }
}

/**
 * Extiende el objeto String
 * Usado para trabajar con query strings como "hola.php?uno=1&dos=2"
 * Agrega el parametro con su valor a la query string
 * @param {String} param
 * @param {String} value
 * @return {String}
 */
String.prototype.addParam = function (param, value){
	return this + (this.indexOf("?") == -1 ? "?" : "&") + param + "=" + (value + "").replaceAll(" ", "+"); 
}

/**
 * Extiende el objeto String
 * Usado para trabajar con query strings como "hola.php?uno=1&dos=2"
 * Agrega varios parametros a una query string
 * @param {Object} params
 * @return {String}
 */
String.prototype.addParams = function (params){
	return (Object.empty(params)) ? (this + "") : (this + (this.indexOf("?") == -1 ? "?" : "&") + Object.toQuery(params));
}

/**
 * Extiende el objeto Array
 * Remueve la posicion y su valor en el array
 * La posicion pasada por parametro no debe sobrepasar el rango
 * @param {Number} index
 * @return {Array}
 */
Array.prototype.removeAt = function(index) {
	for(var i = index; i < this.length - 1; i ++){
		this[i] = this[i + 1];
	}
	this.length --;
	return this;
};

/**
 * Extiende el objeto Array
 * Devuelve true si el valor pasado por parametro se encuentar en el array dado
 * @param {Mixed} value
 * @return {Boolean}
 */
Array.prototype.has = function(value){
	return this.indexOf(value) != -1;
};

/**
 * Extiende el objeto Array
 * Devuelve la posicion del valor pasado por parametro en el array.
 * Devuelve -1 si el valor no se encuentra en el array
 * @param {Mixed} value
 * @return {Number}
 */
Array.prototype.indexOf = function(value){
	var i = 0;
	var has = false;
	while(!has && i < this.length){
		if(this[i] === value)
			has = true;
		else
			i ++;
	}
	if(has)
		return i;
	else
		return -1;
};

/**
 * Verifica si el objeto pasado esta vacio.
 * Si el segundo parametro es true, se le hace un trim al objeto (Solo sirve para strings)
 * @param {Mixed} o
 * @param {Boolean} [strong]
 * @return {Boolean}
 * @example
 * Object.empty(undefined); //true
 * Object.empty(null); //true
 * Object.empty(""); //true
 * Object.empty(" ") //false
 * Object.empty(" ", true) //true
 * Object.empty("p"); //false
 * Object.empty([]); //true
 * Object.empty(["1"]); //false
 * Object.empty({}); //true
 * Object.empty({uno:1}); //false
 * var element = document.createElementById("div"); //(solo contenedores)
 * Object.empty(element); //true
 * element.innerHTML = "peteco";
 * Object.empty(element); //false
 */
Object.empty = function $empty(o, strong){  
	if(o == null || o == undefined) return true;
	if(typeof(o) == "string" && strong)
		o = o.trim();
	if (!o || o === "0"){
        return true;
    }
	if(o.length){
		return o.length == 0;
	}
    if (typeof(o) == "object") {
        for (var key in o) {
            return false;
        }
        return true;
    }
	if(o.nodeType == 1){
		if(o.nodeName != "INPUT" && o.nodeName != "IMG" && o.nodeName != "SELECT" && o.nodeName != "TEXTAREA")
			return Object.empty(new String(o.innerHTML).trim());
		else
			return false;
	}
    return false;
}

/**
 * Devuelve el nombre de propiedad de la primera concurrencia del valor pasado por parametro en el array.
 * Devuelve -1 si el valor no se encuentra en el objeto
 * @param {Mixed} value
 * @return {Number}
 */
Object.indexOf = function $index_of(value, o){
	var result = -1;
	Object.eachProp(o, function(p, v){
		if(v == value){
			result = p;
			return;
		}
	});
	return result;
}

/**
 * Devuelve true si el valor pasado por parametro se encuentar en el object dado
 * @param {Mixed} value
 * @return {Boolean}
 */
Object.has = function $has(value, o){
	return Object.indexOf(value, o) != -1;
}

/**
 * Itera sobre las propiedades de un objeto
 * @param {Object} obj
 * @param {Function} func
 */
Object.eachProp = function $each_prop(obj, func){
	for(var prop in obj){
		func.apply(obj, [prop, obj[prop]]);
	}	
}

/**
 * Itera sobre las propiedades de un objeto.
 * Delega un objeto a la funcion que se llama al iterar
 * @param {Object} obj
 * @param {Function} func
 */
Object.eachDelegate = function $each_delegate(obj, instance, func){
	BaseUtil.eachProp(obj, Function.delegate(instance, func));	
}

/**
 * Convierte el objeto pasado en una query string sin el ?
 * @param {Object} obj
 * @return {String}
 */
Object.toQuery = function(obj){
	var result = [];
	Object.eachProp(obj, function $to_query(prop, value){
		result[result.length] = prop + "=" + value;
	});
	return result.join("&");
}

/**
 * json Encode
 * @param {Mixed} mixed_val
 */
Object.jsonEncode = function $json_encode(mixed_val) {    
	var value = mixed_val;
	var quote = function (string) {
		var escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
		var meta = {    // table of character substitutions
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'"' : '\\"',
			'\\': '\\\\'
		};
		escapable.lastIndex = 0;
		return escapable.test(string) ?
		'"' + string.replace(escapable, function (a) {
			var c = meta[a];
			return typeof c === 'string' ? c :
			'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
		}) + '"' :
		'"' + string + '"';
	};
	var str = function(key, holder) {
		var gap = '';
		var indent = '    ';
		var i = 0;          // The loop counter.
		var k = '';          // The member key.
		var v = '';          // The member value.
		var length = 0;
		var mind = gap;
		var partial = [];
		var value = holder[key];
		if (value && typeof value === 'object' &&
			typeof value.toJSON === 'function') {
			value = value.toJSON(key);
		}
		switch (typeof value) {
			case 'string':
				return quote(value);
			case 'number':
				return isFinite(value) ? String(value) : 'null';
			case 'boolean':
			case 'null':
				return String(value);
			case 'object':
				if (!value) {
					return 'null';
				}
				gap += indent;
				partial = [];
				if (Object.prototype.toString.apply(value) === '[object Array]') {
					length = value.length;
					for (i = 0; i < length; i += 1) {
						partial[i] = str(i, value) || 'null';
					}
					v = partial.length === 0 ? '[]' :
					gap ? '[\n' + gap +
					partial.join(',\n' + gap) + '\n' +
					mind + ']' :
					'[' + partial.join(',') + ']';
					gap = mind;
					return v;
				}
				for (k in value) {
					if (Object.hasOwnProperty.call(value, k)) {
						v = str(k, value);
						if (v) {
							partial.push(quote(k) + (gap ? ': ' : ':') + v);
						}
					}
				}
				v = partial.length === 0 ? '{}' :
				gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
				mind + '}' : '{' + partial.join(',') + '}';
				gap = mind;
				return v;
		}
	};
	return str('', {
		'': value
	});
}
jType = {
	registerNamespace : function(name){
		var rootObject = window;
		var namespaceParts = name.split('.');
		for (var i = 0; i < namespaceParts.length; i++) {
			if(typeof(rootObject[namespaceParts[i]]) == "undefined"){
				rootObject[namespaceParts[i]] = {};
			}
			rootObject = rootObject[namespaceParts[i]];
		}
	},
	registerClass : function(clas, extend, implements){
		if(typeof(extend) != "undefined"){
			clas.prototype.Super = extend;
			for(var constant in extend){
				if(constant != "prototype"){
					if(typeof(extend[constant]) == "object"){
						if(typeof(clas[constant]) == "undefined")
							clas[constant] = extend[constant];
						for(var subprop in extend[constant]){
							clas[constant][subprop] = extend[constant][subprop];
						}
					}else{
						clas[constant] = extend[constant];
					}
				}
			}
			for(var method in extend.prototype){
				clas.prototype[method] = extend.prototype[method];
			}
		}
	}
}
jType.registerNamespace("MdLightbox");

MdLightbox.Base = function(){
}
MdLightbox.Base.Events = {
	PROPERTY_CHANGED : "property_changed"
};
MdLightbox.Base.prototype = {
	setOptions : function(options){
		options = options || {};
		for (prop in options){
			this["_" + prop] = options[prop];
		}
	},
	set : function (prop, value){
		if(this["_" + prop] != value){
			$(this).trigger(MdLightbox.Base.Events.PROPERTY_CHANGED, [prop, this["_" + prop], value]);
			this["_" + prop] = value;
		}
	},
	get : function (prop){
		return this["_" + prop];
	},
	addOnPropertyChanged : function (handler){
		$(this).bind(MdLightbox.Base.Events.PROPERTY_CHANGED, handler);
	}	
}
jType.registerClass(MdLightbox.Base);

MdLightbox.ScriptLoader = function(){
	this._scriptCollection = [];
}
MdLightbox.ScriptLoader.instance = null;
MdLightbox.ScriptLoader.getInstance = function (){
	if(MdLightbox.ScriptLoader.instance == null)
		MdLightbox.ScriptLoader.instance = new MdLightbox.ScriptLoader();
	return MdLightbox.ScriptLoader.instance;
}
MdLightbox.ScriptLoader.prototype = {
	_initialize : function(){
	},
	addScript : function(script){
		var i = this._scriptCollection.length;
		script.set("index", i);
		this._scriptCollection[i] = script;
		return i;
	},
	loadScript: function(index, callback){
		this.getScript(index).load(callback);
	},
	getScript: function(index){
		return this._scriptCollection[index];
	},
	getScriptById: function(id){
		var i = 0;
		var found = false;
		while(!found && i < this._scriptCollection.length){
			if(id == this._scriptCollection[i].get("id"))
				found = true;
			else
				i++;
		}
		if(found)
			return this._scriptCollection[i];
		else
			return  false;
	},
	removeScript: function(script){
		script.remove();
		this._scriptCollection.removeAt(script.get("index"));
	},
	notifyScriptLoaded : function(mixed){
		this.getScript(mixed).setCompleted();
	},
	_destruct : function(){
	}
}
jType.registerClass(MdLightbox.ScriptLoader, MdLightbox.Base);

MdLightbox.Script = function(url, options){
	this._url = url;
	this._id = "";
	this._index = 0;
	this._paramName = MdLightbox.Script.Params.INDEX;
	this._elmScript;
	this._status = "";
	this.setOptions(options);
	this._initialize();
}
MdLightbox.Script.Status = {
	IDLE : "idle",
	RUNNING : "running",
	COMPLETE : "complete"
}
MdLightbox.Script.Events = {
	INIT : "init",
	COMPLETED : "completed",
	REMOVED : "removed"
}
MdLightbox.Script.Params = {
	INDEX : "scripti"
}
MdLightbox.Script.prototype = {
	_initialize : function(){
		this._elmScript = document.createElement("script");
		this._elmScript.id = this._id;
		this.set("status", MdLightbox.Script.Status.IDLE);
	},
	_getUrl : function(){
		return this._url.addParam(this._paramName, this._index);
	},
	load : function(callback){
		$(this).trigger(MdLightbox.Script.Events.INIT, [this]);
		this.set("status", MdLightbox.Script.Status.RUNNING);
		this._elmScript.src = this._getUrl();
		document.getElementsByTagName("head")[0].appendChild(this._elmScript);
	},
	setCompleted : function(){
		this.set("status", MdLightbox.Script.Status.COMPLETE);
		$(this).trigger(MdLightbox.Script.Events.COMPLETED, [this]);
	},
	addOnInit : function(handler){
		$(this).bind(MdLightbox.Script.Events.INIT, handler);
	},
	addOnCompleted : function(handler){
		$(this).bind(MdLightbox.Script.Events.COMPLETED, handler);
	},
	addOnRemoved : function(handler){
		$(this).bind(MdLightbox.Script.Events.REMOVED, handler);
	},
	remove : function(){
		$(this._elmScript).remove();
		$(this).trigger(MdLightbox.Script.Events.REMOVED);
	},
	_destruct : function(){
	}
}
jType.registerClass(MdLightbox.Script, MdLightbox.Base);

MdLightbox.Scroll = function(options){
	this._checkpoint = null;
	this._ready = true;
	this.setOptions(options);
	this._initialize();
}
MdLightbox.Scroll.Events = {
	CHECKPOINT : "checkpoint"
}
MdLightbox.Scroll.instance = null;
MdLightbox.Scroll.getInstance = function (){
	if(MdLightbox.Scroll.instance == null)
		MdLightbox.Scroll.instance = new MdLightbox.Scroll();
	return MdLightbox.Scroll.instance;
}
MdLightbox.Scroll.prototype = {
	_initialize : function(){
		$(window).bind("scroll", Function.delegate(this, this._checkScrollPos));
	},
	_checkScrollPos : function(event){
		if(this._checkpoint != null){
			//console.log($(window).scrollTop() + " - " + $(document).height() - $(window).height() - this._checkpoint);
			if($(window).scrollTop() >= $(document).height() - $(window).height() - this._checkpoint){
				if(this._ready){
					$(this).trigger(MdLightbox.Scroll.Events.CHECKPOINT);
					this._ready = false;
				}
			}
		}
	},
	setCheckpoint : function(checkpoint){
		this._checkpoint = checkpoint;
	},
	setReady : function(ready){
		this._ready = ready;
	},
	addOnCheckpoint : function(handler){
		$(this).bind(MdLightbox.Scroll.Events.CHECKPOINT, handler);
	},
	removeOnCheckpoint : function(handler){
		$(this).unbind(MdLightbox.Scroll.Events.CHECKPOINT, handler);
	},
	_destruct : function(){
	}
}
jType.registerClass(MdLightbox.Scroll, MdLightbox.Base);

MdLightbox.Util = {
	getTotalWidth : function $util_get_total_width(e){
		return e.outerWidth() + (isNaN(parseInt(e.css("margin-left"))) ? 0 : parseInt(e.css("margin-left"))) + (isNaN(parseInt(e.css("margin-right"))) ? 0 : parseInt(e.css("margin-right")));
	},
	getTotalHeight : function $util_get_total_height(e){
		return e.outerHeight() + (isNaN(parseInt(e.css("margin-top"))) ? 0 : parseInt(e.css("margin-top"))) + (isNaN(parseInt(e.css("margin-bottom"))) ? 0 : parseInt(e.css("margin-bottom")));
	},
	getOuterHTML : function(element){
		var div = $(document.createElement("div"));
		div.append(element.clone());
		return div.html();
	}
}


