/* exported getParameterByName, updateQueryString, throttle, debounce, getHashParams, setStatus, addAtPosition, getValue, getValueJSON, setValueJSON, bindString, ellipsize, sizeOfObject, base64Encode */

//the following is absolutely necessary for IE compatibility, because IE <= 9 have no console object
window.console = window.console || (function()
{
    var c = {}; c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile = c.clear = c.exception = c.trace = c.assert = function(){};
    return c;
})();


function getParameterByName(name)
{
	name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
	var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
		results = regex.exec(window['location']['search']);
		
	return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

function updateQueryString(key, value, url)
{
    if (!url) url = window.location.href;
    var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
        hash;

    if (re.test(url)) {
        if (typeof value !== 'undefined' && value !== null)
            return url.replace(re, '$1' + key + "=" + value + '$2$3');
        else {
            hash = url.split('#');
            url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
            if (typeof hash[1] !== 'undefined' && hash[1] !== null) 
                url += '#' + hash[1];
            return url;
        }
    }
    else {
        if (typeof value !== 'undefined' && value !== null) {
            var separator = url.indexOf('?') !== -1 ? '&' : '?';
            hash = url.split('#');
            url = hash[0] + separator + key + '=' + value;
            if (typeof hash[1] !== 'undefined' && hash[1] !== null) 
                url += '#' + hash[1];
            return url;
        }
        else
            return url;
    }
}

function throttle(func, wait, options)
{
	var context, args, result;
	var timeout = null;
	var previous = 0;
	
	if(!options)
		options = {};
	
	var later = function()
	{
		previous = options['leading'] === false ? 0 : (new Date().getTime());
		timeout = null;
		result = func.apply(context, args);
		context = args = null;
	};

	return function()
	{
		var now = new Date().getTime();
		if (!previous && options['leading'] === false) previous = now;
		
		var remaining = wait - (now - previous);
		context = this;
		args = arguments;
		
		if (remaining <= 0)
		{
			clearTimeout(timeout);
			timeout = null;
			previous = now;
			result = func.apply(context, args);
			context = args = null;
		}
		else if (!timeout && options['trailing'] !== false)
		{
			timeout = setTimeout(later, remaining);
		}
		
		return result;
	};
}

/**
* Returns a function, that, as long as it continues to be invoked, will not
* be triggered. The function will be called after it stops being called for
* N milliseconds. If `immediate` is passed, trigger the function on the
* leading edge, instead of the trailing.
*
* @param {function} func
* @param {number} wait
* @param {boolean} immediate
*/
function debounce(func, wait, immediate)
{
	var timeout;
	return function() {
		var context = this, args = arguments;
		clearTimeout(timeout);
		timeout = setTimeout(function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		}, wait);
		if (immediate && !timeout) func.apply(context, args);
	};
}



/**
 * Parses the hash parameters to this page and returns them as an object.
 * @function
 */
function getHashParams()
{
	var params = {};
	var hashFragment = window.location.hash;
	
	if(hashFragment)
	{
		// split up the query string and store in an object
		var paramStrs = hashFragment.slice(1).split('&');
		
		for (var i = 0; i < paramStrs.length; i++)
		{
			var paramStr = paramStrs[i].split('=');
			params[paramStr[0]] = unescape(paramStr[1]);
		}
	}
  
	// Opening from Drive will encode the state in a query search parameter
	var searchFragment = window.location.search;
	if (searchFragment)
	{
		// split up the query string and store in an object
		var paramStrs2 = searchFragment.slice(1).split('&');
		
		for(var j = 0; j < paramStrs2.length; j++)
		{
			var paramStr2 = paramStrs2[j].split('=');
			params[paramStr2[0]] = unescape(paramStr2[1]);
		}
	}

	return params;
}

function addAtPosition($item, $list, index)
{
	//append to end
	if(index === null
		|| typeof(index) == 'undefined'
		|| index > $('li', $list).length)
	{
		$item.appendTo($list);
	}
	else if(index === 0)
	{
		$item.prependTo($list);
	}
	else
	{
		$item.insertAfter($list.children().eq(index-1));
	}
}


function ellipsize(text, maxLength)
{
    var ret = text;
	
    if(ret.length > maxLength)
        ret = ret.substr(0, maxLength - 1) + "&hellip;";

    return ret;
}

function sizeOfObject(object)
{
    var objectList = [];
    var stack = [ object ];
    var bytes = 0;

    while ( stack.length ) {
        var value = stack.pop();

        if ( typeof value === 'boolean' ) {
            bytes += 4;
        }
        else if ( typeof value === 'string' ) {
            bytes += value.length * 2;
        }
        else if ( typeof value === 'number' ) {
            bytes += 8;
        }
        else if
        (
            typeof value === 'object'
            && objectList.indexOf( value ) === -1
        )
        {
            objectList.push( value );

            for( var i in value ) {
                stack.push( value[ i ] );
            }
        }
    }
    return bytes;
}

