/**
 * Global variables
 */
var idTax = 8;	// APNC story taxonomy vid
var idLocation = 9;	// APNC locations taxonomy vid
var sURL = 'http://apnc.abcidd.com/services/json';
// var sURL = 'http://newsradio.local/drupal-5.7/services/json';
var sPath = '';	// '/abcaustralianetwork';
var sNewsPath = '';	// sPath + '/news';
var sRSSHost = 'http://apnc.abcidd.com/';

var iTimeout = 15000;	// ajax timeout in milliseconds

var scroller;	// smooth scrolling
var hist;	// ajax history manager

var gTid;	// holder for related stories tid

/**
 * Search object
 * 
 * Maintains all search parameters within the page. Also keeps a local cache of search data for maintaining
 * dynamic pagination results within the same page. 
 */
var Search = $H({
	term: '',
	defaultTerm: '',
	page: 0,
	setTerm: function(term) {
		if (this.term == '')
			this.defaultTerm = term;
		this.term = term;
	},
	setPage: function(page) {
		this.page = page - 1;
		if (this.page < 0)
			this.page = 0;
	},
	linkPage: function() {
		// for visual display
		return parseInt(this.page) + 1;
	},
	addCache: function(json) {
		if (!this.cache)
			this.cache = new Array();
		if (!this.cache[this.term])
			this.cache[this.term] = new Array();
		this.cache[this.term][this.page] = json;
	},
	getCache: function() {
		if (this.cache && this.cache[this.term]) {
			return this.cache[this.term][this.page];
		}
	}
});

/**
 * Display an ajax error message in standard format 
 * 
 * @param string sTag The element to be injected into
 */
function showError(sTag) {
	new Element('p', {
		'text': "Sorry, we're experiencing technical difficulties with our site at the moment. Please try again later.",
		'class': 'error'
	}).inject($(sTag));
}

/**
 * Parse query parameters for the search page and set them within the Search object, update interface elements where required
 * and perform the search where required.
 */
function getQueryParams() {

	// if we have a search term in the QS, search for it now
	var qs = window.location.href.toQueryObject();
	if (window.location.hash) {
		var page = window.location.hash.substring(window.location.hash.length - 1);
		if (parseInt(page) > 0) 
			Search.setPage(page);
	}
	if (qs && qs.q) {
		// also check for and set other filters
		if (qs.locn) 
			Search.locn = unescape(qs.locn);
		if (qs.subj) 
			Search.subj = unescape(qs.subj);
		if (qs.dateAfter) {
			Search.dateAfter = unescape(qs.dateAfter);
			$('dtFrom').value = Search.dateAfter;
			$('ysfDates').set('text', Search.dateAfter);
			$('divYSFDates').removeClass('hide');
		}
		if (qs.dateBefore) {
			Search.dateBefore = unescape(qs.dateBefore);
			$('dtTo').value = Search.dateBefore;
			if ($('ysfDates').get('text') != '') 
				$('ysfDates').appendText(' - ');
			$('ysfDates').appendText(Search.dateBefore);
			$('divYSFDates').removeClass('hide');
		}
		if (qs.reporter) {
			Search.reporter = unescape(qs.reporter);
			$('apncSub').value = Search.reporter;
			$('ysfReporter').set('text', unescape(qs.reporter));
			$('divYSFReporter').removeClass('hide');
		}
		if (qs.dateSort) {
			Search.dateSort = unescape(qs.dateSort);
			if (qs.dateSort == 'ASC') {
				$('aSortOld').addClass('current');
			}
			else 
				if (qs.dateSort == 'DESC') {
					$('aSortNew').addClass('current');
				}
		}
		else 
			$('aSortMatch').addClass('current');
		$('ysfTerms').set('text', unescape(qs.q));
		$('divYSFTerms').removeClass('hide');
		$('query').value = unescape(qs.q);
		doSearch($('query').value);
	}
	else {
		$('apncSub').removeClass('hide');
		// get popular search terms
		getPopularSearches();
	}

}

/**
 * Create a standard teaser listing for a story object.
 * 
 * @param {Object} story JSON story object as returned from archive
 * @param {Object} id ID to assign to div element
 */
function createStoryListing(story, id) {
	
	try {

		var node = story.node?story.node:story;
		var template = getTemplate(node);
		var feature = template == 'APNC FeatureStory';
	
		// create div
		var div = new Element('div', {
			'class': 'item',
			'id': id
		});
	
		// title
		// var href = feature?'feature':'story';
		var qsf = feature?'&feature':'';
		var a = new Element('a', {
			'id': node.nid,
			'href': sNewsPath+'/story.htm?id=' + node.nid + qsf,
			'text': story.title
		});
		var h2 = new Element('h2');
		if (feature)
		h2.appendText('FEATURE: ');
		h2.adopt(a).inject(div);
	
		// get media icons
		var media = getMedia(node);
		if (media.content) {
			if (media.video) {
				new Element('img', {
					'src': sPath+'/img/common/icon_video.gif',
					'alt': 'Video',
					'title': 'Video',
					'class': 'mediaIcon'
				}).inject(h2);
			}
			if (media.audio) {
				new Element('img', {
					'src': sPath+'/img/common/icon_audio.gif',
					'alt': 'Audio',
					'title': 'Audio',
					'class': 'mediaIcon'
				}).inject(h2);
			}
			if (media.image) {
				new Element('img', {
					'src': sPath+'/img/common/icon_photo.gif',
					'alt': 'Photo',
					'title': 'Photo',
					'class': 'mediaIcon'
				}).inject(h2);
			}
		}
	
		// date time
		var sDate = getDateString(node.created);
		var p = new Element('p', {
			'class': 'dateTime',
			'text': sDate
		});
		sAuthor = getReporter(node);
		if (sAuthor)
			p.appendText(' ' + sAuthor);
		p.inject(div);
		
		// teaser
		var sTeaser = node.body_value?node.body_value:node.teaser;
		new Element('div',{
			'html': shrinkTeaser(sTeaser)
		}).inject(div);
		
		/* No tags
		// tags
		var ul = new Element('ul');
		var first = new Element('li', {
			'class': 'first',
			'html': '<strong>Tags</strong>'
		}).inject(ul);
		$each(node.taxonomy, function(bit) {
			if (bit.vid == idTax && bit.name != '') {	// RSS tags
				var a = new Element('a', {
					'href': sNewsPath+'/tags.htm?tag=' + bit.name,
					'text': bit.name
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject(ul);
			} else if (bit.vid == idLocation && bit.name != '') {	// Primary Location
				var a = new Element('a', {
					'href': sNewsPath+'/tags.htm?tag=' + bit.name,
					'text': bit.name,
					'class': 'location'
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject(first, 'after'); // add first in list - after heading of course 
			}
		});
		ul.inject(div);
		*/
		
	} catch (e) {
		
	}
	
	return div;

}

