/*******************
 * CORE JAVASCRIPT *
 *******************/

$ = jQuery;

/**
 * $.namespace:
 *
 * Creates a namespace for each argument to be used for scoping variables and
 * classes so that they are not global.
 *
 * Usage:
 *   $.namespace('Company', 'Company.data');
 *   Company.Widget = function() { ... }
 *   Company.data.CustomStore = function() { ... }
 */
$.namespace = function () {
	var a = arguments, o = null, i, j, d, rt, t;
	for (i = 0; i < a.length; i++) {
		d = a[i].split('.');
		r = d[0];
		t = eval('typeof ' + r);
		if (t == 'undefined') {
			eval(r + ' = {};');
		}
		o = eval(r);
		for (j = 1; j < d.length; j++) {
			o[d[j]] = o[d[j]] || {};
			o = o[d[j]];
		}
	}
};
$.namespace('console');
if (console.debug === undefined) { console.debug = function() {}; }

if (console.log === undefined) {
	console.log = function(){
	};
}
$.extend($.fn, {
	id : function () {
		this.each(function () {
			if ($(this).attr("id") === "" || $(this).attr("id") === undefined) $(this).attr("id", "jQ_" + $.data(this));
		});
		return $(this).attr("id")
	}
});

$.extend({
    doGet: function(url, params) {
        document.location = url + '?' + $.param(params);
    },
    doPost: function(url, params) {
        var $form = $("<form><\/form>")
			.attr('method', 'POST')
			.attr("action", url);
			
        $.each(params, function(name, value) {
            $("<input type='hidden'>")
                .attr("name", name)
                .attr("value", value)
                .appendTo($form);
        });		
        $form.appendTo("body");		
        $form.submit();
    }
});



/**
 * $.scope:
 *
 * Binds a function to the given scope.
 */
Function.prototype.scope = function (scope) {
	var that = this;
	return function () {
		return that.apply(scope, arguments);
	};
};

$.fnFalse = function(){return false;}
$.fnTrue = function(){return true;}

/**
 * return the value of the radio button that is checked
 * return an empty string if none are checked, or
 * there are no radio buttons
 */
$.getCheckedValue = function (radioObj) {
	if(!radioObj)
		return "";
	var radioLength = radioObj.length;
	if(radioLength == undefined)
		if(radioObj.checked)
			return radioObj.value;
		else
			return "";
	for(var i = 0; i < radioLength; i++) {
		if(radioObj[i].checked) {
			return radioObj[i].value;
		}
	}
	return "";
};

/**
 * set the radio button with the given value as being checked
 * do nothing if there are no radio buttons
 * if the given value does not exist, all the radio buttons
 * are reset to unchecked
 */
$.setCheckedValue = function(radioObj, newValue){
	if (!radioObj) {
		return;
	}
	var radioLength = radioObj.length;
	if(radioLength == undefined) {
		radioObj.checked = (radioObj.value == newValue.toString());
		return;
	}
	for(var i = 0; i < radioLength; i++) {
		radioObj[i].checked = false;
		if(radioObj[i].value == newValue.toString()) {
			radioObj[i].checked = true;
		}
	}
};

jQuery.autoBreakLines = function(s, l){
    x = [""]; 
    for (var i = 0; i < s.split(' ').length; i++){
        if (x[x.length-1].length > l) {
            x.push('');
        }
        x[x.length-1] = (x[x.length-1] + " " + s.split(' ')[i]).trim(); 
    } 
    return ' <BR> '.join(x);
}

jQuery.fn.scrollTo = function(parent)
{
    if (!parent) {
		$('html,body').animate({
			scrollTop:  this.offset().top
		}, 500);
	}else {
		console.log('%o', this.offsetTop);
		parent.animate({
			scrollTop: this.offsetTop
		}, 500);
	}
	return this;
};
jQuery.fn.autoscroll = function(duration) {
	if (duration === undefined){
		duration = 500;
	}
	jQuery('html,body').animate({
			scrollLeft: this.offset().left,
			scrollTop: this.offset().top
		}, duration
	);
	return this;
};
jQuery.fn.flash = function(obj, duration){
	if (duration === undefined) {
		duration = 500;
	}
	var current = {};
	for (attr in obj){
		current[attr] = this.css(attr);
	}
	return this.animate(obj, duration / 2 ).animate(current, duration / 2 );
};
/**
 * Replace a DOM element with another.
 *
 * See http://blog.brandonaaron.net/2007/06/01/jquery-snippets-replace/
 */
