//------------------------------------------------------------------------------
//==============================================================================
// Skin Functions
//==============================================================================
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Choose and load skin
// Notes:
//	o For now this just loads the default skin's css
function loadSkin() {
	//---
	var styleLink = document.getElementById('styleLink');
	var currentStyleHref='/skin/default/css/default.css';
	if ( styleLink.href != currentStyleHref ) {
		styleLink.href = '/skin/default/css/default.css';
	}
	//---
}
//------------------------------------------------------------------------------
//==============================================================================
// Utility Functions
//==============================================================================
//------------------------------------------------------------------------------
// Reference: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_levenshtein/
function levenshtein1( str1, str2 ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
    // *     example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
    // *     returns 1: 3

    var s, l = (s = str1.split("")).length, t = (str2 = str2.split("")).length, i, j, m, n;
    if(!(l || t)) return Math.max(l, t);
    for(var a = [], i = l + 1; i; a[--i] = [i]);
    for(i = t + 1; a[0][--i] = i;);
    for(i = -1, m = s.length; ++i < m;){
        for(j = -1, n = str2.length; ++j < n;){
            a[(i *= 1) + 1][(j *= 1) + 1] = Math.min(a[i][j + 1] + 1, a[i + 1][j] + 1, a[i][j] + (s[i] != str2[j]));
        }
    }
    return a[l][t];
}
//------------------------------------------------------------------------------
function levenshteinWords( str1, str2 ) {
        var words1 = str1.split(' ');
        var words2 = str2.split(' ');
        var d = 0;
        for ( var i=0; i < words1.length; i++ ) {
                var word1 = ( words1[i] ) ? words1[i] : "";
                var word2 = ( words2[i] ) ? words2[i] : "";
                d += levenshtein(word1, word2);
        }
        return d;
}
//------------------------------------------------------------------------------
function _a_n_t_i_s_p_a_m_() {
	//---
	document.write('<a href="mailto:' + arguments[0] + '@' + arguments[1] + '.' + arguments[2] + '">' + arguments[0] + '@' + arguments[1] + '.' + arguments[2] + '<' + '/' + 'a' + '>');
	//---
}
//------------------------------------------------------------------------------
function getXMLHttpRequest() {
	//---
	if ( typeof XMLHttpRequest != 'undefined' ) {
		return new XMLHttpRequest();
	} else if ( typeof ActiveXObject != 'undefined' ) {
		return new ActiveXObject('Microsoft.XMLHTTP');
	} else {
		alert("We're sorry, but you need a newer browser to view our website.");
	}
	//---
}
//------------------------------------------------------------------------------
function evalJSON( JSONString ) {
	//---
	//---
	return eval('(' + JSONString + ')');
}
//------------------------------------------------------------------------------
function onEnter( event ) {
	//---
	//---
	return ( (event.which && event.which == 13) || (event.keyCode && event.keyCode == 13) ) ? true : false;
}
//------------------------------------------------------------------------------
function escapeString( subject, escapeChars ) {
	//---
	// Escape slashes that may be escaping other characters
	subject = subject.replace(/\\/g,"\\\\");

	// Escape requested characters
	var chars = escapeChars.split('');
	for ( var i=0; i < chars.length; i++ ) {
		var re = new RegExp(chars[i], 'g');
		subject = subject.replace(re, "\\" + chars[i]);
	}
	//---
	return subject;
}
//------------------------------------------------------------------------------
function escapeURL( subject ) {
	return ( typeof encodeURI != "undefined" ) ?  encodeURI(subject) : escape(subject);
}
//------------------------------------------------------------------------------
function escapeURLParam( subject ) {
	return ( typeof encodeURIComponent != "undefined" ) ?  encodeURIComponent(subject) : escape(subject).replace(/@/g,'%40').replace(/\+/g,'%2B').replace(/\//g, '%2F');
}
//------------------------------------------------------------------------------
function escapeJS( subject ) {
	//---
	subject = subject.replace(/\\/g,"\\\\"); // Escaping existing escapes
	subject = subject.replace(/"/g, '\\"');
	subject = subject.replace(/'/g, "\\'");
	//subject = subject.replace(/\\n/g, '\\\\n');
	subject = subject.replace(/\n/g, '\\n');
	//subject = subject.replace(/\\r/g, '\\\\r');
	subject = subject.replace(/\r/g, '\\r');
	//---
	return subject;
}
//------------------------------------------------------------------------------
function escapeHTML( subject ) {
	//---
	subject = subject.replace(/&(?!amp;)/g, '&amp;');
	subject = subject.replace(/"/g, '&quot;');
	subject = subject.replace(/'/g, '&#39;');
	subject = subject.replace(/</g, '&lt;');
	subject = subject.replace(/>/g, '&gt;');
	//---
	return subject;
}
//------------------------------------------------------------------------------
// option:
//	status  	The status bar at the bottom of the window.
//	toolbar 	The standard browser toolbar, with buttons such as Back and Forward.
//	location 	The Location entry field where you enter the URL.
//	menubar 	The menu bar of the window
//	directories 	The standard browser directory buttons, such as What's New and What's Cool
//	resizable 	Allow/Disallow the user to resize the window.
//	scrollbars 	Enable the scrollbars if the document is bigger than the window
//	height 	Specifies the height of the window in pixels. (example: height='350')
//	width 	Specifies the width of the window in pixels.
//
function newWindow( url, name, option ) {
	if ( ! defined(option) ) { option = {}; }
	if ( ! defined(name) ) { name = '_blank'; }
	//---
	// Generate options string
	var optionNames = ['status', 'toolbar', 'location', 'menubar', 'directories', 'resizable', 'scrollbars', 'height', 'width'];
	var options = [];
	for ( var i=0; i < optionNames.length; i++ ) {
		var optionName = optionNames[i];
		if ( typeof option[optionName] != "undefined" ) {
			options.push(optionName + '=' + option[optionName]);
		}
	}
	var win = window.open(url, name, options.join(','));
	//---
	return win;
}
//------------------------------------------------------------------------------
// Reference: http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
	try {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	} catch ( e ) {
		alert('Exception ' + e.name + ': ' + e.message);
	}
}
//------------------------------------------------------------------------------
// Reference: http://www.quirksmode.org/js/cookies.html
function readCookie(name) {
	try {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	} catch ( e ) {
		alert('Exception ' + e.name + ': ' + e.message);
	}
}
//------------------------------------------------------------------------------
// Reference: http://www.quirksmode.org/js/cookies.html
function eraseCookie(name) {
	createCookie(name,"",-1);
}
//------------------------------------------------------------------------------
function defined( subject ) {
	return ( typeof subject != "undefined" && subject != null ) ? true : false;
}
//------------------------------------------------------------------------------
function len( subject ) {
	return ( defined(subject) ) ? new String(subject).length : 0;
}
//------------------------------------------------------------------------------
// Handler Functions
//==============================================================================
//------------------------------------------------------------------------------
function handleChannelOnclick( event, channelName ) {
	//---
	//alert('handleChannelOnclick');
	// Change channel
	//alert('handleChannelOnclick: ' + channelName + '; ' + Config['channel'][channelName]);
	var call = function() { changeChannel(Config['channel'][channelName]); };
	setTimeout(call, 0);
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleCategoryOnclick( event, categoryName ) {
	//---
	//alert('handleCategoryOnclick');
	// Change category
	var call = function() { changeCategory(Config['category'][categoryName]); };
	setTimeout(call, 0);
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleBookmarkRequest() {
	//---
	if ( window.location.hash ) {
		var bookmark = window.location.hash.replace(/^#/, '');
		var category = Config['categoryBookmark'][bookmark];
		var channel = Config['channel'][category['channelName']];
		var call = function() { changeChannel(channel, category); };
		setTimeout(call, 0);
	}
	//---
}
//------------------------------------------------------------------------------
function handleHistoryOnchange(newLocation, historyData) {
	//---
	//alert('newLocation: ' + newLocation + '; ' + 'historyData: ' + historyData);
	if ( historyData ) {
		var category = Config['category'][historyData['categoryName']];
		var channel = Config['channel'][category['channelName']];
		var call = function() { changeChannel(channel, category); };
		setTimeout(call, 0);
	} else if ( window.location.hash ) {
		// Else, if we have a bookmark hash, handle it
		var call = function() { handleBookmarkRequest(); };
		setTimeout(call, 0);
	} else {
		// Else, no history data at this history item, load up defaults
		//alert('handleHistoryOnchange: historyData=' + historyData + '; newLocation=' + newLocation);
		var call = function() { changeChannel(); };
		setTimeout(call, 0);
	}
	//---
}
//------------------------------------------------------------------------------
function handleWindowOnload() {
	//---
	//alert('handleWindowOnload');
	// Initialize RSH
	dhtmlHistory.initialize();

	// Initialize NewsX
	init();

	// Add history handler to RSH (must be done after NewsX initialization
	// because history handler will call our own functions)
	dhtmlHistory.addListener(handleHistoryOnchange);
	//---
}
//------------------------------------------------------------------------------
function handleSearchFormOnsubmit( form ) {
	//---
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleSearchFormKeywordsOnfocus( input ) {
	//---
	if ( input.value == 'Search by Keywords' ) { input.value=''; }
	//---
	return true
}
//------------------------------------------------------------------------------
function handleSearchFormKeywordsOnenter( input ) {
	//---
	if ( input.value.match(/\w+/) ) {
		var call = function() { searchNews(input.value); };
		setTimeout(call, 0);
	} else {
		alert('Please enter keywords in order to search.');
	}
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleArticleHeadlineOnclick( anchor ) {
	//---
	newWindow(anchor.href, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleArticleImageOnclick( anchor ) {
	//---
	newWindow(anchor.href, '', {width:800,height:480,resizable:1,scrollbars:1});
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleArticleSourceOnclick( anchor ) {
	//---
	newWindow(anchor.href, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleArticleSourceRegOnclick( anchor ) {
	//---
	newWindow(anchor.href, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return false;
}
//------------------------------------------------------------------------------
function handleArticleIconOnclick( headline_source, headline_text, headline_url ) {
	//---
	var uniqueID = headline_url.replace(/[^a-zA-Z0-9]/g, '');
	dhtmlwindow.open(uniqueID, "iframe", headline_url, headline_text + ' - ' + headline_source, "width=800px,height=480px,resize=1,scrolling=1,center=1");
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleGoogleNewsIconOnclick( headline_text ) {
	//---
	var url = 'http://news.google.com/?q=' + escape(headline_text);
	newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleGoogleSearchIconOnclick( headline_text ) {
	//---
	var url = 'http://google.com/search?q=' + escape(headline_text);
	newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleYahooIconOnclick( headline_text ) {
	//---
	var url = 'http://news.search.yahoo.com/news/search?p=' + escape(headline_text);
	newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleNewsIsFreeIconOnclick( headline_text ) {
	//---
	var url = 'http://newsisfree.com/pages/power/?query=' + escape(headline_text);
	newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleWikipediaIconOnclick( headline_text ) {
	//---
	var headline_text = prompt('Refining the headline text to specific terms can help improve the focus of your Wikipedia search.', headline_text);
	if ( headline_text ) {
		var url = 'http://en.wikipedia.org/wiki/Special:Search?go=Go&search=' + escape(headline_text);
		newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	}
	//---
	return true;
}
//------------------------------------------------------------------------------
function handleWikinewsIconOnclick( headline_text ) {
	//---
	var headline_text = prompt('Refining the headline text to specific terms can help improve the focus of your Wikinews search.', headline_text);
	if ( headline_text ) {
		var url = 'http://en.wikinews.org/wiki/Special:Search?go=Go&search=' + escape(headline_text);
		newWindow(url, '', {width:800,height:480,status:1,toolbar:1,location:1,menubar:1,resizable:1,scrollbars:1});
	}
	//---
	return true;
}
//------------------------------------------------------------------------------
//==============================================================================
// Business Functions
//==============================================================================
//------------------------------------------------------------------------------
function init() {
	//---
	//alert('init');
	// Setup
	setup();

	// Load default news
	if ( dhtmlHistory.isFirstLoad() ) {
		if ( window.location.hash ) {
			handleBookmarkRequest();
		} else {
			// Else, no bookmark hash
			//alert('no bookmark');
			changeChannel();
		}
	}
	//---
}
//------------------------------------------------------------------------------
function setup() {
	//---
	//
	// Setup context
	//

	// Store element references because getElementById can be slow
	// Update header/footer values
	Context['headerCurrentChannelName']	= document.getElementById('headerCurrentChannelName');
	Context['footerCurrentChannelName']	= document.getElementById('footerCurrentChannelName');
	Context['headerCurrentCategoryName']	= document.getElementById('headerCurrentCategoryName');
	Context['footerCurrentCategoryName']	= document.getElementById('footerCurrentCategoryName');
	Context['headerCurrentTime']		= document.getElementById('headerCurrentTime');
	Context['footerCurrentTime']		= document.getElementById('footerCurrentTime');
	Context['searchForm']			= document.getElementById('searchForm');
	Context['searchFormKeywords']		= document.getElementById('searchFormKeywords');
	Context['headlinesDiv']			= document.getElementById('headlines');
	Context['advertisementsDiv']		= document.getElementById('advertisements');
	Context['channelsDiv']			= document.getElementById('channels');
	Context['categoriesDiv']		= document.getElementById('categories');

	//
	// Setup client-side state
	//

	// Refresh state from cookie
	var stateStr = readCookie("state");
	if ( stateStr ) {
		try {
			var state = JSON.parse(stateStr);
			Config['channel']['Search'] = state['searchChannel'];
			var categories = state['searchChannel']['categories'];
			for ( var i=0; i < categories.length; i++ ) {
				Config['category'][categories[i]['categoryName']] = categories[i];
			}
		} catch ( e )  {
			alert("Exception " + e.name + ": " + e.message);
		}
	}

	// Display channels
	loadChannels();
	//---
}
//------------------------------------------------------------------------------
function loadNews( processed ) {
	//---
	// Request news
	if ( ! processed ) {

		// Clear interval refreshing while we load up new news
		clearInterval(Context['refreshIntervalID']);

		// Check category's modifyDate and display cached headlines if
		// available and if not timed out yet
		var currentCategory = Context['currentCategory'];
		if ( ! currentCategory['channelName'].match(/^Search/) && currentCategory['modifyDate'] && ((Date.now() - currentCategory['modifyDate']) / 1000) < Config['cacheTimeout'] ) {

			// Flash cached headlines
			currentCategory['cached'] = true;

			// Just display existing headlines
			displayNews();

		} else {

			// Activate loader-spinner
			//var call = function() {
				Context['headlinesDiv'].innerHTML = '<img src="/skin/default/img/loader-spinner-small.gif" width="16" height="16" title="Loading..." />';
				Context['advertisementsDiv'].innerHTML = '';
				//document.getElementById('headerSpinner').innerHTML = '<img src="/skin/default/img/loader-spinner-small.gif" width="16" height="16" align="absmiddle" title="Loading..." />';
			//};
			//setTimeout(call, 0);
			//advertisementsDiv.innerHTML = '<img src="/skin/default/img/loader-spinner-small.gif" width="16" height="16" title="Loading..." />';

			// Flag cached headlines
			currentCategory['cached'] = false;

			// Request news
			requestNews(loadNews);
		}
	} else {
		// Else, news acquired

		// Display news
		displayNews();

		// Setup interval refreshing
		Context['refreshIntervalID'] = setInterval("refreshCategory();", Config['refreshTimeout'] * 1000);
	}
	//---
}
//------------------------------------------------------------------------------
function requestNews( callback ) {
	//---
	var currentCategory = Context['currentCategory'];
	var r = getXMLHttpRequest();
	var url = '';
	if ( currentCategory['search'] ) {
		url = '/headlines.php?output=tsv&' + escape(Math.random()) + '=1';
		//keywords=' + escape(currentCategory['keywords']) + '&
		var words = currentCategory['keywords'];
		url += '&keywords' + escape('[]') + '=';
		for ( var i=0; i < words.length; i++ ) {
			url += escape(words[i]);
			if ( i < words.length - 1 ) { url += escape('|'); }
		}
	} else {
		// Else, normal category headlines
		url = '/headlines.php?category=' + escape(currentCategory['categoryName']) + '&output=tsv&' + escape(Math.random()) + '=1';
	}
	r.open('GET', url);
	r.onreadystatechange = function() { processNews(r, callback); };
	r.send(null);
	//---
}
//------------------------------------------------------------------------------
function processNews( r, callback ) {
	//---
	try {
		if ( r.readyState == XMLHTTPREQUEST_READYSTATE_LOADED ) {
			var data = r.responseText.split(/[\r\n]+/);
			Context['currentCategory']['headlines'] = [];
			Context['currentCategory']['advertisementHeadlines'] = [];
			for ( var i=0; i < data.length; i++ ) {

				// Skip if this is not a tsv line
				if ( ! data[i].match(/\t/) ) { continue; }

				// Protect against braindead (IE) split algos that
				// treat \t\t as one field
				data[i] = data[i].replace(/\t\t/g, '\t \t');
				data[i] = data[i].replace(/\t$/g, '\t ');

				// Split TSV into array
				var fields = data[i].split(/\t/);

				// Build healdine object
				var headline = {	"url":fields[0],
							"headline_text":fields[1],
							"source":fields[2],
							"media_type":fields[3],
							"cluster":fields[4],
							"tagline":fields[5],
							"document_url":fields[6],
							"harvest_time":fields[7],
							"access_status":fields[8],
							"access_registration":fields[9],
							"extra_sources":[] };

				// Flag advertisements
				headline['advertisement'] = ( headline['source'].indexOf('Ad - ') != -1 ) ? true : false;

				// Formatting
				headline['headline_text'] = headline['headline_text'].replace(/^(.{120}).*$/, '$1');
				if ( headline['headline_text'].length == 120 ) {
					//headline['headline_text'] = headline['headline_text'].replace(/ *[^ ]*$/, '...');
					headline['headline_text'] = headline['headline_text'].replace(/\s*[^ ]*$/, '...');
				}

				// Push on headlines
				if ( ! headline['advertisement'] ) {
					Context['currentCategory']['headlines'].push(headline);
				} else {
					Context['currentCategory']['advertisementHeadlines'].push(headline);
				}
			}

			// Group similar headlines as extra sources
			//var start = new Date();
			var headlines = Context['currentCategory']['headlines'];
			for ( var i = 0; i < headlines.length; i++ ) {
				var headline = headlines[i];
				if ( ! headline ) { alert('ack! no headline grouping similar headlines as extra sources!'); }
				var headline_text = headline['headline_text'].replace(/[^a-zA-Z]/g, '').toUpperCase();
				for ( var j=i+1; j < headlines.length; j++ ) {
					var checkHeadline = headlines[j];
					var checkHeadline_text = checkHeadline['headline_text'].replace(/[^a-zA-Z]/g, '').toUpperCase();
					if ( headline_text == checkHeadline_text ) {
						headline['extra_sources'].push(checkHeadline);
						headlines.splice(j, 1);
						j--; // Splice correction
						//alert(headline['extra_sources'].length + '; ' + headlines.length + '; ' + Context['currentCategory']['headlines'].length);
					}
				}
				//headline['extra_sources'].reverse();
			}

			// Timestamp category
			Context['currentCategory']['modifyDate'] = new Date().getTime();

			// Push on cached categories
			Context['cachedCategories'].push(Context['currentCategory']);
			
			// Clean cached categories
			//alert(Context['cachedCategories'].length + '; ' + Context['cachedCategories']);
			if ( Context['cachedCategories'].length > Config['maxCachedCategories'] ) {
				Context['cachedCategories'].shift();
				//alert(Context['cachedCategories'].length + '; ' + Context['cachedCategories']);
			}

			// Callback
			callback(true);
		}
	} catch ( e ) {
		alert('Exception ' + e.name + ': ' + e.message);
	}
	//---
}
//------------------------------------------------------------------------------
function displayNews() {
	var headlines = Context['currentCategory']['headlines'];
	var advertisementHeadlines = Context['currentCategory']['advertisementHeadlines'];
	//---
	var headlinesDiv = Context['headlinesDiv'];
	var html = ''
	for ( var i=0; i < headlines.length; i++ ) {
		var headline = headlines[i];
		if ( i % 2 != 0 ) {
			html += '<div style="border: 1px solid gray; padding: 1px; background-color: #eeeeee">';
		} else {
			html += '<div style="border: 1px solid darkgray; padding: 1px;">';
		}

		// Article headline
		html += '<div class="headline" style="float: left; padding: 1px;">';
		html += '<a href="' + escapeHTML(headline['url']) + '" title="Open article web page (new window)" onclick="' + escapeHTML('return handleArticleHeadlineOnclick(this);') + '">' + escapeHTML(headline['headline_text']) + '</a>';
		html += '</div>';

		// Article links
		html += '<div style="float: right;">';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Open article web page (in-page window)" style="float: left; width: 12px; height: 12px; border: 1px solid black; cursor: pointer; color: black; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleArticleIconOnclick("' + escapeJS(headline['source']) + '", "' + escapeJS(headline['headline_text']) + '", "' + escapeJS(headline['url']) + '");') + '">A</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div style="float: left; width: 1px; height: 12px; background-color: gray; border: 1px solid gray;"><img src="/skin/default/img/1x1.gif" width="1" height="12" /></div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search for this headline on Google News (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid skyblue; cursor: pointer; color: skyblue; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleGoogleNewsIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">G</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search Yahoo News for this headline (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid #FF0033; cursor: pointer; color: #FF0033; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleYahooIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">Y</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search NewsIsFree.com for this headline (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid #044382; cursor: pointer; color: #044382; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleNewsIsFreeIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">N</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search Wikinews for this headline (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid gray; cursor: pointer; color: gray; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleWikinewsIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">W</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div style="float: left; width: 1px; height: 12px; background-color: gray; border: 1px solid gray;"><img src="/skin/default/img/1x1.gif" width="1" height="12" /></div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search Google for this headline (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid blue; cursor: pointer; color: blue; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleGoogleSearchIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">G</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '<div title="Search Wikipedia for this headline (new window)" style="float: left; width: 12px; height: 12px; border: 1px solid black; cursor: pointer; color: black; text-align: center; vertical-align: middle; font-size: x-small; font-weight: bold;" onclick="' + escapeHTML('handleWikipediaIconOnclick("' + escapeJS(headline['headline_text']) + '");') + '">W</div>';
		html += '<div style="float: left; width: 6px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		html += '</div>';
		html += '<div style="clear: left;"></div>';

		// Article source
		if ( headline['access_status'] != "reg" ) {
			html += '<div class="source" style="padding: 1px;"><a href="' + escapeHTML(headline['document_url']) + '" title="Open article source web page (new window)" onclick="' + escapeHTML('return handleArticleSourceOnclick(this);') + '">' + escapeHTML(headline['source']) + '</a> - ' + escapeHTML(headline['harvest_time']) + '</div>';
		} else {
			html += '<div class="source" style="padding: 1px;"><a href="' + escapeHTML(headline['document_url']) + '" title="Open article source web page (new window)" onclick="' + escapeHTML('return handleArticleSourceOnclick(this);') + '">' + escapeHTML(headline['source']) + '</a> (<a href="' + escapeHTML(headline['access_registration']) + '" title="Open article source registration web page (new window)" onclick="' + escapeHTML('return handleArticleSourceRegOnclick(this);') + '">Reg</a>) - ' + escapeHTML(headline['harvest_time']) + '</div>';
		}

		// Extra article sources
		var extra_sources = headline['extra_sources'];
		if ( extra_sources.length ) {
			for ( var j=0; j < extra_sources.length; j++ ) {
				var extraHeadline = extra_sources[j];
				if ( extraHeadline['access_status'] != "reg" ) {
					html += '<div class="source" style="padding: 1px;"><a href="' + escapeHTML(extraHeadline['document_url']) + '" title="Open article source web page (new window)" onclick="' + escapeHTML('return handleArticleSourceOnclick(this);') + '">' + escapeHTML(extraHeadline['source']) + '</a> - ' + escapeHTML(extraHeadline['harvest_time']) + ' - <span style="font-weight: bold;"><a href="' + escapeHTML(extraHeadline['url']) + '" title="Open article web page (new window)" onclick="' + escapeHTML('return handleArticleHeadlineOnclick(this);') + '">' + escapeHTML(extraHeadline['headline_text']) + '</a></span></div>';
				} else {
					html += '<div class="source" style="padding: 1px;"><a href="' + escapeHTML(extraHeadline['document_url']) + '" title="Open article source web page (new window)" onclick="' + escapeHTML('return handleArticleSourceOnclick(this);') + '">' + escapeHTML(extraHeadline['source']) + '</a> (<a href="' + escapeHTML(extraHeadline['access_registration']) + '" title="Open article source registration web page (new window)" onclick="' + escapeHTML('return handleArticleSourceRegOnclick(this);') + '">Reg</a>) - ' + escapeHTML(extraHeadline['harvest_time']) + ' - <span style="font-weight: bold;"><a href="' + escapeHTML(extraHeadline['url']) + '" title="Open article web page (new window)" onclick="' + escapeHTML('return handleArticleHeadlineOnclick(this);') + '">' + escapeHTML(extraHeadline['headline_text']) + '</a></span></div>';
				}
			}
		}

		// Clear element to ensure enough room for article image
		html += '<div style="clear: both; width: 1px; height: 1px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';

		// Finish headline
		html += '</div>';

		// Add in spacer
		if ( i < headlines.length-1 ) {
			html += '<div style="height: 5px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		}
	}

	// If there were no headlines, display a short no headlines message and try again
	if ( ! headlines.length ) {
		html += '<div class="headline" style="text-align: center;">Sorry, I couldn\'t find any headlines for you. I\'ll try again, shortly.</div>';
		Context['currentCategory']['modifyDate'] = 0;
		clearTimeout(Context['loadNewsRetryTimeout']);
		Context['loadNewsRetryTimeout'] = setTimeout("loadNews();", 15000);
	}

	// Populate headlinesDiv element
	headlinesDiv.innerHTML = html;

	var advertisementsDiv = Context['advertisementsDiv'];
	var html = ''
	for ( var i=0; i < advertisementHeadlines.length; i++ ) {
		var headline = advertisementHeadlines[i];
		html += '<div><span class="headline"><a href="' + escapeHTML(headline['url']) + '" title="Open article web page (new window)" onclick="' + escapeHTML('return handleArticleHeadlineOnclick(this);') + '">' + escapeHTML(headline['headline_text']) + '</a></span> <span class="source"><a href="' + escapeHTML(headline['document_url']) + '" title="Open article source web page (new window)" onclick="' + escapeHTML('return handleArticleSourceOnclick(this);') + '">' + escapeHTML(headline['source']) + '</a> - ' + escapeHTML(headline['harvest_time']) + '</span></div>';
		if ( i < advertisementHeadlines.length-1 ) {
			html += '<div style="height: 5px;"><img src="/skin/default/img/1x1.gif" width="1" height="1" /></div>';
		}

	}
	advertisementsDiv.innerHTML = html;

	// Stop header spinner
	//document.getElementById('headerSpinner').innerHTML = '';

	// Update current time in header/footer
	Context['headerCurrentTime'].innerHTML = new Date(Context['currentCategory']['modifyDate']).toString() + (( Context['currentCategory']['cached'] ) ? ' - Cached' : ' - Fresh!');
	Context['footerCurrentTime'].innerHTML = Context['headerCurrentTime'].innerHTML;
	//---
}
//------------------------------------------------------------------------------
function changeCategory( category ) {
	//---
	// Determine category if not given
	if ( ! category ) {

		var channel = Context['currentChannel'];
		if ( channel['channelName'] == 'Search' ) {
			categry = ( channel['categories'].length ) ? channel['categories'][0] : null;
		} else {
			category = Config['category'][channel['defaultCategoryName']];
			if ( category ) {
				Context['currentCategory'] = category;
			}
		}
	}

	if ( category && category['categoryName'] == 'Search' && Config['channel'][category['channelName']]['categories'].length > 1 ) {
		category = Config['channel'][category['channelName']]['categories'][1];
	}

	if ( category ) {

		// Setup new current category
		Context['currentCategory'] = category;

		// Update screen
		Context['headerCurrentCategoryName'].innerHTML	= category['categoryName'];
		Context['footerCurrentCategoryName'].innerHTML	= category['categoryName'];

		// Mark active category
		setTimeout("markActiveCategory();", 0);

		// Load new headlines
		setTimeout("loadNews();", 0);

		// Track history
		//alert('track: ' + newCategoryName);
		historyName = category['categoryName'].replace(/[^a-zA-Z0-9]/g, '');
		dhtmlHistory.add(historyName, {"categoryName":category['categoryName'],"searchFormKeywords":Context['searchFormKeywords'].value});
		if ( pageTracker ) { setTimeout("try { pageTracker._trackPageview('/" + historyName + "'); } catch ( e ) { alert('Exception ' + e.name + ': ' + e.message); }", 0); }

	} else {
		// Update screen
		Context['headerCurrentCategoryName'].innerHTML	= "No category selected.";
		Context['footerCurrentCategoryName'].innerHTML	= "No category selected.";
	}

	// Scroll to top
	window.scrollTo(0,0);
	//---
}
//------------------------------------------------------------------------------
function changeChannel( channel, category ) {
	//---
	// Default channel if not given
	if ( ! defined(channel) ) {
		channel = Config['channel'][Config['defaultChannelName']];
	}

	// Set new current channel
	Context['currentChannel'] = channel;

	// Determine category name to use
	if ( ! defined(category) ) {
		category = ( channel ) ? Config['category'][channel['defaultCategoryName']] : Config['category'][Config['defaultCategoryName']];
	}

	// Update screen
	Context['headerCurrentChannelName'].innerHTML	= escapeHTML(channel['channelName']);
	Context['footerCurrentChannelName'].innerHTML	= escapeHTML(channel['channelName']);

	// Mark current channel
	setTimeout("markActiveChannel();", 0);

	// Load new categories
	setTimeout("loadCategories();", 0);

	// Change category
	changeCategory(category);

	// Scroll to top
	window.scrollTo(0,0);
	//---
}
//------------------------------------------------------------------------------
function refreshCategory() {
	//---
	var currentCategory = Context['currentCategory'];
	changeCategory(currentCategory);
	//---
}
//------------------------------------------------------------------------------
function refreshChannel() {
	//---
	var currentCategory = Context['currentCategory'];
	var currentChannel = Config['channel'][currentCategory['channelName']];
	changeChannel(currentChannel);
	//---
}
//------------------------------------------------------------------------------
function searchNews( keywords ) {
	//---
	// Create category object 
	//var searchCategory					= {"categoryName":"Search","channelName":"Search","search":true,"keywords":[]};
	var categoryName = 'Search for "' + keywords + '"';
	var categoryBookmarkName = categoryName.replace(/[^a-zA-Z0-9]/g, '');
	var searchCategory = {"categoryName":categoryName,"categoryBookmarkName":categoryBookmarkName,"channelName":"Search","channelBookmarkName":"Search","search":true,"keywords":[keywords]};
	var searchChannel = Config['channel'][searchCategory['channelName']];

	// Remove existing categories if name matches case insensitively
	var categories = Config['channel']['Search']['categories'];
	for ( var i=0; i < categories.length; i++ ) {
		if ( searchCategory['categoryName'].toUpperCase() == categories[i]['categoryName'].toUpperCase() ) {
			categories.splice(i, 1);
		}
	}
	
	// Setup channels/categories with new search category
	Config['category'][searchCategory['categoryName']] = searchCategory;

	// Add our own category
	Config['channel']['Search']['categories'].push(searchCategory);

	// Update client-side state
	var categories = Config['channel']['Search']['categories'];
	for ( var i=0; i < categories.length; i++ ) {
		delete categories[i]['modifyDate'];
		delete categories[i]['cached'];
		delete categories[i]['headlines'];
		delete categories[i]['advertisementHeadlines'];
	}
	var stateStr = JSON.stringify({"searchChannel":Config['channel']['Search']});
	createCookie("state", stateStr, 365);

	// Reload categories
	setTimeout("loadCategories();", 0);

	// Change to search channel
	changeCategory(searchCategory);
	//---
}
//------------------------------------------------------------------------------
function loadChannels() {
	//---
	// Activate loader-spinner
	//var call = function() {
		var channelsDiv = Context['channelsDiv'];
		channelsDiv.innerHTML = '<img src="/skin/default/img/loader-spinner-small.gif" width="16" height="16" title="Loading..." />';
	//};
	//setTimeout(call, 0);

	// Display channels 
	setTimeout("displayChannels();", 0);
	//---
}
//------------------------------------------------------------------------------
function loadCategories() {
	//---
	// Activate loader-spinner
	//var call = function() {
		var categoriesDiv = Context['categoriesDiv'];
		categoriesDiv.innerHTML = '<img src="/skin/default/img/loader-spinner-small.gif" width="16" height="16" title="Loading..." />';
	//};
	//setTimeout(call, 0);

	// Display categories
	setTimeout("displayCategories();", 0);
	//---
}
//------------------------------------------------------------------------------
function displayChannels() {
	//---
	// Remove Search channel
	var channels = [];
	for ( var i=0; i < Config['channels'].length; i++ ) {
		if ( Config['channels'][i]['channelName'] == 'Search' ) { continue; }
		channels[channels.length] = Config['channels'][i];
	}
	var channelsDiv = Context['channelsDiv'];
	var html = ''
	var colors=['#ffce00','#00008b','#008100','#cd0000','#ff6500','#be4c00','#62309c'];
	var colorIndex=0;
	var colorIndexFactor=1;
	for ( var i=0; i < channels.length; i++ ) {
		var channel = channels[i];
		if ( i > 0 && i % 7 == 0 ) {
			html += '<div class="channelSeparatorContainer"><div class="channelSeparatorSubContainer"><img src="/skin/default/img/1x1.gif" alt="1x1.gif" /></div></div>';
		}
		if ( i == 0 ) {
			html += '<div id="' + escapeHTML(channel['channelBookmarkName']) + 'Container" title="Change channel" class="channelButtonContainer" style="margin-top: 0px;" onclick="' + escapeHTML('return handleChannelOnclick(event, "' + escapeJS(channel['channelName']) + '");') + '"><div class="channelButtonSubContainer" style="border-left: 5px solid ' + colors[colorIndex] + ';"><span class="channelButtonText">' + escapeHTML(channel['channelName']) + '</span></div></div>';
		} else {
			html += '<div id="' + escapeHTML(channel['channelBookmarkName']) + 'Container" title="Change channel" class="channelButtonContainer" onclick="' + escapeHTML('return handleChannelOnclick(event, "' + escapeJS(channel['channelName']) + '");') + '"><div class="channelButtonSubContainer" style="border-left: 5px solid ' + colors[colorIndex] + ';"><span class="channelButtonText">' + escapeHTML(channel['channelName']) + '</span></div></div>';
		}
		colorIndex += 1 * colorIndexFactor;
		if ( colorIndex > colors.length-1 ) {
			colorIndex = colors.length-1;
			colorIndexFactor=-1;
		} else if ( colorIndex < 0 ) {
			colorIndex = 0;
			colorIndexFactor=1;
		}
	}
	html += '<div class="channelSeparatorContainer"><div class="channelSeparatorSubContainer"><img src="/skin/default/img/1x1.gif" alt="1x1.gif" /></div></div>';
	html += '<div id="' + escapeHTML('Search') + 'Container" title="Change channel" class="channelButtonContainer" onclick="' + escapeHTML('return handleChannelOnclick(event, "' + escapeJS('Search') + '");') + '"><div class="channelButtonSubContainer" style="border-left: 5px solid ' + colors[0] + ';"><span class="channelButtonText">' + escapeHTML('Search') + '</span></div></div>';
	channelsDiv.innerHTML = html;
	//---
}
//------------------------------------------------------------------------------
function displayCategories() {
	//---
	var categoriesDiv = Context['categoriesDiv'];
	var html = ''
	var channel = Context['currentChannel'];
	var categories = channel['categories'];
	if ( categories.length ) {
		for ( var i=0; i < categories.length; i++ ) {
			var category = categories[i];
			if ( i > 0 && i % 7 == 0 ) {
				html += '<div class="categorySeparatorContainer"><div class="categorySeparatorSubContainer"><img src="/skin/default/img/1x1.gif" alt="1x1.gif" /></div></div>';
			}
			if ( i == 0 ) {
				html += '<div id="' + escapeHTML(category['channelBookmarkName'] + category['categoryBookmarkName']) + 'Container" title="Change category" class="categoryButtonContainer" style="margin-top: 0px;" onclick="' + escapeHTML('return handleCategoryOnclick(event, "' + escapeJS(category['categoryName']) + '");') + '"><div class="categoryButtonSubContainer"><span class="categoryButtonText">' + escapeHTML(category['categoryName']) + '</span></div></div>';
			} else {
				html += '<div id="' + escapeHTML(category['channelBookmarkName'] + category['categoryBookmarkName']) + 'Container" title="Change category" class="categoryButtonContainer" onclick="' + escapeHTML('return handleCategoryOnclick(event, "' + escapeJS(category['categoryName']) + '");') + '"><div class="categoryButtonSubContainer"><span class="categoryButtonText">' + escapeHTML(category['categoryName']) + '</span></div></div>';
			}
		}
	} else {
		if ( channel['channelName'] == "Search" ) {
			html = '<div style="text-align: center; border-top: 1px solid gray; border-bottom: 1px solid gray;">Please perform a search.</div>';
		} else {
			html = '<div style="text-align: center; border-top: 1px solid gray; border-bottom: 1px solid gray;">Sorry, there are no categories available yet.</div>';
		}
	}
	categoriesDiv.innerHTML = html;
	//---
}
//------------------------------------------------------------------------------
var _markActiveChannel_currentActive=null;
function markActiveChannel( calls ) {
	if ( ! defined(calls) ) { calls=1; }
	//---
	if ( calls > 50 ) { return; }
	var currentChannel = Context['currentChannel'];
	var channelBookmarkName = currentChannel['channelBookmarkName'];
	var channelButtonContainer = document.getElementById(channelBookmarkName + 'Container');
	if ( channelButtonContainer ) {
		if ( _markActiveChannel_currentActive ) {
			_markActiveChannel_currentActive.className = 'channelButtonContainer';
		}
		channelButtonContainer.className = 'channelButtonContainerActive';
		_markActiveChannel_currentActive = channelButtonContainer;
	} else {
		setTimeout(function() { markActiveChannel(calls+1); }, 100);
	}
	//---
}
//------------------------------------------------------------------------------
var _markActiveCategory_currentActive=null;
function markActiveCategory( calls ) {
	if ( ! defined(calls) ) { calls=1; }
	//---
	if ( calls > 50 ) { return; }
	var currentCategory = Context['currentCategory'];
	var channelBookmarkName = currentCategory['channelBookmarkName'];
	var categoryBookmarkName = currentCategory['categoryBookmarkName'];
	var categoryButtonContainer = document.getElementById(channelBookmarkName + categoryBookmarkName + 'Container');
	if ( categoryButtonContainer ) {
		if ( _markActiveCategory_currentActive ) {
			_markActiveCategory_currentActive.className = 'categoryButtonContainer';
		}
		categoryButtonContainer.className = 'categoryButtonContainerActive';
		_markActiveCategory_currentActive = categoryButtonContainer;
	} else {
		setTimeout(function() { markActiveCategory(calls+1); }, 100);
	}
	//---
}
//------------------------------------------------------------------------------

