if (typeof(WWDC) == "undefined") WWDC = {};



WWDC.GetTime = function(time, increment) {
	for (id in time) {
		var value = time[id];
		if (value.bounds[0].lower <= increment.lower && increment.upper <= value.bounds[0].upper) {
			return value;
		}
	}

	return value;
};



WWDC.GetPlacement = function(type, inc, wrapper) {
	if (!this.width) this.width = 756; // FIX ME $$('.grid-mask')[0].getWidth();
	var fullwidth = this.width;
	if (inc.bounds) inc = inc.bounds;
	var placement = [];
	
	inc.each(function(increment) {
		var day = new Date(increment.lower).getDay();
		day = WWDC.sections.type[type.capitalize()].day[day];

		var time = WWDC.GetTime(day.time, increment);

		var scale = time.increments.length;
		var timeWrapper = wrapper.getElementsByClassName(day.id)[0].getElementsByClassName(time.id)[0];

		var scale = (time.bounds[0].upper - time.bounds[0].lower)/fullwidth;

		if (increment.listing) {
			timeWrapper = timeWrapper.getElementsByClassName('all')[0].getElementsByClassName('padder')[0];
			var style = 'left:0; width:100%;';
		} else {
			var left = WWDC.RoundToHalfHour(increment.lower) - time.bounds[0].lower;
			left = Math.ceil(left/scale)-1;

			var right = time.bounds[0].upper - WWDC.RoundToHalfHour(increment.upper);
			right = Math.ceil(right/scale);
			if (type == 'lab' && time.id == 'morning') right--;

			var width = fullwidth-right-left;
			var style = 'left:'+left+'px; width:'+width+'px;';
		}
		
		placement.push({ wrapper:timeWrapper, style:style });
	});

	return placement;
};