jQuery.fn.replace = function() {
	var stack = [];
	return this.domManip(arguments, true, 1, function(a){
		this.parentNode.replaceChild( a, this );
		stack.push(a);
	}).pushStack( stack );
};

$.extend({
	min: function(objs, func) {
		return Math.min.apply(Math, func === undefined ? objs : $.map(objs, func));
	},

	max: function(objs, func) {
		return Math.max.apply(Math, func === undefined ? objs : $.map(objs, func));
	}
});

/**
 * Generates a random alphanumeric string of the given length.
 */
function randomString(length) {
	var chars =	'0123456789'
		    	+ 'ABCDEFGHIJKLMNOPQRSTUVWXTZ'
		    	+ 'abcdefghiklmnopqrstuvwxyz';

	var random = '';
	for (var i = 0; i < length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		random += chars.substring(rnum, rnum + 1);
	}

	return random;
}

$.random = {string: randomString};

/**
 * Provides safe access to a variable.
 */
function get(obj,defaultValue) {
	try{
		if (obj === undefined) return defaultValue;
		else return obj;
	}catch(e){
		return defaultValue;
	}
}

Number.prototype.round = function( places, truncate )
{
    var factor = Math.pow( 10, places );
    return Math[Boolean(truncate)?'floor':'round']( this * factor ) / factor;
}

/**
 * Adds cls to el and removes cls from all siblings.
 *
 * @param {Object} el
 * @param {Object} cls
 */
function radioClass(el, cls) {
	$(el).addClass(cls).siblings().removeClass(cls);
};

$.fn.extend({
	radioClass: function(cls) {
		return this.addClass(cls).siblings().removeClass(cls).end();
	}
});


/**
 * We add "loading" to the document element's classes before the page is
 * loaded and remove it when the page is ready. This allows for elements to
 * be hidden when the page is loaded (to avoid FoPSC, or Flash of Partially
 * Styled Content), and also for a page to work as expected when Javascript is
 * disabled.
 */

$(function () {
	$('html').removeClass('loading');
});

$('html').addClass('loading');


String.prototype.unescapeHtml = function () {
    var el = $('<div style="display:none"></div>').appendTo($('body')).html(this.toString().replaceAll('&quot;', '"'));
	var result = el.text();
	el.remove();
	return result;
};

String.prototype.escapeHtml = function() {
	var el = $('<div style="display:none"></div>').appendTo($('body')).text(this.toString().replaceAll('"','&quot;'));
	var result = el[0].innerHTML;
	el.remove();
	return result;
};

String.prototype.join = function (a) {
	if (a.constructor.toString().indexOf("Array") != -1 && a.length > 0) {
		var out;
		out = a[0].toString();
		for (var i = 1; i < a.length; i++) {
			out = out + this + a[i];
		}
		return out;
	}
};

String.prototype.truncate = function (length, ellipses) {
	if (ellipses === undefined ) ellipses = true;
	if (ellipses == true){
		length = (length>5) ? length-3:length;
	}
	return this.substring(0, length) + ((ellipses && this.length > length) ? '...':'');
};
String.prototype.replaceAll = function (find, replacement) {
	var out = this;
	var last = '';
	do {
		last = out;
		out = last.replace(find, replacement);
	} while (out != last);
	return out;
};


Array.prototype.insert = function (i, v) {
	if (i >= 0) {
		var a = this.slice(), b = a.splice(i);
		a[i] = v;
		return a.concat(b);
	}
};

/**
 * by John Resig (MIT Licensed)
 * see http://ejohn.org/blog/javascript-array-remove/
 */