/**
 * Update title and other meta tags by replacing {title} field with passed string
 * 
 * @param String sMeta Content to replace with
 */
function updateMeta(sMeta) {
	document.title = document.title.substitute({title: sMeta});
	var meta = document.getElement("META[name=Title]");
	if (meta)
		meta.content = meta.content.substitute({title: sMeta});
	meta = document.getElement("META[name=DC.title]");
	if (meta)
		meta.content = meta.content.substitute({title: sMeta});
}

/**
 * Create a teaser from the supplied text. Return everything up until the first full stop character - '.'
 * If not present, return it all.
 * 
 * @param String text Text to be shrunk.
 */
function shrinkTeaser(text) {
	var arr = text.split('.');
	var s = arr[0] + '.';
	return s.replace(/<p>/, '');
}

/**
 * Callback for getAllTags ajax call. Parse and present the tag list.
 * 
 * @param object json Collection of tag objects as returned from the archive.
 */
function gotAllTags(json) {
	
	if (!json.status) {
		// we have bad data
		showError('tagBlocks');
		$('loading').addClass('hide');
		return false;
	}
	
	var list = json.data;
	
	// get some HTML elements first
	var ulNav = $$('#allTagsNav ul');
	
	// a flag to indiciate we're on the first letter
	var bFirst = true;
	
	// for each letter of the alphabet, create the link and the tag list
	$each(list, function(tags, letter) {
		
		// create the nav links
		// <li id="navA"><a href="#navA">A</a></li>
		var sNav = 'nav' + letter;
		var a = new Element('a', {
			'id': letter,
			'href': '#',
			'text': letter,
			'events': {
        'click': function(){
					var s = this.id;
					// update the headline letter
					$$('#subHeadlinesBar h1').set('text', s);
					// hide the other blocks
					$$('.tagList').each(function(div){
						if (!div.hasClass('hide')) {
							// hide it immediately
							div.addClass('hide');
						}
					})
					// show the block for this letter
					var block = $('tagItems'+s);
					block.removeClass('hide');
					// block.fade('in');
					this.blur();
         	return false;
        }
			}
		});
		var li = new Element('li', {'id': sNav}).adopt(a);
		li.appendText(' | ');
		ulNav.adopt(li);
		
		// create the tag lists
		var div = new Element('div', {
			'id': 'tagItems'+letter,
			'class': 'tagList'
		});
		
		// for each tag in the list, create the link
		var sClass = 'odd';
		$each(tags, function(tag, idx) {
			var h4 = new Element('h4', {'class': sClass});
			new Element('a', {
				'href': sNewsPath+'/tags.htm?tag=' + tag.name,
				'text': tag.name
			}).inject(h4);
			div.adopt(h4);
			sClass = (sClass=='odd')?'':'odd';
		});
		
		// if not the first, hide the div
		if (!bFirst) {
			// div.fade('hide');
			div.addClass('hide');
		}
		
		// add div to block
		$('tagBlocks').adopt(div);
		
		// set bFirst
		bFirst = false;
		
	});
	
	$('loading').addClass('hide');
}

/**
 * Call back for getRelatedItems ajax call.
 * 
 * @param object json Collection of related item objects as returned from the archive.
 */
function gotRelatedItems(json){
	
	if (!json.status) {
		// we have bad data - hide the div
		return false;
	}
	
	if (!window.nid)	// check that we have a valid nid - we won't if it's a static page
		var nid = 0;
	else
		var nid = window.nid;
	
	var iStories = 0;
	// for each story, create the HTML - up to 5 stories
	$each(json.data, function(story, idx) {
		if (iStories < 5 && story.nid != nid) {	// if not this story!

			// get the location taxonomy item
			if (iStories == 0 && !$('ancRelated').get('updated')) {
				var tax = getPrimaryLocation(story);
				if (tax) {
					$('ancRelated').set('html', tax.name.toUpperCase() + ' STORIES');
					$('ancRelated').set('href', sNewsPath+'/tags.htm?tag=' + tax.name);
				}
			}
		
			var div = new Element('div', {
				'class': 'item',
				'id': 'i' + idx
			});
			
			// h4 & link
			var a = new Element('a', {
				'id': story.nid,
				'href': sNewsPath+'/story.htm?id=' + story.nid,
				'html': story.title
			});
			var h4 = new Element('h4').adopt(a).inject(div);
			
			// get media icons
			var media = getMedia(story);
			if (media.video) {
				new Element('img', {
					'src': sPath+'/img/common/icon_video.gif',
					'alt': 'Video',
					'title': 'Video',
					'class': 'mediaIcon'
				}).inject(h4);
			}
			if (media.audio) {
				new Element('img', {
					'src': sPath+'/img/common/icon_audio.gif',
					'alt': 'Audio',
					'title': 'Audio',
					'class': 'mediaIcon'
				}).inject(h4);
			}
			if (media.image) {
				new Element('img', {
					'src': sPath+'/img/common/icon_photo.gif',
					'alt': 'Photo',
					'title': 'Photo',
					'class': 'mediaIcon'
				}).inject(h4);
			}

			$('headlinesCol_Right').adopt(div);
			$('ancRelated').removeClass('hide');
			iStories++;
		}
	});
	
}

/**
 * Callback function from getTagStories ajax call. Parse and present the story list.
 * 
 * @param object json Collection of story objects as returned by the archive.
 */
function gotStoryList(json) {

	if (!json.status) {
		// we have bad data
		showError('headlines');
		// hide loading
		$('loading').addClass('hide');
		return false;
	}

	if (!json.data) {
		// create div
		var div = new Element('div', {
			'class': 'item'
		});
		// teaser
		new Element('div',{
			'html': 'Sorry, no stories were found for this tag.'
		}).inject(div);
		// add div to block
		$('headlines').adopt(div);
	} else {
		var list = $('results_list');
		// for each story, create the HTML
		$each(json.data.nodes, function(story, idx) {
			// create story teaser div
			var div = createStoryListing(story, 'id'+idx);
			// add div to block
			list.adopt(div);
		});
	}

	// pagination
	var pager = json.data.pager;
	
	// get list
	var ul = $('ulPages');
	var first = document.getElement('#pager2 #first');
	var previous = document.getElement('#pager2 #previous');
	var next = document.getElement('#pager2 #next');
	var last = document.getElement('#pager2 #last');
	// clear lists
	ul.innerHTML = '';
	
	if (pager) {
		
		// create the new query string
		var qs = window.location.href.toQueryObject();
		var sTag = qs.tag?qs.tag:'';
		var sQS = '?tag=' + sTag + '&page=';
		
		if (pager.first) {
			var num = parseInt(pager.first.page) + 1;
			first.set('href', sQS + num);
			first.removeClass('hide');
		} else
			first.addClass('hide');
		if (pager.previous) {
			var num = parseInt(pager.previous.page) + 1;
			previous.set('href', sQS + num);
			previous.removeClass('hide');
		} else
			previous.addClass('hide');
		$each(pager.list, function(page) {
			var li = createPageLi(page.text, page.page, page.current, sQS);
			ul.adopt(li);
		});
		if (pager.next) {
			var num = parseInt(pager.next.page) + 1;
			next.set('href', sQS + num);
			next.removeClass('hide');
		} else
			next.addClass('hide');
		if (pager.last) {
			var num = parseInt(pager.last.page) + 1;
			last.set('href', sQS + num);
			last.removeClass('hide');
		} else
			last.addClass('hide');

		showPagers();
	} else {
		hidePagers();
	}

	// hide loading
	$('loading').addClass('hide');
	
}

