
//----------------------------------------
//
// global fields
//
//----------------------------------------

/** 
 * we cache a single xmlhttp request object
 */
var ps_xmlhttp = null;

/**
 * last requested subscription
 */
var ps_lastrequest = "";

/**
 * local array of subscriptions for the user
 */
var ps_localSubscriptions = Array();

/**
 * image we use for async operations, "please wait"
 */
var g_pendingImage = "<img src='http://partners.pubsub.com/images/smallclock.gif'/>";

/**
 * html displayed in the "cancel subscription" link
 */
var g_strCancelLinkHTML = "<img src='http://partners.pubsub.com/images/cancel.gif' border='0'/>";

/**
 * the page (document) element containing the list of subscriptions
 */
var g_eltSubscriptionList = "subs-list";

/**
 * the page element containing the list of messages (results)
 */
var g_eltMessagesArea = "results-area";

/**
 * page element containing the subscription title when a subscription is fetched
 */
var g_eltSubscriptionTitleArea = "sub-title-area";

/** 
 * stylesheet class for entries in the list of subscriptions
 */
var g_clsSubscriptionListEntry = "subscription";

/** 
 * stylesheet class for entries in the list of messages
 */
var g_clsMessagesListEntry = "entry";

/**
 * stylesheet class for entry timestamps (under the messages class)
 */
var g_clsMessagesListTimeEntry = "entry-time";
 
/**
 * stylesheet class for entry sources (under the messages class)
 */
var g_clsMessagesListSourceEntry = "entry-source";

/**
 * stylesheet class for cancel subscription blocks
 */
var g_clsCancelSubscription = "cancel-subscription-block";

/**
 * target window for links
 */
var g_linkTarget = "_blank"; 

/**
 * default method for rendering subscription entries
 */
var g_mthRenderSubscriptionEntry = renderSubscriptionEntry;

/**
 * default method for rendering messages (events)
 */
var g_mthRenderMessage = renderMessage;

/**
 * controls whether a "please wait" is displayed as the list
 * of user subscriptions is loaded
 */
g_displaySubscriptionWait = true;

/**
 * callback method when sublist is called 
 */
g_fnSubscriptionListCallback = "ps_loadFirstSubscription";

/**
 * callback method when subscription is loaded
 */
g_fnNewMessagesCallback = null;

/**
 * html to display in an empty subscriptions list 
 */
g_strNoAccountSubscriptions = "";

/**
 * callback method after a new subscription is created
 */
g_fnNewSubscriptionCallback = null;

/**
 * html displayed when a subscription is empty
 */
g_strNoResultsHTML = "";

/**
 * the RPC URL
 */
var ps_baseRPCURI = "http://partners.pubsub.com/ps-rpc/";

/**
 * the function to use, either xml or javascript 
 */
//g_psRemoteFunction = ps_callRemoteFunctionPost;
g_psRemoteFunction = ps_callRemoteFunctionJS;

/**
 * global fields for JS RPC
 */
var ps_lastEltIdx = 108;
var ps_firstEltIdx = 100;
var ps_currentEltIdx = ps_firstEltIdx;

var g_strSubscriptionsListHeader = "";
var g_strSubscriptionsListFooter = "";

//----------------------------------------
//
// generic methods 
//
//----------------------------------------

/** 
 * construct the xmlhttp request object; use whatever method
 * is necessary on each of the browsers.
 */
function ps_newXMLObject()
{
	var xml = null;
	if( typeof XMLHttpRequest != 'undefined' ) xml = new XMLHttpRequest();
	if( null != xml ) return xml;
	try { xml = new ActiveXObject("Msxml2.XMLHTTP"); }
	catch(e){}
	if( null != xml ) return xml;
	try { xml = new ActiveXObject("Microsoft.XMLHTTP"); }
	catch(e){}
	return xml;
}

/** 
 * get a cached-instance xmlhttp request object
 */
function ps_getXMLObject()
{
	if( null != ps_xmlhttp ) return ps_xmlhttp;
	ps_xmlhttp = ps_newXMLObject();
	return ps_xmlhttp;
}

/**
 * deserialize our standard data response format to a js array
 */
function ps_deserialize( text )
{
	var data = "";
	
	// need to normalize...
	data = text.replace( /\n/g, " " );

	// alert( text );

	return data;
}