Array.prototype.remove = function(from, to){
	var rest = this.slice((to || from) + 1 || this.length);
	this.length = from < 0 ? this.length + from : from;
	return this.push.apply(this, rest);
};

Array.prototype.flatten = function(target){
	target = target || [];
	for (var i = 0; i < this.length; i++) {
		if (this[i].constructor.toString().indexOf("Array") == -1) {
			target.push(this[i]);
		} else{
			this[i].flatten(target);
		}
	}
	return target;
};

Array.prototype.each = function(f) {
	for (var i = 0; i < this.length; i++) {
		f.call(this[i], i);
	}
}

Array.prototype.map = function (func) {
	var mapped = [];
	for (var i = 0; i < this.length; i++) {
		var temp = func(this[i], i);
		if (temp !== undefined && temp !== null) {
			mapped.push(temp);
		}
	}
	return mapped;
};
Array.prototype.filter = function (func) {
	var reduced = [];
	for (var i = 0; i < this.length; i++) {
		var temp = func(this[i], i);
		if (temp === true) {
			reduced.push(this[i]);
		}
	}
	return reduced;
};

Array.prototype.grep = function (func, invert) {
	return jQuery.grep(this, func, invert);
};

/**
 * Return the first index position of a value
 * contained in an array, or -1 if it isn't contained.
 * @param {Object} the value to check OR a function(item, index)
 * @param {boolean} true if you are looking for a function in the array
 * @return {int} the index of the first occurence of val or first true result in function(item,index), or -1
 */
Array.prototype.indexOf = function(func, directCompare) {
   var value = func;
   if (typeof(func) != "function" || !!directCompare) func = function(a, b){ return (a===value);};
   var i = -1;
   while (i++ < this.length - 1) {
	  if (func(this[i], i))
	 return i;
   }
   return -1;
};

/**
 * return the last index position of a value
 * contained in an array, or -1 if it isn't contained.
 * @param {Object} the value to check OR a function(item, index)
 * @param {boolean} true if you are looking for a function in the array
 * @return {int} the index of the last occurence of val or first true result in function(item,index), or -1
 */
Array.prototype.lastIndexOf = function(func, directCompare) {
   var value = func;
   if (typeof(func) != "function" || !!directCompare) func = function(a, b){ return (a===value);};
   var i = 1;
   while (this.length - i++ >= 0) {
	  if (func(this[i], i))
	 return i;
   }
   return -1;
};

/**
 * check if the array passed contains the specified value (start from end of array)
 * @param {Object} the value to check OR a function(item, index)
 * @param {boolean} true if you are looking for a function in the array
 * @return {boolean} true if the value is contained
 */
Array.prototype.contains = function(func, directCompare) {
   if (this.indexOf(func, directCompare) > -1) return true;
   return false;
};

/**
 * Retrieve the union set of a bunch of arrays
 * @param {Array} array1,... the arrays to unify
 * @return {Array} the union set
 */
Array.union = function() {
	var result = [];
	var map = [];
	for (var i=0; i<arguments.length; i++) {
		for (var j=0; j < arguments[i].length; j++) {
			var item = arguments[i][j];
			if (!result.contains(item)) {
				result.push(item);
			}
		}
	}
	return result;
};

/**
 * Retrieve the intersection set of a bunch of arrays
 * @param {Array} array1,... the arrays to intersect
 * @param {Object} a function(item, index)
 * @return {Array} the intersection set
 */
Array.intersection = function() {
	var all = Array.union.apply(this, arguments);
	var result = [];
	for (var j = 0; j < all.length; j++) {
		var chksum = 0;
		var item = all[j];
		for (var i=0; i<arguments.length; i+=1) {
			if (arguments[i].contains(item)) chksum += 1;
			else break;
		}
		if (chksum == arguments.length)	result.push(item);
	}
	return result;
};

function cloneObject(obj){
	var clone = ((obj.constructor.toString().indexOf('Array') != -1) ? [] : {});
	for (property in obj) {
		if (obj[property] && typeof obj[property] == "object") {
			clone[property] = cloneObject(obj[property]);
		}
		else {
			clone[property] = obj[property];
		}
	}
	return clone;
}