/**
 * Parse multimedia elements from a story element.
 * 
 * @param object story Json object for story.
 * @return array Array of multimedia elements
 */
function getMedia(story) {

	var media = {};
	
	if (story.field_apnc_multimedia) {
		
		// now, we need to check the date - only supply media within 3 month window
		dt = new Date(story.created * 1000);
		dtNow = new Date();
		if (dtNow.getTime() - dt.getTime() <= 46656000000) {	// less then 18 months => 540 days
			media.content = true;
			// do some parsing and set some properties
		  $each(story.field_apnc_multimedia, function(el) {
				if (!media[el.m_type])
					media[el.m_type] = new Array();
					media[el.m_type].push(el);
			});
		}
		
	}
	
	return media;
	
}

/**
 * Parse node for reporter field
 * 
 * @param object node Field from story node
 * @return string Reporter name if present
 */
function getReporter(node) {
	try {
		if (node.field_apnc_reporter && node.field_apnc_reporter[0] && node.field_apnc_reporter[0].value)
			return node.field_apnc_reporter[0].value;
	} catch(e) {
		// ignore it
	}
}

/**
 * Parse node for template field
 * 
 * @param object node Field from story node
 * @return string Template name if present
 */
function getTemplate(node) {
	try {
		if (node.field_apnc_template_name && node.field_apnc_template_name[0] && node.field_apnc_template_name[0].value)
			return node.field_apnc_template_name[0].value;
	} catch(e) {
		// ignore it
	}
}

/**
 * Get primary location taxonomy object from story node
 * 
 * @param node story Story node containing taxonomy array
 */
function getPrimaryLocation(story) {
	var ret;
	$each(story.taxonomy, function(tax) {
		if (!ret && tax.vid == idLocation && tax.name != '') {
			if (gTid && gTid != '') {
				if (gTid == tax.tid)
					ret = tax;
			} else
				ret = tax;
		}
	});
	return ret;
}

/**
 * Format a Created date time p tag for a related media element
 * 
 * @param object el Media element (audio or video)
 */
function formatMediaDate(el) {
	// date time
	if (el.date != '') {
		var sDate = getMediaDateString(el.date);
		var p = new Element('p', {
			'class': 'dateTime',
			'text': 'Created: '
		});
		new Element('span', {
			'class': 'timestamp',
			'text': sDate
		}).inject(p);
		return p;
	}
}

/**
 * Callback function for getStory ajax call. Parse and present the story elements.
 * 
 * @param object json Story object as returned by the archive.
 */