/**
 * this is a remote function call implemented with XMLHTTPRequest;
 * this one has a security limitation in that it can only call back
 * to the calling host+domain, so we'll need some passthrough to 
 * support it.
 */
function ps_callRemoteFunctionPost( callback, functionname, parameters )
{
	// switch to using new objects; a bit snappier performance
	var i, xml = ps_newXMLObject(); // ps_getXMLObject();
	var data = Array();
	var packet = "";
	if( null == xml ) return -1;
	
	// construct the output packet
	packet = "<?xml version='1.0' encoding='UTF-8'?>\n" +
		"<xml>\n" +
		"<function>" + functionname + "</function>\n" +
		"<parameters>\n" +
		"<parameter name='random'>" + Math.random() + "</parameter>\n";
	for( i in parameters ) packet += "<parameter name='" + i + "'>" + parameters[i] + "</parameter>\n";
	packet += "</parameters>\n" +
		"</xml>\n";
	
	xml.open( "POST", "http://partners.pubsub.com/ps-rpc/", true );
	xml.setRequestHeader( "Content-Type", "text/xml");
	xml.onreadystatechange = function() 
	{
		if( xml.readyState == 4 ) 
		{
			data = ps_deserialize( xml.responseText );
			callback( data );
			xml = undefined;
		}
	}
	xml.send(packet)	
	
}

/**
 * this is a remote function call implemented with Javascript;
 * it uses scripts from remote hosts, which may be unsupported 
 * in future browsers.  we should use a reflected URI and use the 
 * xml request method wherever possible. 
 */
function ps_callRemoteFunctionJS( callback, functionname, parameters )
{
	var d = new Date();
	var eltID = "ps_script_" + (ps_currentEltIdx++);
	var elt = document.getElementById( eltID );
	var uri = ps_baseRPCURI + "?";
	var parms = "";
	var k, v, m;
	
	if( ps_currentEltIdx >= ps_lastEltIdx ) ps_currentEltIdx = ps_firstEltIdx;
	if( null != elt ) document.body.removeChild( elt );
	elt = document.createElement( "script" );
	elt.id = eltID;
	
	v = callback.toString();
	if( m = v.match( /function\s+([^\)]+)\(/ )) uri += "&c=" + m[1];
	uri += "&e=" + eltID;
	
	for( k in parameters )
	{
		v = parameters[k].toString();
		v = v.replace( /&/, "%26" );
		v = v.replace( /=/, "%3d" );
		uri += "&" + k + "=" + v;
	}
	
	uri += "&f=" + functionname;
	uri += "&r=" + Math.random() + "," + d;
	elt.src = uri; 
	document.body.appendChild( elt );
	document.close();
}

//----------------------------------------
//
// site-specific methods 
//
//----------------------------------------

/**
 * call with a subscription id (internal) to cancel a subscription.
 */
function ps_cancelSubscription( sub )
{
	var p = Array();
	p['id'] = sub;
	g_psRemoteFunction( ps_callback_cancelSubscription, "cancel", p );
	ps_removeSubFromList( sub );
}

/**
 * return a list of subscriptions for the given email, password 
 */
function ps_subscriptionList( usertoken )
{
	var o = document.getElementById( g_eltSubscriptionList );
	var p = Array();
	p[ 'token' ] = usertoken;
	if( null == usertoken || usertoken == "" )
	{
		if( null != o ) o.innerHTML = g_strNoAccountSubscriptions;
		if( null != g_fnSubscriptionListCallback )
		{
			setTimeout( g_fnSubscriptionListCallback + "();", 100 );
		}
	}
	else
	{
		g_psRemoteFunction( ps_callback_subscriptionList, "subscriptions", p );
	}
}

/**
 * clone account: copy all subscriptions from one account to another
 * account.  if the other account doesn't exist, we may need to create
 * it.
 */
function ps_cloneAccount( oldToken, newToken )
{
	
}

/**
 * create a new subscription: required parameters are user, pass, query; title and topic are optional.
 */
function ps_createSubscription( usertoken, query, title, topic )
{
	var p = Array();
	p[ 'token' ] = usertoken;
	p[ 'query'] = query;
	if( null != title ) p[ 'title'] = title;
	if( null != topic ) p[ 'topic'] = topic;
	g_psRemoteFunction( ps_callback_createSubscription, "subscribe", p );
}

