// make sure we don't get errors because of logging on browsers that don't have a console object
if (typeof window.console == 'undefined') {
	window.console = {
		assert: function(){},
		log: function(){},
		trace: function(){},
		debug: function(){},
		info: function(){},
		warn: function(){},
		error: function(){}
	};
}


// misc functions

function hasClass(node, className) {
	if (node.className) {
		var classes = node.className.split(/\s+/);
		for (var i=0; i<classes.length; i++)
			if (classes[i] == className) return true;
	}
	return false;
}

function addClass(node, className) {
	if (hasClass(node, className)) return;
	var newclass = "" + (node.className ? node.className+' ' : '');
	node.className = newclass + className;
}

function removeClass(node, className) {
	if (node.className) {
		var classes = node.className.split(/\s+/);
		for (var i=0; i<classes.length; i++) {
			if (classes[i] == className) {
				classes.splice(i, 1);
				i--;
			}
		}
		node.className = classes.join(' ');
	}
}


// a function similar to parseInt but will not interpret integers with
// leading zeros as being in octal
function int(i) {
	if (typeof(i) == 'string') {
		while (i.length > 0 && i.charAt(0) == '0') i = i.substring(1);
		return i.length > 0 ? parseInt(i) : 0;
	}
	return i;
}

function range(x, y) {
	var result = [];
	while (x <= y) result.push(x++);
	return result;
}


// cookie functions (functions adapted from the jquery.cookie plugin)

function getCookie(name) {
	var cookieValue = null;
	if (document.cookie && document.cookie != '') {
		var cookies = document.cookie.split(';');
		for (var i = 0; i < cookies.length; i++) {
			var cookie = cookies[i].trim();
			// Does this cookie string begin with the name we want?
			if (cookie.substring(0, name.length + 1) == (name + '=')) {
				cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
				break;
			}
		}
	}
	return cookieValue;
}

function closeRemindExpire() {
	if ($("#noReminder").is(':checked')) {
		
		$.ajax({
			type: 'GET',
			url: 'expreminder.php',
			data:'remind=0',
			timeout: 15000
		});
		
		setCookie('remindExpire', 'dontremind', { path: '/'	});
	}
	else 
		setCookie('remindExpire', 'closed', {
			path: '/'
		});
	$('#expiration').hide();		
}


/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
function setCookie(name, value, options) {
	options = options || {};
	if (value === null) {
		value = '';
		options.expires = -1;
	}
	var expires = '';
	if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
		var date;
		if (typeof options.expires == 'number') {
			date = new Date();
			date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
		} else {
			date = options.expires;
		}
		expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
	}
	var path = options.path ? '; path=' + (options.path) : '';
	var domain = options.domain ? '; domain=' + (options.domain) : '';
	var secure = options.secure ? '; secure' : '';
	document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
}


// number extensions

// put some ettmonth functions on the Number prototype
Number.prototype.year = function() { return Math.floor(this / 100); };
Number.prototype.month = function() { return this % 100; };
Number.prototype.addmonths = function(months) {
	if (months == 0) return this;
	var y = this.year();
	var m = this.month();
	var step = (months > 0 ? 1 : -1);
	do {
		m += step;
		if (m == 0) {
			y--;
			m = 12;
		} else if (m == 13) {
			y++;
			m = 1;
		}
	} while ( (months -= step) != 0 );
	return (y * 100) + m;
};
Number.prototype.addmonth = function() { return this.addmonths(1); }
Number.prototype.formatMonthForDisplay = function() {
	return new Date(this.year(), this.month()-1).strftime('%B, %Y');
};



// string extensions

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
}

String.prototype.lpad = function(length, char) {
	return length <= this.length ? this : new Array(length-this.length+1).join(char||' ') + this;
}

String.prototype.rpad = function(length, char) {
	return length <= this.length ? this : this + new Array(length-this.length+1).join(char||' ');
}

String.prototype.contains = function(str) {
	return this.indexOf(str) != -1;
}

