// 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); }
Array.prototype.has = function(v) {
    for (i=0;i<this.length;i++){
        if (this[i]==v) return true;
    }
    return false;
}


// 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.parseDateTime = function(str) {
    if (match = /^([0-9]{4}).([0-9]{2}).([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/.exec(str))
        return  new Date(int(match[1]),int(match[2])-1,int(match[3]),int(match[4]),int(match[5]),int(match[6]));
    else
        return null;
}

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;
}
Date.prototype.dateOnly = function() {
    var today = new Date(this.getTime());
    today.setHours(0);
    today.setMinutes(0);
    today.setSeconds(0);
    today.setMilliseconds(0);
    return today
}
Date.prototype.toLocalTime = function() {
    var offs = new Date().getTimezoneOffset();
    return new Date(this.getTime() - (offs * 60000));
}
Date.prototype.toUTCTime = function() {
    var offs = new Date().getTimezoneOffset();
    return new Date(this.getTime() + (offs * 60000));
}

Date.prototype.toFriendlyString = function() {
    var ms = this.getTime();
    var date = this.dateOnly();
    var today = new Date().dateOnly();
    var msDate = date.getTime();
    var msToday = today.getTime();

    if (ms == 0) {
        // empty date
        result = "Never";
    }
    else if (msDate == msToday)
    {
        result = 'Today, ' + this.strftime('%H:%M');
    }
    else if (msDate == today.addHours(-24).getTime())
    {
        result = 'Yesterday, ' + this.strftime('%H:%M');
    }
    else
    {
        // anything before yesterday
        result = this.strftime('%b %d, %H:%M');
    }
    return result;
}


// 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);
    }
}

function Trip() {
    this.id;    this.date;    this.month;
    this.dom;    this.eqp;    this.pos;
    this.mine = false;

    var self = this;

    this.isSameTrip = function(other) {
        return     self.id == other.id &&
                self.date == other.date &&
                self.month == other.month &&
                self.dom == other.dom &&
                self.eqp == other.eqp &&
                self.pos == other.pos;
    };

    this.getParams = function() {
        return {
            id: self.id,
            date: self.date,
            m: self.month,
            d: self.dom,
            f: self.eqp,
            s: self.pos
        };
    };

    this.toString = function() {
        return ''+self.id+'_'+self.date+'_'+self.month+'_'+self.dom+'_'+self.eqp+'_'+self.pos;
    };

    this.toUrlParams = function() {
        return "id=" + self.id + "&date=" + self.date + "&m=" + self.month + "&d=" + self.dom + "&f=" + self.eqp + "&s=" + self.pos;
    }
}

Trip.fromString = function(str) {
    var rx = /^([0-9]{1,4})_([0-9]{4}-[0-9]{2}-[0-9]{2})_([0-9]{6})_([A-Z]{3})_([A-Z0-9]{3})_(CAP|FO|FA)$/i,
        m = rx.exec(str);
    if (!m) return null;
    var t = new Trip(), i = 0;
    t.id = m[++i];
    t.date = m[++i];
    t.month = m[++i];
    t.dom = m[++i];
    t.eqp = m[++i];
    t.pos = m[++i];
    return t;
}

Trip.fromUrl = function(url) {
    url = url.replace(/\&amp;/g,'_');
    url = url.replace(/&/g,'_');
    var rx = /id=([0-9]{1,4})_date=([0-9]{4}-[0-9]{2}-[0-9]{2})_m=([0-9]{6})_d=([A-Z]{3})_f=([A-Z0-9]{3})_s=(CAP|FO|FA)$/i,
        m = rx.exec(url);
    if (!m)
        return null;

    var t = new Trip(), i = 0;
    t.id = m[++i];
    t.date = m[++i];
    t.month = m[++i];
    t.dom = m[++i];
    t.eqp = m[++i];
    t.pos = m[++i];
    return t;
}