/**
 * copy an existing or pre-created subscription into the user's account
 */
function ps_copySubscription( usertoken, id )
{
	var p = Array();
	p[ 'token' ] = usertoken;
	p[ 'id'] = id;
	g_psRemoteFunction( ps_callback_createSubscription, "clone", p );
}

/** 
 * load a subscription; set results in the target window
 */
function ps_loadSubscription( id )
{
	var p = Array();
	var o = document.getElementById( g_eltMessagesArea );
	if( null != o ) o.innerHTML = "<div style='text-align: center;'>" + g_pendingImage + "</div>\n";
	o = document.getElementById( g_eltSubscriptionTitleArea );
	if( null != o ) o.innerHTML = "";
	p['code'] = id;
	p['max'] = 10;
	g_psRemoteFunction( ps_callback_subscriptionMessages, "messages", p );

}

/**
 * utility function: remove the subscription identified by ID from the 
 * list of subscriptions.  this has the appearance of working faster.
 */
function ps_removeSubFromList( id )
{
	var i, o, a = Array();
	for( i in ps_localSubscriptions )
	{
		o = ps_localSubscriptions[i];
		if( o.code != id ) a[i] = o;
	}
	ps_localSubscriptions = Array();
	for( i in a ) ps_localSubscriptions[i] = a[i];
	ps_renderSubscriptions();
}

/**
 * utility function: format an iso date into more human-readable syntax
 */
function reformatDate( d )
{
	var m, s = "";
	if( m = d.match( /(\d+)-(\d+)-(\d+)T(\d+)\:(\d+)\:(\d+)(\D\d+)\:(\d+)/ ))
	{
		s = m[2] + "/" + m[3] + "/" + m[1] + " " + m[4] + ":" + m[5] + ":" + m[6] + " " + m[7] + m[8];
		return new Date( s ).toLocaleString();
	}
	return d;
}

/**
 * format a subscription-list entry.  this is handled with a separate method 
 * so it can be overridden (more accurately, by setting a function 
 * pointer) to provide different behavior.
 */
function renderSubscriptionEntry( subcode, subid, substr, idx )
{
	return "<div class='" + g_clsSubscriptionListEntry + "'>" + 
		"<span class='" + g_clsCancelSubscription + "'>" +
		"<a href=\"javascript:ps_cancelSubscription(%27" + subcode + "%27);\">" + 
		g_strCancelLinkHTML + "</a>" + " </span>" +
		"<a href=\"javascript:ps_loadSubscription(%27" + 
		subid + "%27);\">" + substr + "</a>" +
		"</div>\n";
}

/**
 * format a message (event) for display.  can be overridden using the function pointer.
 */
function renderMessage( link, title, time, source, sourcelink, summary )
{
	return "<div class='" + g_clsMessagesListEntry + "'>\n" + 
		"<div><a target='" + g_linkTarget + "' href='/media/coverage/12-5-05%20CBS%205%20Green%20Bay%20Manure%20Slinging%20Erupts%20in%20Manitowoc%20County_files/%22%20+%20link%20+%20%22'>" + title + "</a></div>\n" + 
		"<div class='" + g_clsMessagesListTimeEntry + "'>" + time + "</div>\n" +
		"<div class='" + g_clsMessagesListSourceEntry + "'>" + source + "</div>\n" +
		"<div>" + summary + "</div>\n" +
		"</div>\n";
}

/**
 * load the first subscription from the list of subscriptions.  this 
 * is a default callback after the list of subscriptions is loaded.
 */
function ps_loadFirstSubscription()
{
	if( ps_localSubscriptions )
	{
		for( i in ps_localSubscriptions )
		{
			ps_loadSubscription( i );
			return;
		}
	}
}

//----------------------------------------
// 
// utility functions for handling 
// cookies, query strings, etc.
//
//----------------------------------------

/**
 * break query string into a set of key/value pairs
 */