function setStatus(status)
{
	window['status'] = status;
	console.log(window['status'] = status);
}

function base64Encode(str)
{
	var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var out = "", i = 0, len = str.length, c1, c2, c3;
	
	while (i < len)
	{
		c1 = str.charCodeAt(i++) & 0xff;
		
		if (i == len)
		{
			out += CHARS.charAt(c1 >> 2);
			out += CHARS.charAt((c1 & 0x3) << 4);
			out += "==";
			break;
		}
		c2 = str.charCodeAt(i++);
		
		if (i == len)
		{
			out += CHARS.charAt(c1 >> 2);
			out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
			out += CHARS.charAt((c2 & 0xF) << 2);
			out += "=";
			break;
		}
		
		c3 = str.charCodeAt(i++);
		out += CHARS.charAt(c1 >> 2);
		out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
		out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
		out += CHARS.charAt(c3 & 0x3F);
	}
	
	return out;
}


function formatMoney(c)
{
	if(!c) return '';
	
	return Number(c).toLocaleString('en-us', { style: 'currency', currency: 'USD' }).split('.')[0];
}


function formatDate(dt)
{
	if(!dt)
		return '';
	
	var date = new Date(dt);
	
	var year = date.getFullYear();
	var month = (1 + date.getMonth()).toString();
	
	month = month.length > 1 ? month : '0' + month;
	
	var day = date.getDate().toString();
	day = day.length > 1 ? day : '0' + day;
	
	return year + '-' + month + '-' + day;
}


function array_intersection(old, nu, property, inclusion)
{
	//items to add starts empty
	var added = [];
	
	//start with all the old items, which we'll remove as we find them in the new list
	var removed = old.slice(0);
	
	var unchanged = [];
	
	
	//go through all new items
    for(var i = 0; i < nu.length; i++)
	{
		if(!nu[i]) continue;
			
		var found = false;
		
		//go through all old items
        for(var j = 0; j < removed.length; j++)
		{
			if(!removed[j]) continue;
			
			//if the new item is found in the old list
			//also check if the item is to be included no matter what
            if(removed[j][property] == nu[i][property]
				|| (inclusion && removed[j][property] == inclusion[property]))
			{
				//this new item is in the old list, so remove it from the old list
				found = true;
				
				//remove this item from the remove list
				var unchanged_item = removed.splice(j, 1)[0];
				
				//add it to the unchanged list
				unchanged.push(unchanged_item);
				
                break;
            }
		}
			
		if(!found)
			added.push(nu[i]);
	}
	
	
	var obj =
	{
		'added':		added,
		'removed':		removed,
		'unchanged':	unchanged
	};
	
    return obj;
}


var second = 1e3,
	minute = 6e4,
	hour = 36e5,
	day = 864e5,
	week = 6048e5;

function timeFromNow(tm)
{
    var system_date = new Date(Date.parse(tm));
    var user_date = new Date();
	
    var diff = Math.floor((user_date - system_date) / 1000);
    if (diff <= 1) {return "just now";}
    if (diff < 20) {return diff + " seconds ago";}
    if (diff < 40) {return "half a minute ago";}
    if (diff < 60) {return "less than a minute ago";}
    if (diff <= 90) {return "one minute ago";}
    if (diff <= 3540) {return Math.round(diff / 60) + " minutes ago";}
    if (diff <= 5400) {return "1 hour ago";}
    if (diff <= 86400) {return Math.round(diff / 3600) + " hours ago";}
    if (diff <= 129600) {return "1 day ago";}
    if (diff < 604800) {return Math.round(diff / 86400) + " days ago";}
    if (diff <= 777600) {return "1 week ago";}
	/*
	if (diff < 1209600) {return Math.round(diff / 1209600) + " weeks ago";}
    if (diff <= 4838400) {return "1 month ago";}
	*/
	
	var dateString =
		system_date.getFullYear() +"/"+
		("0" + (system_date.getMonth()+1)).slice(-2) +"/"+
		("0" + system_date.getDate()).slice(-2) + " " +
		("0" + system_date.getHours()).slice(-2) + ":" +
		("0" + system_date.getMinutes()).slice(-2) + ":" +
		("0" + system_date.getSeconds()).slice(-2);
	
    return "on " + dateString;
}

function timeDifference(a, b)
{
	var date_a = new Date(a);
	var date_b = new Date(b);
	
	var sec = parseInt((date_b - date_a) / 1000);
	
	var ret = '';
	
	if(sec > 60)
	{
		ret += parseInt(sec / 60) + ' min ';
		sec = sec % 60;
	}
	
	ret += sec + ' sec';
	
	return ret;
}