function gotStory(json) {
	
	if (!json.status) {
		// we have bad data
		showError('divContent');
		$('loading').addClass('hide');
		return false;
	}
	
	// get the node data
	var story = json.data;
	
	// get related stories
	var bDone = false;
	var tax = getPrimaryLocation(story);
	if (tax)
		getRelatedItems(tax.tid);
	
	// first, update the title
	$$('#headlinesBar h1').set('text', story.title);
	// update Meta content
	updateMeta(story.title);
	
	// update the HTML
	
	// query string parameters
	//var qs = window.location.href.toQueryObject();
	//var feature = qs.feature;
	
	var node = story.node?story.node:story;
	var template = getTemplate(node);
	var feature = template == 'APNC FeatureStory';
	
	if (feature) {
		$$('#headlinesBar h1').appendText('FEATURE: ','top');
	}

	// apncSub
	if (story.field_apnc_reporter && story.field_apnc_reporter[0] && story.field_apnc_reporter[0]['value'] != '') {
		new Element('strong', {
			'html': story.field_apnc_reporter[0]['value']
		}).inject($('apncSub'));
		$('apncSub').removeClass('hide');
	}
	
	// feature Intro
	if (feature && story.teaser) {
	//$('largeIntro').inject('html': story.teaser);
	new Element('p', {'html': story.field_apnc_intro[0]['value']}).inject($('largeIntro'));
	//$('largeIntro').set('html', story.field_apnc_intro[0]['value']);
	}
	else {
	$('largeIntro').addClass('hide');
	}
	
	// related content
	var media = getMedia(story);
	if (media.content) {
		
		var bAdded = false;
				
		// photos
		if (media.image) {
			gallery_array = new Array('0');
			var bFirst = false;
			$each(media.image, function(img, idx) {
				if (img.url) {
					if (!bFirst) {
						// first
						$('sspPhotoImg').src = img.url;
						$('sspPhotoImg').alt = img.caption;
						$('sspPhotoImg').title = img.caption;
						$('sspPhotoCaption').set('text', img.caption);
						bFirst = true;
					}
					gallery_array.push(img.url);
					gallery_array.push(img.caption);
					// gallery_array.push(img.source);
				}
			});
			if (bFirst) { // we have at least one photo with a url
				$('relatedPhoto').removeClass('hide');
				bAdded = true;
			}
		}
		
		// video
		if (media.video) {
			$each(media.video, function(vid, idx) {
				var sURL = vid.url;
				if (sURL.contains('http://mpegmedia/'))
					sURL = sURL.replace(/mpegmedia/, 'mpegmedia.abc.net.au');
				var div = new Element('div', {
					'class': 'relatedItem'
				});
				
				if (vid.source && vid.source != '')
				{
				vSource = ' from ' + vid.source;
				}
				else
				{
				vSource = '';
				}
				
				div.adopt(
					new Element('h5', {
						'text': 'VIDEO' + vSource
					})
					);

				var sTitle = vid.title;
				var a = new Element('a', {
					'href': sURL,
					'text': sTitle,
					'events': {
						'click': relVidClick
					}
				});
				new Element('h4').adopt(a).inject(div);

				var p = formatMediaDate(vid);
				if (p) p.inject(div);
				
				div.inject($('relatedVideo'));
			});	// each
			$('relatedVideo').removeClass('hide');
			bAdded = true;
		}
		
		// audio
		if (media.audio) {
			$each(media.audio, function(aud, idx) {
				var div = new Element('div', {
					'class': 'relatedItem'
				});
				
				
				if (aud.source && aud.source != '')
				{
				aSource = ' from ' + aud.source;
				}
				else
				{
				aSource = '';
				}
				
				div.adopt(
					new Element('h5', {
						'text': 'AUDIO' + aSource
					})
				);
				var a = new Element('a', {
					'href': aud.url,
					'text': aud.title,
					'events': {
						'click': relAudioClick
					}
				});
				new Element('h4').adopt(a).inject(div);

				var p = formatMediaDate(aud);
				if (p) p.inject(div);

				div.inject($('relatedAudio'));
			});	// each
			$('relatedAudio').removeClass('hide');
			bAdded = true;
		}

		if (bAdded) {
			$('headlinesRelated').removeClass('hide');
		} else {
			$('headlinesRelated').setStyle('display', 'none');
		}
			
	}
	
	// last updated
	var sUpdated = getDateString(story.created);
	$('dtUpdated').set('text', sUpdated);
	if (story.body == '')
	{
		$('divContent').set('html', story.teaser);
	}
	else
	{
		$('divContent').set('html', story.body);
	}
	$('loading').addClass('hide');
	
	// tags
	var ul = $('ulTags');

	if (story.taxonomy) {
		// just doing it twice now, first only for locations
		$each(story.taxonomy, function(bit) {
			if (bit.vid == idLocation && bit.name != '') {	// Primary Location
				var a = new Element('a', {
					'href': sNewsPath+'/tags.htm?tag=' + bit.name,
					'text': bit.name,
					'class': 'location'
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject(ul); 
			}
		});
		// second time for everything else
		$each(story.taxonomy, function(bit) {
			if (bit.vid == idTax && bit.name != '') {	// RSS tags
				var a = new Element('a', {
					'href': sNewsPath+'/tags.htm?tag=' + bit.name,
					'text': bit.name
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject(ul);
			}
		});
	}	// if tags
	
	// inject 'Tags' heading at top of list
	var first = new Element('li', {
		'class': 'first',
		'html': '<strong>Tags:</strong>'
	}).inject(ul, 'top');

	if (window.doLoad)
		window.doLoad();
	
}

/**
 * Return a formatted date string from a timestamp
 * 
 * @param timestamp ts
 */
function getDateString(ts) {
	var dt = new Date(ts * 1000);
	if (typeof serverGMTDateTime != 'undefined') {
  		var today = new Date(serverGMTDateTime);
		var myoffsetdate = today.toString();
		var myoffsetpos = myoffsetdate.indexOf("+") + 1;
		var myoffset = myoffsetdate.substring(myoffsetpos, myoffsetpos + 2);
		today.setHours(today.getHours() + myoffset);
	}
	if (!today || today.toString() == 'Invalid Date') { var today = new Date(); };
	var sDate = getTimeDiffInWords(today, dt);
	if (!sDate) { sDate = dt.format('%a, %d %b %Y %H:%M:%S %T'); };
	return sDate;
}

/**
 * Return a formatted date string from media date field
 * 
 * @param string sDate
 */
function getMediaDateString(sDate) {
	// sDate = 24/11/08
	var arr = sDate.split('/');
	if (arr[2].length == 2) { arr[2] = '20' + arr[2]; };
	var dt = new Date(arr[2], arr[1]-1, arr[0]);
	if (typeof serverGMTDateTime != 'undefined') {
  		var today = new Date(serverGMTDateTime);
	};
	if (!today || today.toString() == 'Invalid Date') { var today = new Date(); };
	var sDate = getTimeDiffInWords(today, dt);
	if (!sDate) { sDate = dt.format('%a, %d %b %Y %H:%M:%S %T'); };
	return sDate;
}

/**
 * Inline onclick event for related video items.
 */
function relVidClick() {
	if (typeof showVideo == 'function') 
		return showVideo(this.href, this, '100%', true);
}

/**
 * Inline onclick event for related audio items.
 */
function relAudioClick() {
	if (typeof showAudio == 'function') 
		return showAudio(this.href, this, '100%', true);	
}

/**
 * Callback function for doSearch. Parse and display result list.
 * 
 * @param object json Results as returned from the archive.
 */
function gotSearchResults(json) {
	
	Search.addCache(json);

	if (!json.status) {
		// we have bad data
		showError('results_list');
		$('loading').addClass('hide');
		$('loadSpacer').addClass('hide');
		return false;
	}
	
	var data = json.data;
	if (data.total == 0) {
		// no results!
		$('ysfMsg').set('text', 'Sorry, we were unable to find:');
		$('pFilter').addClass('hide');
		getPopularSearches();
	} else {
		
		$('ysfMsg').set('text', 'You searched for:');
		
		// list location taxonomy
		if (!Search.locations) {
			Search.locations = data.locations;
			$each(data.locations, function(locn, tid) {
				var a = new Element('a', {
					'text': locn.name + ' ('+ locn.count +')',
					'href': 'search.htm?' + getQS() + '&locn=' + tid
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject('ulLocations');
			});
		}

		// list subject taxonomy
		if (!Search.subjects) {
			Search.subjects = data.subjects;
			$each(data.subjects, function(subject, tid) {
				var a = new Element('a', {
					'text': subject.name + ' ('+ subject.count +')',
					'href': 'search.htm?' + getQS() + '&subj=' + tid
				});
				new Element('li', {
					'class': 'tag_item'
				}).adopt(a).inject('ulSubjects');
			});
		}
		
		$('filter_section').removeClass('hide');
		$('searchSort').removeClass('hide');
		
		if (Search.locn || Search.subj) {
			if (Search.subj)
				$('spanFilter').set('text', 'Subject: ' + Search.subjects[Search.subj].name);
			else
				$('spanFilter').set('text', 'Location: ' + Search.locations[Search.locn].name);
			$('pFilter').removeClass('hide');
			$('ancFilter').addEvent('click', function(event) {
				event.stop();
				location.href="search.htm?" + getQS(true);	// pass true to remove filter from QS
			});
		}

		data.results.each(function(story, idx) {
			
			// create story teaser div
			var div = createStoryListing(story, 'id'+idx);
			
			// add to results listing
			$('results_list').adopt(div);

		});
		$('results_list').removeClass('hide');

		// update total
		var sTotal = 'Showing ' + data.from + '-' + data.to + ' of ' + data.total + ' results';
		$('h4Results').set('text', sTotal);
		$('results_totals').removeClass('hide');
		
		// pagination
		var pager = data.pager;
		
		// get list
		var ul = $('ulPages');
		// var ul2 = $('ulPages2');
		var first = document.getElement('#pager1 #first');
		var previous = document.getElement('#pager1 #previous');
		var next = document.getElement('#pager1 #next');
		var last = document.getElement('#pager1 #last');
		// clear lists
		ul.innerHTML = '';
		// ul2.innerHTML = '';
		if (pager) {
			if (pager.first) {
				var num = parseInt(pager.first.page) + 1;
				first.set('href', '#page' + num);
				first.removeClass('hide');
			} else
				first.addClass('hide');
			if (pager.previous) {
				var num = parseInt(pager.previous.page) + 1;
				previous.set('href', '#page' + num);
				previous.removeClass('hide');
			} else
				previous.addClass('hide');
			$each(pager.list, function(page) {
				var li = createPageLi(page.text, page.page, page.current);
				ul.adopt(li);
			});
			if (pager.next) {
				var num = parseInt(pager.next.page) + 1;
				next.set('href', '#page' + num);
				next.removeClass('hide');
			} else
				next.addClass('hide');
			if (pager.last) {
				var num = parseInt(pager.last.page) + 1;
				last.set('href', '#page' + num);
				last.removeClass('hide');
			} else
				last.addClass('hide');
			
			// duplicate
			/*
			ul.getElements('li').each(function(li){
				li.clone().inject(ul2);
			});
			*/
			$('pager2').innerHTML = $('pager1').innerHTML;

			showPagers();
		} else {
			hidePagers();
		}

	}
	
	$('loading').addClass('hide');
	$('loadSpacer').addClass('hide');

}

/**
 * Hide pagination elements.
 */
function hidePagers() {
	$$('.results_links').each(function(el){
		el.addClass('hide');
	});
}

/**
 * Show pagination elements.
 */
function showPagers() {
	$$('.results_links').each(function(el){
		el.removeClass('hide');
	});
}

/**
 * Create pagination elements.
 * 
 * @param string text Link text
 * @param string href Link href
 * @param boolean current True if this is the current page
 */
function createPageLi(text, href, current, qs) {
	var num = parseInt(href) + 1;
	var li = new Element('li');
	if (current) {
		new Element('span', {
			'text': text
		}).inject(li);
	} else {
		if (!qs)
			qs = '#page';
		var a = new Element('a', {
			'text': text,
			'id': num,
			'href': qs+num,
			'events': {
				'click': function(event) {
					Search.setPage(this.id);
					doSearch(Search.term);
					event.stop();
					this.blur();
					return false;
				}
			}
		});
		li.adopt(a);		
	}
	li.appendText(' | ');
	return li;
}

/**
 * Function attached to ajax history manager. Used to store details into the Search object.
 * 
 * @param string hash New page url hash e.g.: #page1
 */
function historyChange(hash) {
	var page = window.location.hash;
	page = page.substring(page.length-1);
	if (page) {
		Search.setPage(page);
		doSearch(Search.term);
	} else {
		Search.page = 0;
		doSearch(Search.defaultTerm);
	}
}

/**
 * Perform the search on the terms provided. Other parameters are provided from the Search object.
 * 
 * @param string sTerm Search terms
 */
function doSearch(sTerm) {
	
	// scroll into view
	scroller.toElement('headlinesBar').start();
	
	// set terms to search hash
	Search.setTerm(sTerm);
	
	// empty existing items
	$$('.item').each(function(item){
		item.dispose();
	});

	// check for cache
	if (Search.getCache()) {

		gotSearchResults(Search.getCache());

	} else {

		// show loading
		$('loadSpacer').removeClass('hide');
		$('loading').removeClass('hide');
		new JsonP(sURL, {
			data: {
				'method': 'search.apncSearchNodes',
				'search_keys': Search.term,
				'page': Search.page,
				'locn': Search.locn,
				'subj': Search.subj,
				'dateAfter': Search.dateAfter,
				'dateBefore': Search.dateBefore,
				'dateSort': Search.dateSort,
				'reporter': Search.reporter
			},
			onComplete: gotSearchResults,
			timeout: iTimeout,
			onTimeout: function() {
				showError('results_list');
				$('loading').addClass('hide');
				$('loadSpacer').addClass('hide');
			}
		}).request();
		
	}

	// history management
	var iPage = Search.linkPage();
	if (iPage > 1)
		hist.addState('page' + iPage);
	
}

/**
 * Ajax function to retrieve all tags list from the archive.
 */
function getAllTags() {
	new JsonP(sURL, {
		data: {
			'method': 'taxonomy.apnc_getTree',
			'vid': idTax + ',' + idLocation	// RA RSS taxonomy
		},
		onComplete: gotAllTags,
		timeout: iTimeout,
		onTimeout: function() {
			showError('tagBlocks');
			$('loading').addClass('hide');
		}
	}).request();
}

/**
 * Ajax function to get a list of stories for a given tag from the archive.
 * 
 * @param string sTag Tag to retrieve for
 */
function getTagStories(sTag) {

	// make the request
	var qs = window.location.href.toQueryObject();
	var sPage = qs.page?qs.page:'1';
	sPage = sPage - 1;
	new JsonP(sURL, {
		data: {
			'method': 'taxonomy.apnc_selectNodesByName',
			'term': sTag,
			'page': sPage
		},
		onComplete: gotStoryList,
		timeout: iTimeout,
		onTimeout: function() {
			showError('headlines');
			$('loading').addClass('hide');
		}
	}).request();

	// update the RSS feed link
	var bRegion = sTag.contains('region:'); 

	// update heading
	sTag = sTag.replace(/region:/, '');
	$$('#headlinesBar h1').set('text', sTag.toUpperCase());
	
	// update RSS link
	var sRSS = sRSSHost + 'tags/';
	if (bRegion) sRSS += 'region/';
	sRSS += sTag + '/feed';
	$('ancRSS').set('href', sRSS);

	// update Meta content
	updateMeta(sTag);

}

/**
 * Ajax function to retrieve a story for a given id.
 *  
 * @param string nid Story id
 */
function getStory(nid) {
	new JsonP(sURL, {
		data: {
			'method': 'node.apnc_load',
			'nid': nid
		},
		onComplete: gotStory,
		timeout: iTimeout,
		onTimeout: function() {
			showError('divContent');
			$('loading').addClass('hide');
		}
	}).request();
}

/**
 * Ajax function to retrieve related items for a given taxonomy id. This is taken from the story object.
 * 
 * @param string tid Taxonomy id to retrieve for
 */
function getRelatedItems(tid) {
	gTid = tid;
	new JsonP(sURL, {
		data: {
			'method': 'taxonomy.apnc_selectNodes',
			'tids': tid
		},
		onComplete: gotRelatedItems
	}).request();
}

/**
 * Ajax function to retrieve related items for a given taxonomy term. This is taken from the story object.
 * 
 * @param string term Taxonomy term to retrieve for
 */
function getRelatedItemsByTerm(term) {
	gTid = '';
	new JsonP(sURL, {
		data: {
			'method': 'taxonomy.apnc_selectNodesByTerm',
			'term': term
		},
		onComplete: gotRelatedItems
	}).request();
	$('ancRelated').set('html', term.toUpperCase() + ' STORIES');
	$('ancRelated').set('href', sNewsPath+'/tags.htm?tag=' + term);
	$('ancRelated').set('updated', 'true');
}

/**
 * Function to retrieve all currently set search parameters from the Search object.
 * 
 * @param boolean filter If true, don't return the current location or search filters
 */
function getQS(filter) {
	var qs = {};
	qs.q = Search.term;
	if (!filter) {
		if (Search.locn)
			qs.locn = Search.locn;
		if (Search.subj)
			qs.subj = Search.subj;
	}
	if (Search.sortOrder)
		qs.sortOrder = Search.sortOrder;
	if (Search.reporter)
		qs.reporter = Search.reporter;
	if (Search.dateBefore)
		qs.dateBefore = Search.dateBefore;
	if (Search.dateAfter)
		qs.dateAfter = Search.dateAfter;
	
	return unescape($H(qs).toQueryString());
}

/**
 * Function to set the sort order for existing results. Will reload the page with the new parameter.
 * 
 * @param string order Sort order. Valid values are: new, old
 */
function setSortOrder(order) {
	if (Search.sortOrder != order) {
		var url = 'search.htm?' + getQS(); 
		if (order == 'match') {
			// nothing
		} else if (order == 'new') {
			url += '&dateSort=DESC';
		} else if (order == 'old') {
			url += '&dateSort=ASC';
		}
		location.href = url;
	}
}

/**
 * Initialise search form field, functions and other element interfaces. Called on search page load.
 */
function initSearchEvents() {
	
	// main form
	if ($('frmSearch') && $('query')) {
		$('frmSearch').addEvent('submit', function() {
			if ($('query').value != '') {
				var url = sNewsPath+'/search.htm?q=' + $('query').value;
				if ($('dtFrom').value)
					url += '&dateAfter=' + $('dtFrom').value;
				if ($('dtTo').value)
					url += '&dateBefore=' + $('dtTo').value;
				if ($('apncSub').value)
					url += '&reporter=' + $('apncSub').value;
				window.location.href = url + '#page1';
			} else {
				alert('Please enter a search term in the search field');
			}
			return false;
		});
	}
	
	// search tips slideout
	if ($('search_tips')) {
		$('search_tips').slide('hide');
		$('search_tips').removeClass('hide');
		$$('.helptips').each(function(tip){
			tip.addEvent('click', function(evt) {
				$('search_tips').slide('toggle');
				evt.stop();
			});
		})
	}

	// sorting links
	if ($('aSortMatch')) {
  	$('aSortMatch').addEvent('click', function(event){
  		event.stop();
  		setSortOrder('match');
  	});
  }

	if ($('aSortNew')) {
  	$('aSortNew').addEvent('click', function(event){
  		event.stop();
  		setSortOrder('new');
  	});
  }

	if ($('aSortOld')) {
  	$('aSortOld').addEvent('click', function(event){
  		event.stop();
  		setSortOrder('old');
  	});
  }

	// set date fields
	if ($('dtFrom')) {
		$('dtFrom').onComplete = function(){
			if (!$('dtTo').value) {
				// $('dtTo').value = $('dtFrom').value;
				$('dtTo').setDate($('dtFrom').value);
			}
			else {
				if ($('dtFrom').value == '') {
					// clear other date
					// $('dtTo').value = '';
					$('dtTo').setDate('');
				}
				else {
					// check dates
					if ($('dtTo').getDate() < $('dtFrom').getDate()) {
						// $('dtTo').value = $('dtFrom').value;
						$('dtTo').setDate($('dtFrom').value);
					}
				}
			}
		};
	}
	if ($('dtTo')) {
		$('dtTo').onComplete = function(){
			if (!$('dtFrom').value) {
				// $('dtFrom').value = $('dtTo').value;
				$('dtFrom').setDate($('dtTo').value);
			}
			else {
				// check dates
				if ($('dtFrom').getDate() > $('dtTo').getDate()) {
					// $('dtFrom').value = ;
					$('dtFrom').setDate($('dtTo').value);
				}
			}
		};
	}
	$$('input.DatePicker').each(function(el){
		el.set('alt', " { yearOrder: 'asc', format: 'dd/mm/yyyy', yearRange: 1 } ");
		new DatePicker(el);
	});

	scroller = new Fx.Scroll(window);
	hist = new HistoryManager();
	hist.addEvent('onHistoryChange', historyChange);
}

/**
 * Ajax function to retrieve a list of most popular searches from the archive.
 */
function getPopularSearches() {
	new JsonP(sURL, {
		data: {
			'method': 'search.apncTopSearches'
		},
		onComplete: gotTopSearches
	}).request();
}

/**
 * Callback function for getPopularSearches ajax call. Parse and display results.
 * 
 * @param object json Json object as returned from the archive.
 */
function gotTopSearches(json) {
	if (!json.status) {
		// we have bad data - ignore it
		return false;
	}
	$each(json.data, function(el) {
		a
		var a = new Element('a', {
			'text': el.term + ' (' + el.count + ')',
			'href': 'search.htm?q=' + el.term
		});
		
		new Element('li').adopt(a).inject($('ulRecent'));
		$('popular_section').removeClass('hide');
	});
}

/*
Script: Date.js
	Extends the Date native object to include methods useful in managing dates.

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/
new Native({name:"Date",initialize:Date,protect:true});["now","parse","UTC"].each(function(A){Native.genericize(Date,A,true)});Date.$Methods=new Hash();["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds","Time","TimezoneOffset","Week","Timezone","GMTOffset","DayOfYear","LastMonth","UTCDate","UTCDay","UTCFullYear","AMPM","UTCHours","UTCMilliseconds","UTCMinutes","UTCMonth","UTCSeconds"].each(function(A){Date.$Methods.set(A.toLowerCase(),A)});$each({ms:"Milliseconds",year:"FullYear",min:"Minutes",mo:"Month",sec:"Seconds",hr:"Hours"},function(B,A){Date.$Methods.set(A,B)});Date.implement({set:function(B,C){B=B.toLowerCase();var A=Date.$Methods;if(A.has(B)){this["set"+A.get(B)](C)}return this},get:function(B){B=B.toLowerCase();var A=Date.$Methods;if(A.has(B)){return this["get"+A.get(B)]()}return null},clone:function(){return new Date(this.get("time"))},increment:function(A,B){return this.multiply(A,B)},decrement:function(A,B){return this.multiply(A,B,false)},multiply:function(B,G,A){B=B||"day";G=$pick(G,1);A=$pick(A,true);var H=A?1:-1;var E=this.format("%m").toInt()-1;var C=this.format("%Y").toInt();var D=this.get("time");var F=0;switch(B){case"year":G.times(function(I){if(Date.isLeapYear(C+I)&&E>1&&H>0){I++}if(Date.isLeapYear(C+I)&&E<=1&&H<0){I--}F+=Date.$units.year(C+I)});break;case"month":G.times(function(K){if(H<0){K++}var J=E+(K*H);var I=I;if(J<0){I--;J=12+J}if(J>11||J<0){I+=(J/12).toInt()*H;J=J%12}F+=Date.$units.month(J,I)});break;case"day":return this.set("date",this.get("date")+(H*G));default:F=Date.$units[B]()*G;break}this.set("time",D+(F*H));return this},isLeapYear:function(){return Date.isLeapYear(this.get("year"))},clearTime:function(){["hr","min","sec","ms"].each(function(A){this.set(A,0)},this);return this},diff:function(D,B){B=B||"day";if($type(D)=="string"){D=Date.parse(D)}switch(B){case"year":return D.format("%Y").toInt()-this.format("%Y").toInt();break;case"month":var A=(D.format("%Y").toInt()-this.format("%Y").toInt())*12;return A+D.format("%m").toInt()-this.format("%m").toInt();break;default:var C=D.get("time")-this.get("time");if(C<0&&Date.$units[B]()>(-1*(C))){return 0}else{if(C>=0&&C<Date.$units[B]()){return 0}}return((D.get("time")-this.get("time"))/Date.$units[B]()).round()}},getWeek:function(){var A=(new Date(this.get("year"),0,1)).get("date");return Math.round((this.get("dayofyear")+(A>3?A-4:A+3))/7)},getTimezone:function(){return this.toString().replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3")},getGMTOffset:function(){var A=this.get("timezoneOffset");return((A>0)?"-":"+")+Math.floor(Math.abs(A)/60).zeroise(2)+(A%60).zeroise(2)},parse:function(A){this.set("time",Date.parse(A));return this},format:function(A){A=A||"%x %X";if(!this.valueOf()){return"invalid date"}if(Date.$formats[A.toLowerCase()]){A=Date.$formats[A.toLowerCase()]}var B=this;return A.replace(/\%([aAbBcdHIjmMpSUWwxXyYTZ])/g,function(C,E){switch(E){case"a":return Date.$days[B.get("day")].substr(0,3);case"A":return Date.$days[B.get("day")];case"b":return Date.$months[B.get("month")].substr(0,3);case"B":return Date.$months[B.get("month")];case"c":return B.toString();case"d":return B.get("date").zeroise(2);case"H":return B.get("hr").zeroise(2);case"I":return((B.get("hr")%12)||12);case"j":return B.get("dayofyear").zeroise(3);case"m":return(B.get("mo")+1).zeroise(2);case"M":return B.get("min").zeroise(2);case"p":return B.get("hr")<12?"AM":"PM";case"S":return B.get("seconds").zeroise(2);case"U":return B.get("week").zeroise(2);case"W":throw new Error("%W is not supported yet");case"w":return B.get("day");case"x":var D=Date.$cultures[Date.$culture];return B.format("%"+D[0].substr(0,1)+D[3]+"%"+D[1].substr(0,1)+D[3]+"%"+D[2].substr(0,1).toUpperCase());case"X":return B.format("%I:%M%p");case"y":return B.get("year").toString().substr(2);case"Y":return B.get("year");case"T":return B.get("GMTOffset");case"Z":return B.get("Timezone");case"%":return"%"}return E})},setAMPM:function(A){A=A.toUpperCase();if(this.format("%H").toInt()>11&&A=="AM"){return this.decrement("hour",12)}else{if(this.format("%H").toInt()<12&&A=="PM"){return this.increment("hour",12)}}return this}});Date.prototype.compare=Date.prototype.diff;Date.prototype.strftime=Date.prototype.format;Date.$nativeParse=Date.parse;$extend(Date,{$months:["January","February","March","April","May","June","July","August","September","October","November","December"],$days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],$daysInMonth:function(B,A){if(Date.isLeapYear(A.toInt())&&B===1){return 29}return[31,28,31,30,31,30,31,31,30,31,30,31][B]},$epoch:-1,$era:-2,$units:{ms:function(){return 1},second:function(){return 1000},minute:function(){return 60000},hour:function(){return 3600000},day:function(){return 86400000},week:function(){return 608400000},month:function(C,A){var B=new Date();return Date.$daysInMonth($pick(C,B.format("%m").toInt()),$pick(A,B.format("%Y").toInt()))*86400000},year:function(A){A=A||new Date().format("%Y").toInt();return Date.isLeapYear(A.toInt())?31622400000:31536000000}},$formats:{db:"%Y-%m-%d %H:%M:%S",compact:"%Y%m%dT%H%M%S",iso8601:"%Y-%m-%dT%H:%M:%S%T",rfc822:"%a, %d %b %Y %H:%M:%S %Z","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M"},isLeapYear:function(A){return new Date(A,1,29).getDate()==29},parseUTC:function(B){var A=new Date(B);var C=Date.UTC(A.get("year"),A.get("mo"),A.get("date"),A.get("hr"),A.get("min"),A.get("sec"));return new Date(C)},parse:function(F){var C=$type(F);if(C=="number"){return new Date(F)}if(C!="string"){return F}if(!F.length){return null}for(var B=0,A=Date.$parsePatterns.length;B<A;B++){var D=Date.$parsePatterns[B].re.exec(F);if(D){try{return Date.$parsePatterns[B].handler(D)}catch(E){dbug.log("date parse error: ",E);return null}}}return new Date(Date.$nativeParse(F))},parseMonth:function(D,C){var B=-1;switch($type(D)){case"object":B=Date.$months[D.get("mo")];break;case"number":B=Date.$months[D-1]||false;if(!B){throw new Error("Invalid month index value must be between 1 and 12:"+index)}break;case"string":var A=Date.$months.filter(function(E){return this.test(E)},new RegExp("^"+D,"i"));if(!A.length){throw new Error("Invalid month string")}if(A.length>1){throw new Error("Ambiguous month")}B=A[0]}return(C)?Date.$months.indexOf(B):B},parseDay:function(A,D){var C=-1;switch($type(A)){case"number":C=Date.$days[A-1]||false;if(!C){throw new Error("Invalid day index value must be between 1 and 7")}break;case"string":var B=Date.$days.filter(function(E){return this.test(E)},new RegExp("^"+A,"i"));if(!B.length){throw new Error("Invalid day string")}if(B.length>1){throw new Error("Ambiguous day")}C=B[0]}return(D)?Date.$days.indexOf(C):C},fixY2K:function(B){if(!isNaN(B)){var A=new Date(B);if(A.get("year")<2000&&B.toString().indexOf(A.get("year"))<0){A.increment("year",100)}return A}else{return B}},$cultures:{US:["month","date","year","/"],GB:["date","month","year","/"]},$culture:"US",$language:"enUS",$cIndex:function(A){return Date.$cultures[Date.$culture].indexOf(A)+1},$parsePatterns:[{re:/^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})$/,handler:function(B){var C=new Date();var A=Date.$cultures[Date.$culture];C.set("year",B[Date.$cIndex("year")]);C.set("month",B[Date.$cIndex("month")]-1);C.set("date",B[Date.$cIndex("date")]);return Date.fixY2K(C)}},{re:/^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})\s(\d{1,2}):(\d{1,2})(\w{2})$/,handler:function(A){var B=new Date();B.set("year",A[Date.$cIndex("year")]);B.set("month",A[Date.$cIndex("month")]-1);B.set("date",A[Date.$cIndex("date")]);B.set("hr",A[4]);B.set("min",A[5]);B.set("ampm",A[6]);return Date.fixY2K(B)}}]});Number.implement({zeroise:function(A){return String(this).zeroise(A)}});String.implement({repeat:function(C){var A=[];for(var B=0;B<C;B++){A.push(this)}return A.join("")},zeroise:function(A){return"0".repeat(A-this.length)+this}});["LastDayOfMonth","Ordinal"].each(function(A){Date.$Methods.set(A.toLowerCase(),A)});Date.implement({timeDiffInWords:function(){var A=(arguments.length>0)?arguments[1]:new Date();return Date.distanceOfTimeInWords(this,A)},getOrdinal:function(){var A=this.get("date");return(A>3&&A<21)?"th":["th","st","nd","rd","th"][Math.min(A%10,4)]},getDayOfYear:function(){return((Date.UTC(this.getFullYear(),this.getMonth(),this.getDate()+1,0,0,0)-Date.UTC(this.getFullYear(),0,1,0,0,0))/Date.$units.day())},getLastDayOfMonth:function(){var A=this.clone();A.setMonth(A.getMonth()+1,0);return A.getDate()}});Date.alias("timeDiffInWords","timeAgoInWords");$extend(Date,{distanceOfTimeInWords:function(B,A){return Date.getTimePhrase(((A.getTime()-B.getTime())/1000).toInt(),B,A)},getTimePhrase:function(E,D,B){var A=Date.$resources[Date.$language];var C=function(){if(E>=0){if(E<60){return A.ago.lessThanMinute}else{if(E<120){return A.ago.minute}else{if(E<(45*60)){E=(E/60).round();return A.ago.minutes}else{if(E<(90*60)){return A.ago.hour}else{if(E<(24*60*60)){E=(E/3600).round();return A.ago.hours}else{if(E<(48*60*60)){return A.ago.day}else{E=(E/86400).round();return A.ago.days}}}}}}}if(E<0){E=E*-1;if(E<60){return A.until.lessThanMinute}else{if(E<120){return A.until.minute}else{if(E<(45*60)){E=(E/60).round();return A.until.minutes}else{if(E<(90*60)){return A.until.hour}else{if(E<(24*60*60)){E=(E/3600).round();return A.until.hours}else{if(E<(48*60*60)){return A.until.day}else{E=(E/86400).round();return A.until.days}}}}}}}};return C().substitute({delta:E})}});Date.$resources={enUS:{ago:{lessThanMinute:"less than a minute ago",minute:"about a minute ago",minutes:"{delta} minutes ago",hour:"about an hour ago",hours:"about {delta} hours ago",day:"1 day ago",days:"{delta} days ago"},until:{lessThanMinute:"less than a minute from now",minute:"about a minute from now",minutes:"{delta} minutes from now",hour:"about an hour from now",hours:"about {delta} hours from now",day:"1 day from now",days:"{delta} days from now"}}};Date.$parsePatterns.extend([{re:/^(\d{4})(?:-?(\d{2})(?:-?(\d{2})(?:[T ](\d{2})(?::?(\d{2})(?::?(\d{2})(?:\.(\d+))?)?)?(?:Z|(?:([-+])(\d{2})(?::?(\d{2}))?)?)?)?)?)?$/,handler:function(A){var C=0;var B=new Date(A[1],0,1);if(A[2]){B.setMonth(A[2]-1)}if(A[3]){B.setDate(A[3])}if(A[4]){B.setHours(A[4])}if(A[5]){B.setMinutes(A[5])}if(A[6]){B.setSeconds(A[6])}if(A[7]){B.setMilliseconds(("0."+A[7]).toInt()*1000)}if(A[9]){C=(A[9].toInt()*60)+A[10].toInt();C*=((A[8]=="-")?1:-1)}C-=B.getTimezoneOffset();B.setTime((B*1)+(C*60*1000).toInt());return B}},{re:/^tod/i,handler:function(){return new Date()}},{re:/^tom/i,handler:function(){return new Date().increment()}},{re:/^yes/i,handler:function(){return new Date().decrement()}},{re:/^(\d{1,2})(st|nd|rd|th)?$/i,handler:function(A){var B=new Date();B.setDate(A[1].toInt());return B}},{re:/^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,handler:function(A){var B=new Date();B.setMonth(Date.parseMonth(A[2],true),A[1].toInt());return B}},{re:/^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,handler:function(A){var B=new Date();B.setMonth(Date.parseMonth(A[2],true),A[1].toInt());B.setYear(A[3]);return B}},{re:/^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,handler:function(A){var B=new Date();B.setMonth(Date.parseMonth(A[1],true),A[2].toInt());B.setYear(A[3]);return B}},{re:/^next (\w+)$/i,handler:function(D){var E=new Date();var B=E.getDay();var C=Date.parseDay(D[1],true);var A=C-B;if(C<=B){A+=7}E.setDate(E.getDate()+A);return E}},{re:/^\d+\s[a-zA-z]..\s\d.\:\d.$/,handler:function(B){var C=new Date();B=B[0].split(" ");C.setDate(B[0]);var A;Date.$months.each(function(E,D){if(new RegExp("^"+B[1]).test(E)){A=D}});C.setMonth(A);C.setHours(B[2].split(":")[0]);C.setMinutes(B[2].split(":")[1]);C.setMilliseconds(0);return C}},{re:/^last (\w+)$/i,handler:function(A){return Date.parse("next "+A[0]).decrement("day",7)}}]);

/*
Script: String.Extras.js
	Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/
String.implement({stripTags:function(){return this.replace(/<\/?[^>]+>/gi,"")},parseQuery:function(C,A){C=$pick(C,true);A=$pick(A,true);var D=this.split(/[&;]/);var B={};if(D.length){D.each(function(F){var E=F.split("=");if(E.length&&E.length==2){B[(C)?encodeURIComponent(E[0]):E[0]]=(A)?encodeURIComponent(E[1]):E[1]}})}return B},tidy:function(){var A=this.toString();$each({"[\xa0\u2002\u2003\u2009]":" ","\xb7":"*","[\u2018\u2019]":"'","[\u201c\u201d]":'"',"\u2026":"...","\u2013":"-","\u2014":"--","\uFFFD":"&raquo;"},function(C,B){A=A.replace(new RegExp(B,"g"),C)});return A},cleanQueryString:function(A){return this.split("&").filter(A||function(B){return $chk(B.split("=")[1])}).join("&")}});

/*
 * toQueryObject returns an object containing key/value pair of each query string parameter.
 * e.g.: String.toQueryObject.param == value
 */
String.implement({toQueryObject:function(){var A={};$A(this.replace(/(^.*\?)|(#.*$)/g,"").split("&")).each(function(B){B=B.split("=");A[decodeURIComponent(B[0])]=decodeURIComponent(B[1])});return A}});