function ps_parseQueryParameters( target )
{
	var m, qs, a, i, idx = document.location.href.indexOf('?');
	if( idx <= 0 ) return;
	qs = document.location.href.substring( idx + 1 );
	if( m = qs.match( /(.+)\#$/ )) qs = m[1];
	if( qs.length <= 0 ) return;
	a = qs.split( '&' );
	for( i = 0; i< a.length; i++ )
	{	
		idx = a[i].indexOf( '=' );
		if( idx > 0 ) target[ a[i].substring( 0, idx ) ] = a[i].substring( idx + 1 );
	}
}

/**
 * get root domain; this is useful for setting cookies 
 * to work across www/non-www domains (if that's what
 * you want)
 */
function ps_getRootDomain()
{
	var a, m, l = document.location.href;
	if( m = l.match( /\/\/(.+?)(?:\/|\?|$)/ )) l = m[1];
	a = l.split( "." );
	if( a.length >= 2 ) l = a[a.length-2] + "." + a[a.length-1];
	l = "." + l; // necessary?
	return l;
}

/**
 * cookie functions from 
 * http://www.webreference.com/js/column8/functions.html
 */
function ps_setCookie(name, value, expires, path, domain, secure)
{
	// if domain is not set, then set it to the
	// root domain that we're given.
	if( !domain || domain == "" ) domain = ps_getRootDomain();
	
	// if path is not set, use root "/"
	if( !path || path == "" ) path = "/";
	
    document.cookie= name + "=" + escape(value) +
        ((expires) ? "; expires=" + expires.toGMTString() : "") +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
}

/**
 * cookie functions from 
 * http://www.webreference.com/js/column8/functions.html
 */
function ps_getCookie(name)
{
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1)
    {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    }
    else
    {
        begin += 2;
    }
    var end = document.cookie.indexOf(";", begin);
    if (end == -1)
    {
        end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
}

/**
 * cookie functions from 
 * http://www.webreference.com/js/column8/functions.html
 */
function ps_deleteCookie(name, path, domain) 
{
	if (ps_getCookie(name)) 
	{
		document.cookie = name + "=" +
			((path) ? "; path=" + path : "") +
			((domain) ? "; domain=" + domain : "") +
			"; expires=Thu, 01-Jan-70 00:00:01 GMT";
	}
}

//----------------------------------------
//
// callback methods from async functions
//
//----------------------------------------

function ps_callback_subscriptionMessages( data )
{
	var o = document.getElementById( g_eltMessagesArea );
	var html = "", tmp = "";
	var result = null;
	var entry = null;
	var title, link, time, source, summary, sourcelink = "";
	var id, subobj;
	var titleArea = document.getElementById( g_eltSubscriptionTitleArea );
	
	// alert( data );
	
	if( null != titleArea )
	{
		title = "Untitled Subscription";
		if( m = data.match( /<subscription-title>(.+)?<\/subscription-title>/ )) title = m[1];

		if( title == "Untitled Subscription" )
		{
			if( m = data.match( /<subscription-id>(.+)?<\/subscription-id>/ ))
			{
				id = m[1];
				subobj = ps_localSubscriptions[id];
				if( null != subobj ) title = subobj.title;
			}
		}
		titleArea.innerHTML = title;
	}

	// error?
	if( m = data.match( /<error>(.+?)<\/error>/ ))
	{	
		if( m[1].match( /no results/i ) && g_strNoResultsHTML != "" ) html = g_strNoResultsHTML ;
		else html = m[1];
	}
	else if( m = data.match( /<messages>(.+?)<\/messages>/ ))
	{
		result = m[1];
	
		// easiest thing would be a straight transform, but we 
		// probably can't do that in a totally cp manner, so 
		// we're stuck with regexp for now
		
		if( m = result.match( /(<message>.+?<\/message>)/g ))
		{
			for( var i = 0; i< m.length; i++ )
			{
				entry = m[i];
				if( n = entry.match( /<title>(.*?)<\/title>.*?<link>(.*?)<\/link>.*?<time>(.*?)<\/time>.*?<source>(.*?)<\/source>.*?<summary>(.*?)<\/summary>/ ))
				{
					title = n[1];
					link = n[2];
					time = reformatDate( n[3] );
					source = n[4];
					summary = n[5];
					if( n = entry.match( /<sourcelink>(.*?)<\/sourcelink>/ )) sourcelink = n[1];
					html += g_mthRenderMessage( link, title, time, source, sourcelink, summary );
				}
			}
		}
		else html = "results fmt?";
	}
	else html = "Missing data?";
	if( null != o )
	{
		o.innerHTML = html;
	}
	
	// callback?
	if( null != g_fnNewMessagesCallback )
		g_fnNewMessagesCallback ( data );

}

function ps_callback_createSubscription( data )
{
	var re, t, p, n, o = document.getElementById( g_eltSubscriptionList );
	var result = null;
	var html = "";
	var subid = "";
	var substr = "";
	var subcode = "";
	var substr = "";
	var subobj = null;

	if( m = data.match( /<error.*?>(.+?)<\/error>/ ))
	{
		alert( "Error: " + m[1] );
		return;
	}
	
	// the data coming out has the code, id, and title; add
	// these to the list (might as well insert at the top)

	if( m = data.match( /<title>(.+?)<\/title>/ )) substr = m[1];
	if( m = data.match( /<id>(.+?)<\/id>/ )) subid = m[1];
	if( m = data.match( /<code>(.+?)<\/code>/ )) subcode = m[1];
	
	subobj = new Object();
	subobj.title = substr;
	subobj.id = subid;
	subobj.code = subcode;
	ps_localSubscriptions[subid] = subobj ;
		
	if( null != o )
	{
		ps_renderSubscriptions();
	}
	if( null != g_fnNewSubscriptionCallback ) g_fnNewSubscriptionCallback( data );

}

function ps_callback_cancelSubscription( data )
{
	var re, t, p, n, o = document.getElementById( g_eltSubscriptionList );
	var result = null;
	var html = "";
	var substr = "";
	var subcode = "";
	var sub = null;
	var firstsub = "";

	if( m = data.match( /<error.*?>(.+?)<\/error>/ ))
	{
		alert( m[1] );
		return;
	}
}

function ps_callback_subscriptionList(data)
{
	var o = document.getElementById( g_eltSubscriptionList );
	var result = null;
	var html = "";
	var substr = "";
	var subcode = "";
	var sub = null;
	var firstsub = "";
	var subobj = null;
	var elements = new Array();
	var fRender = 0;

	// reset
	ps_localSubscriptions = Array();

	if( m = data.match( /<error.*?>(.+?)<\/error>/ ))
	{
		html = "<div class='" + g_clsSubscriptionListEntry + "'>" + m[1] + "</div>";
	}
	else if( m = data.match( /<subscriptions>(.+?)<\/subscriptions>/ ))
	{
		result = m[1];
		if( m = result.match( /(<subscription.+?<\/subscription>)/g ))
		{
			for( var i = 0; i< m.length; i++ )
			{
				sub = m[i];
				substr = "";
				subcode = "";
				subid = "";
				if( n = sub.match( /<name>(.+?)<\/name>/ )) substr = n[1];
				if( n = sub.match( /id="(.+?)"/ )) subid = n[1];
				if( n = sub.match( /code="(.+?)"/ )) subcode = n[1];
				subobj = new Object();
				subobj.title = substr;
				subobj.id = subid;
				subobj.code = subcode;
				ps_localSubscriptions[subid] = subobj ;
				if( firstsub == "" ) firstsub = subid;
				fRender = true;
			}
		}
		else html = "<div class='" + g_clsSubscriptionListEntry + "'>results ok...</div>";
	}
	else
	{
		html = "<div class='" + g_clsSubscriptionListEntry + "'>An error occurred.<BR/>Please try again later.</div>";
	}
	if( null != o ) 
	{
		if( fRender ) ps_renderSubscriptions();
		else o.innerHTML = g_strSubscriptionsListHeader + html + g_strSubscriptionsListFooter;
	}
	if( null != g_fnSubscriptionListCallback )
	{
		setTimeout( g_fnSubscriptionListCallback + "();", 100 );
	}
}

function ps_renderSubscriptions()
{
	var i, o = document.getElementById( g_eltSubscriptionList );
	var html =  g_strSubscriptionsListHeader;
	var ctr = 0;
	var a = Array();
	
	for( i in ps_localSubscriptions )
	{
		//o = ps_localSubscriptions[i];
		//html += g_mthRenderSubscriptionEntry( o.code, o.id, o.title, ctr++ );
		a.push( ps_localSubscriptions[i] );
	}
	for( i = a.length - 1; i >= 0; i-- ) 
	{
		o = a[i];
		html += g_mthRenderSubscriptionEntry( o.code, o.id, o.title, ctr++ );
	}

	html += g_strSubscriptionsListFooter;
	o = document.getElementById( g_eltSubscriptionList );
	if( null != o ) o.innerHTML = html;
	
}