WWDC.Schedules = Class.create({

	initial: {
		tab: 'session',
		day: 'monday',
		time: 'afternoon'
	},

	clear: function() {
		this.items = [];
		this.loaded = 0;
		this.lastHud = false;
	},

	getInfo: function(oldInfo) {
		if (!oldInfo) oldInfo = this.initial;

		var info = {};
		if (document.location.hash) var info = document.location.hash.replace('#', '').toQueryParams();

		if (!info.tab) info.tab = oldInfo.tab;
		if (!info.day) info.day = oldInfo.day;
		if (!info.time) info.time = oldInfo.time;
		
		if (!WWDC.sections.type[info.tab.capitalize()]) info.tab = this.initial.tab;
		var tab = WWDC.sections.type[info.tab.capitalize()];

		var day = WWDC.GetDayFromEnglish[info.day];
		if (!tab || !tab.day[day]) info.day = this.initial.day;

		if (day.time && !day.time[info.time]) info.time = this.initial.time;

		return info;
	},

	initialize: function(data) {
		this.clear();
		this.data = data;
		Event.observe(window, 'keyup', this.keyevent.bind(this));
	},

	initializeDisplay: function(tabs, panels) {
		if (!tabs || !panels) return;
		this.panels = $(panels);
		
		this.huds = new Element('div', { id:'grid-huds' });
		this.panels.hud.appendChild(this.huds);

		var relative = new Element('div', { className:'relative' });
		this.huds.appendChild(relative);

		var info = this.getInfo();
		this.initializeData(info.tab);
		this.populateGrid();

		this.initializeTabs(tabs);
		this.show(info);
		
		this.setHudEvents();
	},

	initializeData: function() {
		if (Object.isArray(this.data)) {
			for (var i=0; i<this.data.length; i++) {
				var item = (this.data[i].id == 1) ? new WWDC.Schedules.Keynote(this, this.data[i]) : new WWDC.Schedules.Item(this, this.data[i]);
				this.items.push(item);
			}
		} else {
			for (data in this.data) {
				var item = (this.data[data].id == 1) ? new WWDC.Schedules.Keynote(this, this.data[data]) : new WWDC.Schedules.Item(this, this.data[data]);
				this.items.push(item);
			}
		}
	},

	populateGrid: function(tab) {
		for (var i=0; i<this.items.length; i++) {
			var item = this.items[i];

			if (tab == item.type) {
				var placement = this.getPlacement(item);
				item.initializeDisplay(placement);
			}
		}
		this.loaded++;
	},

	setHudEvents: function(item) {
		for (var i=0; i<this.items.length; i++) {
			var item = this.items[i];
			if (item.elements.length>0) {
				if (item.hud && !item.hudEvents) {
					for (var j=0; j<item.elements.length; j++) {
						item.elements[j].observe('click', this.closeHuds.bind(this, item));
					}
					item.hudEvents = true;
				}
			}
		}
	},

	getPlacement: function(item) {
		if (item.type) var wrapper = $(item.type).down('.grid-slider');
		if (wrapper) return WWDC.GetPlacement(item.type, item.data.time, wrapper);
		return false;
	},

	initializeTabs: function(tabs, initial) {
		this.tabs = tabs;

		for (var i=0; i<this.tabs.length; i++) {
			var tab = this.tabs[i];

			if (tab.wrapper.down('.cell')) tabs++;

			tab.wrapper.hide();
			tab.trigger.observe('mousedown', WWDC.UpdateUrl.bind(null, { tab:tab.id }));
			tab.trigger.observe('click', this.show.bind(this, { tab:tab.id }));
		}
	},

	beforeShow: function(loading) {
		if (loading) loading.addClassName('loading');
	},

	show: function(info) {
		var show = function() {
			var info = this.getInfo(info);

			// tabs
			for (var i=0; i<this.tabs.length; i++) {
				var tab = this.tabs[i];

				if (tab.id != info.tab) {
					tab.wrapper.hide();
					tab.trigger.removeClassName('active');
				} else {
					if (!tab.wrapper.down('.cell:not(.all)')) this.populateGrid(tab.id);
					tab.wrapper.show();
					tab.trigger.addClassName('active');

					// grid
					tab.grid.show(info);

					this.setHudEvents();
				}
			}

			this.afterShow.defer(this.panels.tab, this.panels.loading);
		}.bind(this);

		if (this.loaded < this.tabs.length) {
			Effect.Fade(this.panels.tab, { to:.3, duration:.3,
				beforeStart: this.beforeShow.bind(null, this.panels.loading),
				afterFinish: show
			});
		} else {
			show();
		}
	},

	afterShow: function(opacity, loading) {
		opacity.setOpacity('');
		if (loading) loading.removeClassName('loading');
	},

	closeHuds: function(item, evt) {
		if (this.lastHud && item != this.lastHud) this.lastHud.hideHud();
		this.lastHud = item || false;
	},

	keyevent: function(evt) {
		// esc
		if (evt.keyCode == Event.KEY_ESC) this.closeHuds();
	}

});



WWDC.Schedules.Tab = Class.create({

	clear: function() {
		this.id = false;
		this.trigger = false;
		this.wrapper = false;
		this.grid = false;
	},

	initialize: function(id, trigger, wrapper, schedule) {
		this.clear();
		this.id = id;
		this.trigger = $(trigger);
		this.wrapper = $(wrapper);
		this.schedule = schedule;

		this.initializeDisplay();
	},

	initializeDisplay: function() {
		this.grid = new WWDC.Schedules.Grid(this, this.wrapper);
	}

});