// similar to string.replace
String.prototype.gsub = function(needle, replace) {
	var result = this, match;
	while (match = result.match(needle))
		result = result.replace(match.toString(), replace(match.toString()) || '');
	return result;
}


// array extensions

Array.prototype.first = function() { return (this.length > 0 ? this[0] : null); }
Array.prototype.last = function() { return (this.length > 0 ? this[this.length-1] : null); }


// date extensions

Date.dayNames = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(/ /g);
Date.monthNames = 'January February March April May June July August September October November December'.split(/ /g);
Date.parse2 = function(str) {
	var match;
	if (match = /^([0-9]{4})([0-9]{2})([0-9]{2})$/.exec(str)) {
		return new Date(int(match[1]),int(match[2])-1,int(match[3]));
	} else if (match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(str)) {
		return new Date(int(match[1]),int(match[2])-1,int(match[3]));
	} else {
		return Date.parse(str);
	}
};

Date.prototype.neutral = function() { return new Date(this.getFullYear(), this.getMonth(), this.getDate(), 12); }
Date.prototype.strftime = function(format) {
	var date = this, dow = this.getDay(), month = this.getMonth();
	var hours = this.getHours(), minutes = this.getMinutes();
	function pad(num) { return num <= 9 ? '0'+num : num.toString(); };

	return format.gsub(/\%[aAbBcdHImMpSwyY]/, function(part) {
		switch(part.substring(1)) {
			case 'a': return Date.dayNames[dow].substring(0, 3);
			case 'A': return Date.dayNames[dow];
			case 'b': return Date.monthNames[month].substring(0, 3);
			case 'B': return Date.monthNames[month];
			case 'c': return date.toString();
			case 'd': return pad(date.getDate());
			case 'H': return pad(hours);
			case 'I': return (hours % 12 == 0) ? 12 : pad(hours % 12);
			case 'm': return pad(month + 1);
			case 'M': return pad(minutes);
			case 'p': return hours >= 12 ? 'PM' : 'AM';
			case 'S': return pad(date.getSeconds());
			case 'w': return dow;
			case 'y': return pad(date.getFullYear() % 100);
			case 'Y': return date.getFullYear().toString();
		}
	});
};
Date.prototype.addHours = function(h) {    
   this.setTime(this.getTime() + (h*60*60*1000)); 
   return this;   
}


// function extensions

// set the function's execution scope to a specified context object
Function.prototype.setContext = function(context) {
	if (typeof context == 'undefined') return this;
	var func = this;
	return function() { return func.apply(context, arguments); }
}

