// Yahoo! Calendar hCalendar
// version 1.0.1
// 2006-06-20
// Copyright 2006, Yahoo! Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details:
// <http://www.gnu.org/licenses/gpl.txt>
//
// Revision history:
// 1.0  2006-06-20 initial version
// 1.0.1  2006-06-21 better placement for the link
//
// Adapted from Scott Reynen's Google hCalendar script at http://randomchaos.com/software/firefox/greasemonkey/googlehcalendar/.
//
// ==UserScript==
// @name          Yahoo! Calendar hCalendar
// @namespace     http://upcoming.yahoo.com/tools/microformats/ycalhcal.user.js
// @description   Finds vevents and adds a link to send them to Yahoo! Calendar
// @include       *
// ==/UserScript==

( function() {

var innerText = function( node )
{

  var regex = /<\S[^>]*>/g;
  var withHTML = node.innerHTML;
  
  return withHTML.replace( regex , '' );

} // innerText

var trim = function( inString ) 
{
  
  return inString.replace( /^\s+|\s+$/ , '' );
  
} // trime

// Adapted from http://www.forgetfoo.com/?blogid=4077

var getElementsByClassName = function( node , classname )
{

	var a = [];
	var re = new RegExp( '(^| )' + classname + '( |$)' );
	var els = node.getElementsByTagName( "*" );
	for ( var i=0 , j=els.length; i<j; i++ )
		if ( re.test( els[i].className ) ) a.push( els[i] );
	return a;

} // getElementsByClassName

// Adapted from http://delete.me.uk/2005/03/iso8601.html

Date.prototype.setISO8601 = function ( string )
{

    var regexp = "([0-9]{4})(-?([0-9]{2})(-?([0-9]{2})" +
        "([T ]([0-9]{2}):?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):?([0-9]{2})))?)?)?)?";

    var d = string.match( new RegExp( regexp ) );

    var offset = 0;
    var date = new Date( d[1] , 0 , 1 );

    if ( d[3] ) date.setMonth( d[3] - 1 );
    if ( d[5] ) date.setDate( d[5] );
    if ( d[7] ) date.setUTCHours( d[7] );
    if ( d[8] ) date.setUTCMinutes( d[8]);
    if ( d[10] ) date.setUTCSeconds( d[10]);
    if ( d[12] ) date.setMilliseconds( Number( '0.' + d[12] ) * 1000 );
    if ( d[14] ) 
    {
    
        offset = ( Number( d[16] ) * 60 ) + Number( d[17] );
        offset*= ( ( d[15] == '-' ) ? 1 : -1 );
        
    } else {

     offset = date.getTimezoneOffset();
	}
    time = ( Number( date ) + ( offset * 60 * 1000 ) );
    this.setTime( Number( time ) );

} // Date.prototype.setISO8601

var parseDuration = function( duration )
{

	var regexp = "P(([0-9]+)D)?(T([0-9]+)([HM]))?";
	var d = duration.match( new RegExp( regexp ) );
	var response = 0;
	
	if ( d[2] ) response+= 60 * 24 * d[2];
	if ( d[4] ) response+= ( d[5] == 'H' ) ? 60 * d[4] : d[4];

	return response;

} // parseDuration

var yahooDate = function( date )
{

	var year = date.getUTCFullYear();
	var month = date.getUTCMonth() + 1;
	if ( month < 10 ) month = '0' + month;
	var day = date.getUTCDate();
	if ( day < 10 ) day = '0' + day;
	var hours = date.getUTCHours();
	if ( hours < 10 ) hours = '0' + hours;
	var minutes = date.getUTCMinutes();
	if ( minutes < 10 ) minutes = '0' + minutes;
	var seconds = date.getUTCSeconds();
	if ( seconds < 10 ) seconds = '0' + seconds;
	
	return '' + year + month + day + 'T' + hours + minutes + seconds + 'Z';
	
} // yahooDate

// Yahoo Calendar only supports Duration of <= 24 hrs.
// It also does not support end times.
// To play nice, we'll make sure that duration falls between
// 0 and 24 hours.

var yahooDeltaDuration = function ( sdate, edate ) {
	if (sdate.getTime && edate.getTime) {
		var smsec = sdate.getTime();
		var emsec = edate.getTime();
		var delta_sec = (smsec - emsec) / 1000;
		var delta_hrs = Math.floor(delta_sec/(60*60));
		var delta_min = Math.floor(delta_sec/60) % 60;
		if ( delta_min < 10 ) delta_min = '0' + delta_min;
		if ( delta_hrs >= 24 ) delta_hrs = delta_hrs % 24; // Obey Y! limitations
		if ( delta_hrs < 10 ) delta_hrs = '0' + delta_hrs;
		return '' + delta_hrs + delta_min;
	} else {
		return null;
	}
} // yahooDeltaDuration


var parseVcard = function ( vcard ) {
	// We have a vcard object that we need to parse.
	// It should be a DOM element with possible child nodes
	// corresponding to vcards.
	var cardObj = new Object();

	var names = getElementsByClassName( vcard, 'fn' );
	if (names.length > 0) {
		cardObj.fn = innerText( names[0] );
	}
	var orgs = getElementsByClassName( vcard, 'org' );
	if (orgs.length > 0) {
		cardObj.org = innerText( orgs[0] );
	}
	var addresses = getElementsByClassName( vcard, 'street-address' );
	if (addresses.length > 0) {
		cardObj.address = innerText( addresses[0] );
	}
	var tels = getElementsByClassName( vcard, 'tel' );
	if (tels.length > 0) {
		cardObj.tel = innerText( tels[0] );
	}
	var localities = getElementsByClassName( vcard, 'locality' );
	if (localities.length > 0) {
		cardObj.locality = innerText( localities[0] );
	}
	var regions = getElementsByClassName( vcard, 'region' );
	if (regions.length > 0) {
		cardObj.region = innerText( regions[0] );
	}
	var pcodes = getElementsByClassName( vcard, 'postal-code' );
	if (pcodes.length > 0) {
		cardObj.postal_code = innerText( pcodes[0] );
	}

	return cardObj;
}

var addYahooLink = function( event ) 
{

	var durations = getElementsByClassName( event , 'duration' );
	var duration = 0;
	
	if ( durations.length > 0 ) 
		duration = ( durations[0].title != '' ) ?
			parseDuration( durations[0].title ) :
			parseDuration( innerText( durations[0] ) );

	var dtStarts = getElementsByClassName( event , 'dtstart' );
	var dtStart = '';
	var dtEnd = '';

	if ( dtStarts.length > 0 ) 
	{
	
		var dtStartRaw = ( dtStarts[0].title != '' ) ?
			dtStarts[0].title : innerText( dtStarts[0] );
	
		dtStart = new Date();
		dtEnd = new Date();
		dtStart.setISO8601( trim( dtStartRaw ) );
		dtEnd.setISO8601( trim( dtStartRaw ) );
		dtEnd.setTime( Number( dtStart ) + ( duration * 60 * 1000 ) );
		
	} // if
	
	var dtEnds = getElementsByClassName( event , 'dtend' );
	if ( dtEnds.length > 0 ) 
	{
	
		var dtEndRaw = ( dtEnds[0].title != '' ) ?
			dtEnds[0].title : innerText( dtEnds[0] );
	
		dtEnd = new Date();
		dtEnd.setISO8601( trim( dtEndRaw ) );
	
	} // if

	var yDur = yahooDeltaDuration(dtStart, dtEnd);
	
	var summaries = getElementsByClassName( event , 'summary' );
	var summary = ( summaries.length > 0 ) ? 
		innerText( summaries[0] ) : '';
	
	var descriptions = getElementsByClassName( event , 'description' );
	var description = ( descriptions.length > 0 ) ? 
		innerText( descriptions[0] ) : '';
	
	var yahooLink = document.createElement( 'a' );
	var yahooImg = document.createElement( 'img' );
	
	yahooLink.href = 'http://calendar.yahoo.com?v=60&TITLE=' + trim( summary ) + '&ST=' + yahooDate( dtStart );
	if (Number(yDur) > 0) {
		yahooLink.href += '&DUR=' + yDur;
	}

	// Look for a hcard first, before a location.
	var vcards = getElementsByClassName( event , 'vcard' );
	if ( vcards.length > 0 )  {
		vcard = ( vcards[0] ) ?
			parseVcard( vcards[0] ) :
			parseVcard( innerText( vcards[0] ) );

			// Add each part of the hcard now.
			yahooLink.href += '&in_loc='+vcard.fn;
			yahooLink.href += '&in_st='+vcard.address;
			yahooLink.href += '&in_csz='+vcard.locality+', ' + vcard.region + ' ' + vcard.postal_code;
			yahooLink.href += '&in_ph='+vcard.tel;
	} else {
		var locations = getElementsByClassName( event , 'location' );
		var location = ( locations.length > 0 ) ? 
			innerText( locations[0] ) : '';
		yahooLink.href +=  '&in_loc=' + trim( location );
	}
	yahooLink.href += '&DESC=' + trim( description ) ; // Add DESC last to avoid missing important metadata.
	yahooImg.src = 'http://l.yimg.com/a/i/nt/ic/ut/bsc/clndr_1.gif';
	yahooImg.style.border = '0px';

	yahooLink.appendChild( yahooImg );

	// append to event start date end.
	if (dtStarts.length > 0) {
		targetNode = dtStarts[0];
	} else {
		targetNode = event;
	}

	targetNode.appendChild( yahooLink );

} // addYahooLink

var vevents = getElementsByClassName( document , 'vevent' );

for ( var i = 0; i < vevents.length; i++ )
  addYahooLink( vevents[i] );

} )();