WWDC.Schedules.Grid = Class.create({

	clear: function() {
		this.tab = false;
		this.wrapper = false;
		this.lastNavItem = false;
	},

	initialize: function(tab, wrapper) {
		this.clear();
		this.tab = tab;
		this.wrapper = $(wrapper);
		this.initializeDisplay();
	},

	initializeDisplay: function() {
		this.createNav();
		this.createGrid();
	},

	createNav: function() {
		this.nav = new Element('div', { className:'nav' });

		var days = WWDC.sections.type[this.tab.id.capitalize()].day;
		for (day in days) {
			var day = days[day];
			var div = new Element('div', { className:day.id+' day' });
			this.nav.appendChild(div);

			var title = new Element('h3');
			title.innerHTML = day.title;
			div.appendChild(title);

			var list = new Element('ul');
			div.appendChild(list);

			for (time in day.time) {
				var time = day.time[time];

				var item = new Element('li');
				list.appendChild(item);

				var link = new Element('a', { className:time.id });
				link.innerHTML = time.title;
				item.appendChild(link);

				link.observe('mousedown', WWDC.UpdateUrl.bind(null, { day:day.id, time:time.id }));
				link.observe('click', this.show.bind(this, { day:day.id, time:time.id }));
			}
		}

		this.wrapper.appendChild(this.nav);
	},

	createGrid: function() {
		this.grid = new Element('div', { className:'grid' });
		this.wrapper.appendChild(this.grid);

		var mask = new Element('div', { className:'grid-mask' });
		this.grid.appendChild(mask);

		var relative = new Element('div', { className:'relative' });
		mask.appendChild(relative);

		var left = new Element('div', { className:'cap left' });
		relative.appendChild(left);

		var right = new Element('div', { className:'cap right' });
		relative.appendChild(right);

		this.slider = new Element('div', { className:'grid-slider' });
		relative.appendChild(this.slider);
		// this.drag = new Draggable(this.slider, { constraint:'horizontal',  });

		this.createRooms();
		this.createLabels();
	},

	createRooms: function() {
		var rooms = new Element('div', { className:'grid-rooms' });
		this.grid.appendChild(rooms);

		var label = new Element('div', { className:'room label' });
		rooms.appendChild(label);
		var padder = new Element('div', { className:'padder' });
		padder.innerHTML = 'Rooms';
		label.appendChild(padder);

		for (room in WWDC.room) {
			var room = WWDC.room[room];

			var div = new Element('div', { className:'room '+room.id+' '+room.type });
			rooms.appendChild(div);
			var padder = new Element('div', { className:'padder' });
			padder.innerHTML = room.title;
			div.appendChild(padder);
		}
	},

	createLabels: function() {
		var days = WWDC.sections.type[this.tab.id.capitalize()].day;
		for (day in days) {
			var day = days[day];
			var eDay = new Element('div', { className:'day '+day.id });
			this.slider.appendChild(eDay);

			for (time in day.time) {
				var time = day.time[time];

				var eTime = new Element('div', { className:'time '+time.id });
				eDay.appendChild(eTime);

				for (var i=0; i<time.increments.length; i++) {
					if (time.increments[i].listing) {
						var all = new Element('div', { className:'cell all' });
						eTime.appendChild(all);

						var padder = new Element('div', { className:'padder' });
						all.appendChild(padder);
					}
					var increment = new WWDC.Schedules.Label(time.increments[i], WWDC.GetPlacement(this.tab.id, time.increments[i], this.slider));
				}
			}
		}
	},

	show: function(info, evt) {
		if (evt) var navItem = (evt.findElement) ? evt.findElement('li') : evt;
		if (!navItem) var navItem = this.nav.down('.'+info.day).down('.'+info.time).up('li');

		if (navItem != this.lastNavItem) {
			this.tab.schedule.closeHuds();

			navItem.addClassName('active');
			if (this.lastNavItem) this.lastNavItem.removeClassName('active');
			this.lastNavItem = navItem;

			var time = this.slider.down('.'+info.day).down('.'+info.time);

			var left = time.positionedOffset().left;
			if (left == -1) left = 0;

			this.slider.morph({ left:'-'+left+'px' });
		}
	}

});