function Trade() {
    this.tradeItems = [];
    this.vacationDrop = false;

    var self = this;

    this.add = function(type, trip) {
        var found = false;
        var items = self.tradeItems;
        for (var i=0; i<items.length; i++) {
            var titype = items[i][0], titrip = items[i][1];
            if (titype == type && trip.isSameTrip(titrip)) {
                found = true;
                break;
            }
        }

        if(!found)
            items.push([type,trip]);
    };

    this.remove = function(trip) {
        var items = self.tradeItems;
        for (var i=items.length-1; i>=0; i--) {
            var titype = items[i][0], titrip = items[i][1];
            if (trip.isSameTrip(titrip)) {
                delete self.tradeItems[i];
                self.tradeItems.splice(i, 1);
            }
        }
    };

    this.replaceCriteria = function(crit) {
        var items = self.tradeItems;
        for (var i=0; i<items.length; i++) {
            if(items[i][0] == 'intoCriteria') {
                items[i][1] = crit;
                break;
            }
        }
    }

    this.removeAll = function() {
        self.tradeItems = [];
    }

    this.removeAllInto = function() {
        var items = self.tradeItems;
        for (var i=items.length-1; i>=0; i--) {
            if(items[i][0] == 'into') {
                delete self.tradeItems[i];
                self.tradeItems.splice(i, 1);
            }
        }
    }

    this.getParams = function() {
        var params = {};
        var items = self.tradeItems;
        if (items.length > 0) {
            for (var i=0; i<items.length; i++) {
                var tiType = items[i][0], tiData = items[i][1];
                switch(tiType) {
                    case 'into':
                    case 'away':
                        params['ti'+i+'_type'] = tiType;
                        params['ti'+i+'_trip'] = tiData.toString();
                        break;
                    case 'intoCriteria':
                        for (var j=0; j<tiData.length; j++) {
                            var cr = tiData[j];
                            if(cr) {
                                params['crit_'+j+'_type'] = cr.critType;
                                params['crit_'+j+'_co'] = cr.cmpOp;
                                params['crit_'+j+'_value'] = cr.value;
                            }
                        }
                        break;
                }
            }
        }
        params['vacationDrop'] = this.vacationDrop;
        return params;
    };

    this.getUrlParams = function() {
        var params = this.getParams();
        var result = '';

        for(var key in params) {
            if(result != '')
                result += '&';
            result += key + '=' + params[key];
        }

        return result;
    }

    this.clone = function() {
        trade = new Trade();
        for(var i=0; i < this.tradeItems.length; i++)
        {
            ti = this.tradeItems[i];
            trade.add(ti[0], ti[1])
        }
        trade.vacationDrop = this.vacationDrop;

        return trade;
    }

    this.isDirect = function() {
        if(this.tradeItems.length == 0)
            return false;

        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'intocriteria')
                return false;
        }

        return true;
    }

    this.getTradeDirection = function() {
        if(this.tradeItems.length == 0)
            return 'unknown';

        var intocrit = 0;
        var into = 0;
        var away = 0;
        for(var i=0; i < this.tradeItems.length; i++)
        {
            switch (this.tradeItems[i][0])
            {
                case 'intocriteria':
                    intocrit++;
                    break;
                case 'into':
                    into++;
                    break;
                case 'away':
                    away++;
                    break;
            }
        }

        if(intocrit > 0) {
            return away > 0 ? "trade" : "crit-pickup";
        }
        else {
            if(away > 0) {
                return into > 0 ? "trade" : "drop"
            }
            else {
                return into > 0 ? "pickup" : "unknown";
            }
        }
    }

    this.getTradeIntoTrips = function() {
        var trips = [];
        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'into') {
                trips.push(Trip.fromString(this.tradeItems[i][1]));
            }
        }

        return trips;
    }

    this.getIntoTripIds = function() {
        var ids = [];
        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'into') {
                ids.push(this.tradeItems[i][1]);
            }
        }

        return ids;
    }

    this.getAwayTripIds = function() {
        var ids = [];
        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'away') {
                ids.push(this.tradeItems[i][1]);
            }
        }

        return ids;
    }

    this.getIntoTripsCount = function() {
        var count = 0;
        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'into') {
                count++;
            }
        }

        return count;
    }

    this.hasIntoTrip = function(trip) {
        for(var i=0; i < this.tradeItems.length; i++)
        {
            if(this.tradeItems[i][0] == 'into') {
                if(trip.isSameTrip(this.tradeItems[i][1]))
                    return true;
            }
        }

        return false;
    }

    this.isEmpty = function() {
        return this.tradeItems.length == 0;
    }
}

Trade.fromString = function(str) {
    var trade = new Trade();

    str = str.replace(/\&amp;/g,'&');
    var currentType = null;
    var currentTrip = null;
    var params = str.split('&');
    var rxMatch = new RegExp('/^ti([0-9]+)_(.*)$/', 'i');

    for(i=0; i < params.length; i++)
    {
        var param = params[i];
        var nv = param.split('=');
        var name = nv[0];
        var value= nv[1];
        var matches = /^ti([0-9]+)_(.*)/i.exec(name);

        if(matches != null)
        {
            var tiId = matches[1];
            var varName = matches[2];

            switch (varName)
            {
                case 'type':
                    currentType = value;
                    break;
                case 'trip':
                    // value is in format ID_date_month_dom_eqp_pos
                    currentTrip = Trip.fromString(value);
                    break;
            }

            if(currentType != null && currentTrip != null)
            {
                trade.add(currentType, currentTrip);
                currentType = null;
                currentTrip = null;
            }
        }
        else {
            if(name == 'vacationDrop');
                trade.vacationDrop = (value == '1' || value == 'true');
        }
    }

    return trade;
}

function TradeItem() {
    this.type = null;
    this.trip = null;
    this.tripFilter = null;
}

TradeItem.fromString = function(str) {
    str = str.replace(/\&amp;/g,'&');
    var params = str.split('&');
    var ti = new TradeItem();

    for(i=0; i < params.length; i++)
    {
        var param = params[i];
        var nv = param.split('=');
        var name = nv[0];
        var value= nv[1];
        var matches = /^ti([0-9]+)_(.*)/i.exec(name);

        if(matches != null)
        {
            var tiId = matches[1];
            var varName = matches[2];

            switch (varName)
            {
                case 'type':
                    ti.type = value;
                    break;
                case 'trip':
                    // value is in format ID_date_month_dom_eqp_pos
                    ti.trip = Trip.fromString(value);
                    break;
            }
        }
    }

    if(ti.type == null || ti.trip == null)
        return null;
    else
        return ti;
}

function TripFilterCriteria() {
    this.critType = null;
    this.cmpOp = null;
    this.value = null;
}

