/*
 * jQuery Week
 *
 * Copyright (c) 2006, 2007, 2008 Shane Saunders
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * 
 *
 * Depends:
 *	ui.core.js
 *
 *	Based of datepicker plugin 
 */


(function($) { // hide the namespace

/* Date picker manager.
   Use the singleton instance of this class, $.week, to interact with the week.
   Settings for (groups of) weeks are maintained in an instance object
   (WeekInstance), allowing multiple different settings on the same page. */

function Week(intInstanceId) {
	//alert('Week');
	this._intCalendarInstanceId = intInstanceId;
	this._nextId = 0; 		// Next ID for a dateselector instance
	this._ele = null; 		// element
	this._inst = []; 		// List of instances indexed by ID
	this.regional = []; 	// Available regional settings, indexed by language code
	this.regional[''] = { 	// Default regional settings
	};
	this._defaults = {
		boundTo : null,
		xmlData : null
	};
	$.extend(this._defaults, this.regional['']);
	this._weekDiv = $('<div id="' + intInstanceId + '" class="ui-week-div ' + intInstanceId + '"></div>');
	
}

$.extend(Week.prototype, {
	/* Class name added to elements to indicate already configured with a week. */
	markerClassName: 'hasWeek',
	
	/* Register a new week instance - with custom settings. */
	_register: function(inst) {
		var id = this._nextId++;
		this._inst[id] = inst;
		return id;
	},

	/* Retrieve a particular week instance based on its ID. */
	_getInst: function(id) {
		return this._inst[id] || id;
	},

	/* Override the default settings for all instances of the week. 
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return the manager object */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
		return this;
	},
	
	/* Attach the schedule to a jQuery selection.
	   @param  target    element - the target input field or division or span
	   @param  settings  object - the new settings to use for this schedule instance (anonymous) */
	_attachWeek: function(target, settings) {
		//alert('_attachWeek');
		// check for settings on the control itself - in namespace 'date:'
		var nsSettings = null;
		
		// check to see if we have settings in the name space of the element
		// if so loop through and parse out.
		for (attrName in this._defaults) {
			var attrValue = target.getAttribute('date:' + attrName);
			if (attrValue) {
				nsSettings = inlineSettings || {};
				try {
					nsSettings[attrName] = eval(attrValue);
				} catch (err) {
					nsSettings[attrName] = attrValue;
				}
			}
		}
		
		// set instance settings
		var instSettings = ( nsSettings ?  $.extend(settings || {}, nsSettings || {}): settings );
		// create instance
		
		var inst = new WeekInstance(instSettings, true, settings.id);
		var temp = this;
		// make instance
		temp._createWeek(target, inst);
		// reset selector to today to intialize this
		//alert('attachweek ' + settings.id);
		$(inst._settings['boundTo']).dateselector("reset",settings.id);
		//alert('wtf ');
	},
	
	/* Attach an inline schedule to a div. */
	_createWeek: function( target, inst ) {
		// get element
		this._ele = $(target);
		// check if this already has a marker ..i.e alread is an instance of schedule
		if (this._ele.is('.' + this.markerClassName)) return;
		
		// else add marker / instance to element
		this._ele.addClass(this.markerClassName).empty().append( inst._weekDiv )
			.bind("setData.week", function(event, key, value){
				inst._settings[key] = value;
			}).bind("getData.week", function(event, key){
				return inst._get(key);
			});
	
		// set id	
		this._ele[0]._calId = inst._id;
		
	},
	
	_getBindWeek: function(target) {
		if (inst == this._getInst(target._calId)) {
			return inst._settings['boundTo'];
		}
		return null;
	},
	
	_daysBetween: function ( date1 , date2 ) {
		date1 = new Date( date1.substring(0,4), date1.substring(4,6)-1, date1.substring(6) );
		date2 = new Date( date2.substring(0,4), date2.substring(4,6)-1, date2.substring(6) );
		
	    // The number of milliseconds in one day
	    var ONE_DAY = 1000 * 60 * 60 * 24

	    // Convert both dates to milliseconds
	    var date1_ms = date1.getTime()
	    var date2_ms = date2.getTime()

	    // Calculate the difference in milliseconds
	    var difference_ms = Math.abs( date1_ms - date2_ms )

	    // Convert back to days and return
	    return Math.round(difference_ms/ONE_DAY)
	}

});

/* Individualised settings for week functionality applied to one or more related inputs.
   Instances are managed and manipulated through the Week manager. */
function WeekInstance(settings, inline, intCalendarInstanceId) {
//alert('WeekInstance');
	this._intCalendarInstanceId = intCalendarInstanceId;
	// item id
	this._id = $.week[intCalendarInstanceId]._register(this);
	// base div
	this._weekDiv = $.week[intCalendarInstanceId]._weekDiv;
	this._weekHolder 	= null;
	this._contentHolder = null;
	this._currentDate 	= null;
	this._prevDate 		= null;
	//
	this._boundId		= null;
	this._data 			= null;
	//
	this._rowArray		= [ ];		// use to help position bar in corrent positions
	//
	this._curPosition 		= -1;
	this._edgeDist 			= 0;
	this._contentBaseHeight = 0;
	this._currentDir		= 1;
	this._accordionOpen 	= false;	// used to check wiether the accoridan is in use
	this._changingWeek		= false; 	// boolean used to check if accordians are all close so that we can move to the next week
	
	
	// customise the schedule object - uses manager defaults if not overridden
	this._settings = extendRemove(settings || {}); // clone
	
	this._setMaxDate( );
	// create content
	this._createContentHolders( );
	
	// bind to 
	$(this._settings['boundTo']).bind( "onUpdate", { inst:this},  function( evt , id,  data,  to,  from ) { 
			evt.data.inst._setData( id,  data,  to,  from );
	} );
	
	// date selection (day);
	$(this._settings['boundTo']).bind( "onDateSelection", { inst:this},  function( evt , date ) { 
			//evt.data.inst._setData(from, to );
			//alert(' date selection ' + date )
	} );
	
}

$.extend(WeekInstance.prototype, {
	
	/* Get a setting value, defaulting if necessary. */
	_get: function(name) {
		var setting = this._settings[name] !== undefined ? this._settings[name] : $.week[this._intCalendarInstanceId]._defaults[name];
		if(!setting) setting = $(this._settings['boundTo']).data( name+'.dateselector' );
		return setting;
	},
	
	_setMaxDate : function( ){
		
		var xmlString = this._get('xmlData'); // get xml
		//alert('_setMaxDate');
	    var $dom = $.xmlDOM(xmlString, function(error) {
	        alert('A parse error occurred! ' + error);
	    });
		var maxDate = 0;
		var cMaxDate = 0;
		$dom.find('item').each(function() {
			cMaxDate = $(this).attr('to');
			if( cMaxDate > maxDate) maxDate = cMaxDate;
		});
		var date = new Date( maxDate.substring(0,4), (maxDate.substring(4,6))-1, maxDate.substring(6));
		$(this._get('boundTo')).data('maxDate.dateselector', date);
	},
	
	
	_createContentHolders : function ( ){
		//alert('_createContentHolders');
		$(this._weekDiv).append(  jQuery(document.createElement("div")).addClass('ui-week-holder') );
		this._weekHolder = '#'+ this._intCalendarInstanceId + ' > .ui-week-holder';
		$(this._weekDiv).append( jQuery(document.createElement("div")).addClass('ui-content-holder') );
		this._contentHolder = '#'+ this._intCalendarInstanceId + ' > .ui-content-holder';
	},
	
	_setData : function ( id,  data ){
			//alert('_setData');
			this._data = data;
			this._selectorId = id;
			this._currentDate = data['formated_date'];
			if(this._currentDate != this._prevDate)
			{
				this._updateWeekView( );
				this._prevDate = this._currentDate;
				
			}
	},
	
	/* Generate the week content. */
	_updateWeekView : function(  ) {
		
		$(this._weekDiv).stop();
		
		// set width so that we have the correct width to tween by into position later on 
		this._stageWidth = $(this._weekDiv).width();
		this._curPosition++;
		// build content out of site to tween in
		this._edgeDist = $(this._weekHolder).width() - $(this._weekDiv).width();
		if( this._currentDate < this._prevDate )
		{
			
			$(this._weekDiv).css('left', (($(this._weekDiv).width() * -1 ) - this._edgeDist)+'px');
			//$(this._weekDiv).css('left', '500px');
			$(this._weekHolder).prepend( this._generateWeek( ) ); // append before			
			$(this._contentHolder).prepend( this._updateContent( ) ); // append before
		}
		else
		{
			$(this._weekHolder).append( this._generateWeek( ) ); // append after
			$(this._contentHolder).append( this._updateContent( ) ); // append after
		}
		
		//fix the div widths
		var tempwidth = $(this._weekDiv).find('.ui-week-info').length;
		tempwidth = 100/tempwidth;
		$(this._contentHolder).find('.ui-week-info').css('width', tempwidth + '%');
		
		ellipsis( );

		// set view size
		var stripViewerWidth = $(this._weekHolder).find(".ui-week").width()*$(this._weekHolder).find(".ui-week").size(); 
		$(this._weekHolder).css( "width" , stripViewerWidth );
		$(this._contentHolder).css( "width" , stripViewerWidth );

		// apply accodiran to new content
		$(this._contentHolder).find('.ui-week-info').accordion({ 
														collapsible: true,
														active:	true,
														autoHeight: false,
														header:'div.bar', // the bar as a handle
														change:function(event, ui) { 
															//make holder bigger to show the content
															var newHeight = ui.newHeader.parents().parents().height() + 150;
															$(this).parents().prev('.ui-week-holder').animate({ height: newHeight }, 300 ); // get position of item and animate to
														 }
													});
		$(this._contentHolder).find('.ui-week-info').bind('accordionchange', {inst:this}, function(event, ui) {
				if( event.data.inst._changingWeek && !ui.newContent.height() )
				{
					event.data.inst._changingWeek = false;
					event.data.inst._doWeekSlide( );
				}
				
				// if accordian open let us know of it
				event.data.inst._accordionOpen = ui.newContent.height() ? true : false;
		});
		
		// intialize content height
		if(!this._prevDate)
		{
			var content_height = $(this._contentHolder).height() + 100;		 // get content height
			this._contentBaseHeight = content_height;
			//this is fucking not working in fucking ie!!!! never do that again, took a day to debug!
			//$(this._weekHolder).animate({ height: content_height }, 300 ); // get position of item and animate to
			$(this._weekHolder).height(content_height);
		}
		// set current direction
		this._currentDir = ( this._currentDate < this._prevDate) ? -1 : 1 ;
		
		// if accordian is not open the move week
		if( !this._accordionOpen && this._prevDate != null ) {
			this._doWeekSlide( );
		} 
		// else close accordian and then move week
		else if( this._prevDate ){
			this._changingWeek = true;																			
			$(this._contentHolder).find('.ui-week-info').accordion('activate', false); // make closed;
		}
		
	},
	
	_doWeekSlide : function( ){
		//alert('_doWeekSlide');
		var content_height = $(this._contentHolder).height() + 100;	   // get content height
		this._contentBaseHeight = content_height;
		$(this._weekHolder).animate({ height: content_height }, 300 ); // get height of item and animate to
		
		// Tween into place
		$(this._weekDiv).attr( "cdate", this._currentDate);
		
		var parent = this;
		// get current position
		var position;
		if(this._currentDir == -1)
		{
			position = '0px';
		}
		else
		{
			position = ($(this._weekDiv).width() * -1) - this._edgeDist +'px';
		}
		
		$(this._weekDiv).width(($(this._weekDiv).width() * 2) + 'px');
		
		$(this._weekDiv).animate( { left: position }, 1000 , null, function(){ 
			parent._resetDisplay.apply(parent, [this]) 
			} ); // get position of item and animate to
			
	},
	
	
	/*
	*	reset display removing previous or next weeks
	*/
	_resetDisplay: function( target ){
		
		$(this._weekDiv).width(($(this._weekDiv).width() / 2) + 'px');
		$(this._weekHolder).width(($(this._weekHolder).width() / 2) + 'px');
		this._curPosition = 0;
		// clear week display
		var c_date = $(target).attr('cdate');
		$(this._weekDiv).find(".ui-week").each( function(){
			if( !$(this).hasClass(c_date)) $(this).remove();	
		});
		// clear info
		$(this._weekDiv).find(".ui-week-info").each( function(){
			if( !$(this).hasClass(c_date)) $(this).remove();	
		});
		
		// reset position
		$(target).css('left', '0px' );
		
		this._edgeDist = $(this._weekHolder).width() - $(this._weekDiv).width();
	},
	
	_generateWeek: function( ){
		
		var today = new Date();
		today = new Date( today.getFullYear(), today.getMonth(), today.getDate()); // clear time
		
		var html = '';
		
		var c_month = this._data;
		var c_header = c_month['header'];
		var c_content = c_month['content'];
		
		
		// create month
		html += '\n<div class="ui-week '+this._data['formated_date']+'">';
			//
			html += '<div class="ui-week-content" >';
				
				for (var w = 0; w < c_content.length; w++){
				
					var days = c_content[w];
					//
					for (var d = 0; d < days.length; d++){
						html += '<div class="'+days[d].style+'" '+( days[d].script ? days[d].script : '')+'>';
							// header
							html += '<div class="ui-week-weekname-cell '+c_header[d].style+'">';
							html += '<div class="ui-week-weekname-header-cell">'+c_header[d].data+' '+days[d].data+'</div>';
							html += '</div>';
							// day content	
						html += '</div>';
					}
				};
				html += '<div style="clear:both;"></div>';
			html += '</div>';
		html += '</div>';
		
		
		return html;
		
	},
	 /*create content for the actual week display of the events*/
	_updateContent : function( ele ) {
		
		var c_content = this._data['content'];
		
		// create our content element to add to
		var element = jQuery(document.createElement("div")).addClass('ui-week-info '+this._data['formated_date']);		
		// pass this to our set content method
		this._setContent( element, c_content[0][0].date, c_content[0][6].date );
		// return our create content element to be added to the display
		return element;
	},
	
	_setContent : function( ele,  bWeek , eWeek ) {
		//alert('_setContent');
		var day_width = $('.ui-week-weekname-cell').width()+2; 	// plus 2 for border
		var xml = this._settings['xmlData']; 						// get xml
		var $dom = $.xmlDOM(xml, function(error) {
	        alert('A parse error occurred! ' + error);
	    });
		var week = this;
		$.intCurCalendarInstanceId = this._intCalendarInstanceId;
		// pre dates .. these are items that start before our week and either stop in it or continue on
		$dom.find('item').each(function() {
			
			// get dates
			var from_date = $(this).attr('from');
			var to_date = $(this).attr('to');
			// if before this week and is greater than the begining of this week
			if( from_date <  bWeek && to_date >= bWeek  )
			{
				var t_days = $.week[$.intCurCalendarInstanceId]._daysBetween( bWeek, to_date) +1;
				var days = (t_days > 7 ) ? 7 : t_days;
				// do content
				var html = '<div class="bar '+$(this).attr('styleclass')+'"><div class="mid ellipsis" style="padding-left:11px;width:'+(day_width * days - ((days == 7) ? 0 : 24) )+'px;" title="'+$(this).find('title').text()+'">'+$(this).find('title').text()+'</div>'+((days == 7) ? '' : '<div class="right"></div>')+'</div>';
				html += '<div class="bar_content '+$(this).attr('styleclass')+'"><div class="inner">'+week._createContent( from_date, $(this) )+'</div></div>';
				// set position on content rows
				var id = week._makeArray( $(this).attr("row") , ele, html);
				// if we have a row id then we want to save this for future use
				if(id) $(this).attr('row', id);
			}
			
		});
		
		// starting this week...
		$dom.find('item').each(function() {
			
			// get dates .. these are in iso8601 format
			var from_date = $(this).attr('from');
			var to_date = $(this).attr('to');
			// if this date lies with in this week then we want it
			if( from_date >= bWeek && from_date <= eWeek ){
				var day_space = $.week[$.intCurCalendarInstanceId]._daysBetween( bWeek, from_date );	// how many days from start of week
				var total_days = $.week[$.intCurCalendarInstanceId]._daysBetween( from_date, to_date) + 1;	// how many days does this entry go for
				// work out the number of days this is going to cover in this weeks
				var days = (( total_days + day_space ) >= 7 ) ? (7 - day_space) : total_days;
			
				// do content
				var html = '<div class="bar '+$(this).attr('styleclass')+'">';
					html += '<div style="float:left;width:'+(day_width * day_space)+'px;height:1px;"></div>'; 							// set day spaces before date
					html += '<div class="left"></div>'; 																				// do left end as we are fisrt showing this this week
					html += '<div class="mid ellipsis" style="width:'+( day_width * days - ((total_days + day_space > 7 ) ? 11 : 24))+'px;" title="'+$(this).find('title').text()+'">'; 	// set width
						html += ''+$(this).find('title').text()+''; 																// content
						html +=	'</div>';
					html += ( (total_days + day_space > 7) ? '' : '<div class="right"></div>'); 										// right end
				html += '<div class="clearfix"></div>';
				html += '</div>';
				html += '<div class="bar_content '+$(this).attr('styleclass')+'"><div class="inner">'+week._createContent( from_date, $(this) )+'</div></div>';
				// set position on content rows
				var id = week._makeArray( null, ele, html)
				if(id) // if we have a row id then we want to save this for future use
					$(this).attr('row', id);
			}
		});
		//alert('_setContent end');
		// do create element 
		this._createDisplay( ele );
	}, 
	
	_createContent : function( from_date , node )
	{
		return node.find('content').text();
		/*
		var from =  from_date.substring(6)+ "|"+from_date.substring(4,6) +"|"+from_date.substring(2,4)
		
		var html = "";
		html += '<div class="info_image"><img src="'+$(node).find('image').text()+'" alt="'+$(node).find('image_alt').text()+'" width=84 height=84 /></div>';
		html += '<div class="info_general">';
			html += '<a href="'+$(node).find('link').text()+'" alt="'+$(node).find('title').text()+'"><h2>'+$(node).find('title').text()+'</h2></a>';
			html += '<div class="info_broadcast"><img class="logo" width="23" height="18" alt="Last Chance to watch and listen" src="../images/site_logos/'+$(node).attr('type_index')+'_small.gif"/><p>'+$(node).find('broadcast_detail').text()+'</p></div>';
			html += '<div class="info_toolbar">'
				
				if($(node).find('listen_link').text() != "") 	html += this._createMediaLink( 'listen', 	$(node).find('listen_link') );
				if($(node).find('watch_link').text() != "") 	html += this._createMediaLink( 'watch', 	$(node).find('watch_link') );
				if($(node).find('pictures_link').text() != "") 	html += this._createMediaLink( 'look', 		$(node).find('pictures_link') );
				
			html += '</div>';
			html += '<div class="info_cal">';
				html += '<p>Add this to your calendar</p>';
				html += '<div class="ui-week-row-export">'; 
					html += '<a href="'+this._createGoogleCalEvent( $(node) )+'" title="add event to google calendar"><img src="../images/site_general/cal_google.gif" alt="google icon"/></a>';
					html += '<a href="'+this._createYahooCalEvent( $(node) )+'" title="add event to yahoo"><img src="../images/site_general/cal_yahoo.gif" alt="google icon"/></a>';
				html += '</div>';
			html += '</div>';
		html += '</div>';
		html += '<div class="info_detail">'+$(node).find('description').text()+'</div>';
		html += '<div class="clearfix"></div>';
		return html;
		*/
	},
	
	
	_createDisplay : function( element ) {
		//alert('_createDisplay');
		// interate through row array attach appropriate element
		for (var i = 0; i < this._rowArray.length; i++) {
			var item = this._rowArray[i]; // get item
			$(element).append( (item != null) ? item : jQuery(document.createElement("div")).addClass('bar-row spacer') ); //add either the content element or spacer element
		};
		this._rowArray = [ ]; // clear rowArray for next go
	},
	
	_makeArray : function ( row,  ele,  html ) {
		//alert('_makeArray');
		// make element for usage
		var n_bar = jQuery(document.createElement("div")).addClass('bar-row toast').append( html );
		
		if(row){
			for (var k=0; k < row; k++)
					if(!this._rowArray[k]) this._rowArray[k] = null;
			this._rowArray[row] = n_bar;
		}else{
			// add to first avaliable space
			for (var i=0; i < this._rowArray.length; i++) {
				if(this._rowArray[i] == null)
				{
					this._rowArray[i] = n_bar;
					return i;
				}
			};
			// if none then add to end
			this._rowArray.push(n_bar);
			return this._rowArray.length-1;
		}

	},
	
	_createGoogleCalEvent : function( xmlNode ){
		var link = "";
		link += 'http://www.google.com/calendar/event?';
		link += 'action=TEMPLATE';
		link += '&text='+escape(xmlNode.find('title').text());
		link += '&dates='+xmlNode.attr('from')+'T'+xmlNode.attr('from_time')+'Z'+'/'+xmlNode.attr('to')+'T'+xmlNode.attr('to_time')+'Z';
		link += '&location='+escape(xmlNode.find('location').text());
		link += '&sprop=website:'+escape(xmlNode.find('link').text());
		link += '&sprop=name:'+escape(xmlNode.find('title').text());
		link += '&details='+escape(xmlNode.find('info').text());
		return link;
	},
	
	
	_createYahooCalEvent : function( xmlNode ){		
		var link = "";
		link += 'http://calendar.yahoo.com?v=60';
		link += '&VIEW=d'
		link += '&in_loc='+escape(xmlNode.find('location').text());
		link += '&in_csz='+escape(xmlNode.find('city').text());
		link += '&type=20';
		link += '&TITLE='+escape(xmlNode.find('title').text());
		link += '&ST='+xmlNode.attr('from')+'T'+xmlNode.attr('from_time')+'Z';
		link += '&DUR='+xmlNode.find('duration').text();
		link += '&URL='+escape(xmlNode.find('link').text());
		link += '&DESC='+escape(xmlNode.find('info').text());
		return link;
	}
	
	

});

function ellipsis( ) {

	$('.ellipsis').each(function(){	
		var w = $(this).width();
	    var t = $(this).text();
	    $(this).html("<span>" + t + "</span>");
		var e = $('span', this);
	
	   while (t.length > 0 && e.width() >= w) {
	     	t = t.substr(0, t.length - 1);
	     	e.html(t + "...");
	   }
	})

}

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props)
		if (props[name] == null)
			target[name] = null;
	return target;
};

/* 
	Invoke the week functionality.
    @param  options  String - a command, optionally followed by additional parameters or
                    Object - settings for attaching new week functionality
    @return  jQuery object 
*/
$.fn.week = function(options) {
	
	var intInstaceId = options.id;
 	$.week[intInstaceId] = new Week(options.id); // singleton instance	

	// new contruction and options
	var otherArgs = Array.prototype.slice.call(arguments, 1);
	if (typeof options == 'string') {
		return $.week[intInstaceId]['_' + options + 'Week'].apply($.week[intInstaceId], [this[0]].concat(otherArgs));
	}
	return this.each(function() {
		if(typeof options == 'string')
		{
			$.week[intInstaceId]['_' + options + 'Week'].apply($.week[intInstaceId], [this].concat(otherArgs));
		}
		else
		{		
			$.week[intInstaceId]._attachWeek(this,options); 
			//alert('wtf1');
		}
	});
	
};

$.week = {}; //array of singleton instance for all calendars displayed
$.intCurCalendarInstanceId;

/* Initialise the week. */
$(document).ready(function() {
	
});

})(jQuery);