WWDC.Schedules.Item = Class.create({

	clear: function() {
		this.data = {
			time: {}
		};
		this.placement = [];
		this.elements = [];
		this.triggers = [];
		this.hud = false;
		this.type = false;
		this.session = false;
		this.lab = false;
	},

	initialize: function(schedule, data) {
		this.clear();
		
		// data
		data.time.each(function(time) {
			if (time.lower) time.lower = new Date(time.lower.replace(/-/g, '/')).getTime();
			if (time.upper) time.upper = new Date(time.upper.replace(/-/g, '/')).getTime();
		});
		if (data.room && data.room == 'Interface Design Lab') {
			if (data.title.match('iPhone')) {
				data.room = 'Interface Design Lab A';
			} else {
				data.room = 'Interface Design Lab B';
			}
		}
		if (data.description && data.description.match(/open lab hours/i)) data.description = false;
		this.data = data;

		// do we have a hud?
		this.hud = this.checkHud();

		// do we have a type?
		if (this.data.type) var type = WWDC.sections.type[this.data.type];
		if (type) {
			this.type = type.id;
			if (type.id == 'session') this.session = true;
			if (type.id == 'lab') this.lab = true;
		}
	},

	initializeDisplay: function(placement) {
		this.placement = placement;
		var className = this.getClassName(this.data);

		if (this.isVerticallyPlaced()) {
			for (var i=0; i<this.placement.length; i++) {
				var element = new Element('div', { className:className, style:this.placement[i].style });
				if (this.placement[i].wrapper) this.placement[i].wrapper.appendChild(element);
				
				var padder = new Element('div', { className:'padder' });
				element.appendChild(padder);

				var trigger = (this.hud) ? new Element('a') : new Element('span');
				trigger.innerHTML = this.data.title;
				padder.appendChild(trigger);

				this.truncate(trigger);
				trigger.innerHTML += ' ';
				trigger.appendChild(this.getTags(trigger));

				element.observe('click', this.toggleHud.bind(this, element));

				this.elements.push(element);
				this.triggers.push(trigger);
			}
		}
	},
	
	isVerticallyPlaced: function() {
		var placed = !!WWDC.room[this.data.room];
		if (!placed && this.data.time[0].listing) placed = true;
		return placed;
	},

	getClassName: function() {
		var className = '';

		var room = WWDC.room[this.data.room];
		if (room) className += room.id+' ';

		if (this.data.track) {
			this.data.track.each(function(track) {
				var track = WWDC.sections.track[track];
				if (track) className += track.id+' ';
			});
		}

		className += 'id-'+this.data.id;
		return 'cell '+className.strip();
	},

	truncate: function(element) {
		var string = element.innerHTML;
		var to = 60;

		if (string.length>to) {
			// set the tooltip to the entire title
			element.title = string;

			// truncate
			string = string.substring(0, to);
			string = string.substring(0, string.lastIndexOf(' '));
			string = string.strip();

			// remove any trailing puncutation
			var punctuation = string.match(/[,:]/g);
			if (punctuation) {
				var character = punctuation[punctuation.length-1];
				if (string.lastIndexOf(character) == string.length-1) {
					string = string.substring(0, string.length-1);
				} 
			}

			// add ellipses
			string = string.strip();
			string += '...';

			element.innerHTML = string;
		}
	},
 
	getTags: function(trigger, verbose) {
		var tags = '';
		var listing = false;
		if (trigger && trigger.up('.all')) listing = true;
		
		if (this.data.title.match(/to be announced/i) || (this.data.title.match(/open hours/i) && !verbose)) return new Element('span', { className:'tags' });

		// tracks (iphone, mac...)
		if (verbose && this.data.track) {
			this.data.track.each(function(track) {
				if (WWDC.sections.track[track]) tags += WWDC.sections.track[track].title+'/';
			});
		}

		// session id
		if (!listing) tags += this.data.id+'/';

		// categories (media, essentials...)
		if (WWDC.sections.category[this.data.category]) tags += WWDC.sections.category[this.data.category].title+'/';

		// time/room, if necessary
		if (listing) {
			tags += '<b>'+WWDC.TimeToString(this.data.time)+'</b>';
			if (WWDC.room[this.data.room]) tags += 'Room: '+WWDC.room[this.data.room].title+'/';
		}

		tags = tags.substring(0, tags.length-1);

		var tag = new Element('span', { className:'tags' });
		tag.innerHTML = tags;
		return tag;
	},

	checkHud: function() {
		if (this.data.title.match(/open hours/i) || this.data.title.match(/to be announced/i)) return false;
		if (this.data.description || this.data.track || this.data.category) return true;
		return false;
	},

	createHud: function() {
		if (this.hud) {
			this.hud = new Element('div', { className:'hud', style:'display:none;' });
			var relative = new Element('div', { className:'relative' });
			this.hud.appendChild(relative);

			var top = new Element('div', { className:'cap top' });
			relative.appendChild(top);

			var padder = new Element('div', { className:'padder' });
			relative.appendChild(padder);

			var bottom = new Element('div', { className:'cap bottom' });
			relative.appendChild(bottom);

			// title
			var title = new Element('h3')
			title.innerHTML = this.data.title;
			top.appendChild(title);

			// meta
			var time = WWDC.TimeToString(this.data.time);

			var room = '';
			if (WWDC.room[this.data.room]) var room = WWDC.room[this.data.room].title;
			var timeplace = new Element('span', { className:'time' })
			timeplace.innerHTML = time +'. '+ room;

			var tags = this.getTags(null, true);

			var meta = new Element('div', { className:'meta' });
			meta.appendChild(timeplace);
			meta.appendChild(tags);
			padder.appendChild(meta);

			// description
			if (this.data.description) {
				var description = new Element('p');
				description.innerHTML = this.data.description;
				padder.appendChild(description);
			}

			// close button
			var close = new Element('a', { className:'close' });
			close.innerHTML = 'Close';
			padder.appendChild(close);
			
			if (Object.isFunction(this.createHudOptions)) padder.appendChild(this.createHudOptions());

			// insert everything and set events
			$('grid-huds').appendChild(this.hud);

			close.observe('click', this.hideHud.bind(this));
		}
	},

	toggleHud: function(trigger) {
		if (this.hud) {
			if (Object.isElement(this.hud) && this.hud.visible()) {
				this.hideHud();
			} else {
				this.showHud(trigger);
			}
		}
	},

	showHud: function(element) {
		if (this.hud) {
			if (this.hud == true) this.createHud();

			this.hud.removeClassName('last');

			var halfday = element.up();
			var full = halfday.getDimensions();

			var position = element.positionedOffset();
			var dimensions = element.getDimensions();
			var hud = this.hud.getDimensions();

			var left = position.left - (hud.width/2 - dimensions.width/2);
			var right = full.width - (position.left + dimensions.width);
			var top = position.top - hud.height;

			if (right<100 && dimensions.width<400) this.hud.addClassName('last');
			this.hud.setStyle({ left:left+'px', top:top+'px' });

			var show = new Effect.Appear(this.hud, { duration:.2 });
		}
	},

	hideHud: function() {
		if (this.hud) {
			var hide = new Effect.Fade(this.hud, { duration:.2 });
		}
	}

});