function TimestampUpdater(selector) {
    this.updaters = new Array();
    this.modifyng = false;
    this.stopped = false;
    var constCheckInterval = 20000; // 20s
    
    this.addTimestamp = function(selector) {
        var upd = new SingleUpdater(selector);
        if(upd.isValid)
            this.updaters.push(upd);
    }
    this.removeTimestamp = function(selector) {
        for(var i = 0; i < this.updaters.length; i++) {
            if(this.updaters[i].selector == selector) {
                this.updaters.splice(i, 1);
                break;
            }                
        }
    }
    this.syncTimestamp = function(selector) {
        this.modifyng = true;
        this.removeTimestamp(selector);
        this.addTimestamp(selector);
        this.modifyng = false;
    }
    this.run = function() {
        var self = this;
        window.setTimeout(function() {
            self.doUpdate();
        }, constCheckInterval);        
    }
    this.stop = function() {
        this.stopped = true;
    }
    this.doUpdate = function() {
        if(!this.modifyng && !this.stopped) {
            for(var i = 0; i < this.updaters.length; i++) {
                var supd = this.updaters[i];
                if(supd)
                    supd.updateTimestamp();
            }
        }
        
        if(!this.stopped)
            window.setTimeout(getTimeoutFunction(this), constCheckInterval);
    }
    
    function getTimeoutFunction(context) {
        var self3 = context;
        return (function(){
            self3.doUpdate();
        });
    }
    
    function SingleUpdater(selector, id) {
        this.selector = selector;
        this.isValid = false;
        
        var $cont = null;
        var $spanText = null;
        var $spanValue = null;
        var initialInterval = 0;
        var currentInterval = 0;
        var dtLoaded = null;

        function getTimestampContainer(selector) {
            var $findResult = $(selector);
            if($findResult.length > 0)
               return $($findResult[0]);

            return null;
        };
        function getSpanText() {
            if($cont) {
               var $spans = $cont.find('span.ts-text');
               if($spans.length > 0)
                   return $($spans[0]);
            }
            return null;
        };
        function getSpanValue() {
            if($cont) {
               var $spans = $cont.find('span.ts-value');
               if($spans.length > 0)
                   return $($spans[0]);
            }
            return null;
        };
        function formatDuration(seconds) {
            var val = 0;
            if (seconds < 60) {
                val = seconds;
                return val + " second" + (val>1?'s':'');
            } else if (seconds < 60 * 60) {
                val = Math.round(seconds / 60);
                return val + " minute" + (val>1?'s':'');
            } else if (seconds < 60 * 60 * 24) {
                val = Math.round(seconds / 60 / 60);
                return val + " hour" + (val>1?'s':'');
            } else {
                val = Math.round(seconds / 60 / 60 / 24);
                return val + " day" + (val>1?'s':'');
            }
        }
        function createCreationTimeSpan(tdelta) {
            $('<span class="hidden ts-creation-time">' + tdelta + '</span>').appendTo($cont);
        }
        function setInitialTime(now) {
            var $elemenst = $cont.find('span.ts-creation-time');
            if($elemenst.length > 0) {
                var ti = (int)($elemenst[0].innerHTML);
                var dt = new Date(ti);
                return dt;
            }
            else {
                var tdelta = now.getTime();
                createCreationTimeSpan(tdelta);
                return now;
            }
        }
        
        this.initialize = function() {
            $cont = getTimestampContainer(selector);
            if($cont) {
                $spanText = getSpanText();
                $spanValue = getSpanValue();
                if($spanText && $spanValue) {
                    var now = new Date();
                    dtLoaded = setInitialTime(now);
                    
                    initialInterval = (int)($spanValue.text());
                    if(now != dtLoaded) {
                        var delta = now.getTime() - dtLoaded.getTime();
                        delta = Math.round(delta / 1000);
                        delta += initialInterval;
                        if(delta < 0) {
                            delta = 0;
                            currentInterval = initialInterval;
                        }
                        else {
                            currentInterval = delta;
                        }
                        
                        $spanText.text(formatDuration(currentInterval) + ' ago');
                    }
                    else {
                        currentInterval = initialInterval;
                    }
                    this.isValid = true;
                }
            }
        };

        this.updateTimestamp = function() {
            var ts = (new Date()).getTime() - dtLoaded.getTime();

            currentInterval = initialInterval + Math.round(ts / 1000);
            if(currentInterval < 0)
                currentInterval = 0;
            
            $spanText.text(formatDuration(currentInterval) + ' ago');
        }
        
        this.initialize();
    }
    
    if(typeof(selector) == 'string') 
        this.addTimestamp(selector);
}

TimestampUpdater.setLastUpdated = function($from, $to) {
    if(!$from || !$to)
        return;
        
    var $foundEl = $from.find('span.ts-value');
    if($foundEl.length > 0) {
        // duration
        var mrVal = $foundEl[0].innerHTML;
        $to.find('span.ts-value').text(mrVal);
        // date loaded
        $foundEl = $from.find('span.ts-creation-time');
        if($foundEl.length > 0)
            var mrCrTime = $foundEl[0].innerHTML;
        else
            var mrCrTime = (new Date()).getTime();
        
        $foundEl = $to.find('span.ts-creation-time');    
        if($foundEl.length > 0)
            $foundEl[0].innerHTML = mrCrTime;
        else
            createCreationTimeSpan(mrCrTime);
    }
}