WWDC.Schedules.Label = Class.create(WWDC.Schedules.Item, {

	initialize: function(data, placement) {
		this.clear();

		if (data) Object.extend(this.data, data);
		if (placement) Object.extend(this.placement, placement);
		
		this.initializeDisplay(placement);
	},

	isVerticallyPlaced: function() {
		return true;
	},

	getClassName: function() {
		return 'label';
	}, 

	getTags: function() {
		return new Element('span');
	}

});



WWDC.Schedules.Keynote = Class.create(WWDC.Schedules.Item, {

	getTags: function() {
		var tag = new Element('span', { className:'tags' });
		tag.innerHTML = this.data.description;
		return tag;
	},

	checkHud: function() {
		return true;
	},

	createHud: function() {
		if (this.hud) {
			this.hud = new Element('div', { className:'hud', style:'display:none;' });
			var relative = new Element('div', { className:'relative' });
			this.hud.appendChild(relative);

			var top = new Element('div', { className:'cap top' });
			relative.appendChild(top);

			var padder = new Element('div', { className:'padder' });
			relative.appendChild(padder);

			var bottom = new Element('div', { className:'cap bottom' });
			relative.appendChild(bottom);

			// title
			var title = new Element('h3')
			title.innerHTML = this.data.title;
			top.appendChild(title);

			// meta
			var day = new Date(this.data.time[0].lower).getDay();
			var time = WWDC.sections.type['Session'].day[day].title.substring(0)+', ';
			if (this.data.description) time += this.data.description;

			var room = '';
			if (WWDC.room[this.data.room]) var room = WWDC.room[this.data.room].title;
			var timeplace = new Element('span', { className:'time' })
			timeplace.innerHTML = time +'. '+ room;

			var meta = new Element('div', { className:'meta' });
			meta.appendChild(timeplace);
			padder.appendChild(meta);

			// close button
			var close = new Element('a', { className:'close' });
			close.innerHTML = 'Close';
			padder.appendChild(close);

			if (Object.isFunction(this.createHudOptions)) padder.appendChild(this.createHudOptions());

			// insert everything and set events
			$('grid-huds').appendChild(this.hud);

			close.observe('click', this.hideHud.bind(this));
		}
	}

});
w