(function(){
		
			// UTILITY FUNCTIONS
			
			var s = window.s = function( string, values )
			{
				// Handle complex strings
				if (values !== null)
				{
					// replace each '{key}' with 'value'
					for ( key in values ){
						var regex = new RegExp("\\{"+key+"\\}",'g');
						string = string.replace( regex, values[key] );
					}
				}
			
				return string;
			}
			
			var d = window.d = function( date )
			{
				var months = ['January','February','March','April','May','June','July','August','September','October','November','December']
				var month = months[date.getMonth()];
				var date_n = date.getDate();
				var year = date.getFullYear();
				return month+' '+date_n+', '+year;
			};
			
			var t = window.t = function( date ){
				var hour = date.getHours();
				var minutes = date.getMinutes();
				if(minutes<10){
					minutes = '0'+minutes;
				}
				var ampm = 'AM';
				if(hour>12){
					hour = hour-12;
					ampm = 'PM';
				}
				return hour+':'+minutes+' '+ampm;
			};
			
			// CONTAINERS!
			
			var topBar = window.topBar = {
				modules: new Array(),
				// function for adding a module to the topbar
				add: function(title, slug, module){
					this.modules.push(module);
					module.data = null;
					module.element = document.createElement('div');
					module.element.setAttribute('class','module '+slug);
					module.element.innerHTML = (s('<b><span class="b t">{0}</span><span class="b"><img src="graphics/loader.gif" class="loader" /></span></b>',[title]));
				},
				
				init: function(){
					for(key in this.modules){
						document.getElementById("topBar").appendChild(this.modules[key].element);
						this.modules[key].init();
					}
				}
				
			}
			
			var canvas = window.canvas = {
				modules: new Array(),
				add: function(title, slug, model){
					this.modules.push(model);
					model.data = null;
					model.element = document.createElement('div');
					model.element.setAttribute('class','module '+slug);
					model.element.innerHTML = (s('<h3>{title}</h3><p style="text-align:center;"><img src="graphics/loader-on-white.gif" class="loader" /></p>',{'title':title}));
				},
				
				init: function(){
					for(key in this.modules){
						document.getElementById("canvas").appendChild(this.modules[key].element);
						this.modules[key].init();
					}
				}
			}
			
			// TOPBAR MODULES
			
			var academic_dates = {
				getData: function(){
					var that = this;
					$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=b513c542d8fa8e16d2545fab809a08f9&_render=json&_callback=?',function(data){
						that.data = data.value.items;
						that.displayData();
					});
				},
				displayData: function(){
					this.element.innerHTML = s('<a href="http://oberlin.edu/events/"><span class="b t date">{date}</span><span class="b event">{event}</span></a>',{date:d(new Date(this.data[0].pubDate)),event:this.data[0].title});
				},
				init: function(){
					this.getData();
				}
			}
			topBar.add('Upcoming','academic_date', academic_dates);
			
			var feve = {
				getData: function(){
					var that = this;
					$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=9ae4dc3536ea264fb11de57bd6323af9&_render=json&_callback=?',function(data){
						that.data = data.value.items;
						that.displayData();
					});
				},
				displayData: function(){
					this.element.innerHTML = s('<a href="http://www.thefeve.com/upstairs.html"><span class="b t">Feve Special</span><span class="b day">{day}</span><span class="b special">{special}</span></a>',{day:this.data[0].content,special:this.data[1].content});
				},
				init: function(){
					this.getData();
				}
			};
			topBar.add('Feve Special','feve',feve);
						
			var twitter = {
				getData: function(){
					var that = this;
					$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=1d3e767a5fb8386329afad148e6df736&_render=json&_callback=?',function(data){
						that.data = data.value.items;
						that.displayData();
					})
				},
				displayData: function(){
					this.element.setAttribute('class','twitter module');
					this.element.innerHTML = s('<a href="{link}"><!-- span class="t b">Latest tweet</span --><span class="b tweet">{tweet}</span></a>',{tweet:this.data[0].description.replace(/oberlin:/g,'').replace(/\{([^}]*)\}/g,'$1:'),link:this.data[0].link});

				},
				init: function(){
					this.getData();
				}
			}
			/* topBar.add('Oberlin Twitter',twitter); */
			
			var quote = {
				quotes: [
					'<a href="http://leb.net/~mira/works/prophet/prophet7.html">\&ldquo;For to be idle is to become a stranger unto the seasons, and to step out of life\u2019s procession, that marches in majesty and proud submission towards the infinite.\&rdquo;</a>',
					'<a href="http://www.oberlin.edu/external/EOG/Postcards/KatiePostcards.html"><img src="/images/soon.png" /></a>',
					'<a href="http://dcollections.oberlin.edu/cdm4/item_viewer.php?CISOROOT=/photos&CISOPTR=118&CISOBOX=1&REC=15"><img src="/images/25dollars.png" /></a>'
				],
				init: function(){
					var randkey = Math.floor(Math.random()*this.quotes.length);
					this.element.innerHTML = (this.quotes[randkey]);
					var that =  this;
					setTimeout(function(){that.init()},60000);
				}
			}
			topBar.add('Quote','quote',quote);
			
			var apollo = {
				getData: function(){
				},
				displayData: function(){
				},
				init: function(){
					this.element.innerHTML = ('<a href="http://new.oberlin.edu/apollo/"><span class="t b">At the Apollo</span> <span class="b">Schedules soon.</span></a>');
				}
			}
			topBar.add('At The Apollo','apollo',apollo);
		
			var weather = {
				weather_codes: {
						'0 ': null, // tornado
						'1 ': null, // tropical storm
						'2 ': null, // hurricane
						'3 ': null, // severe thunderstorms
						'4 ': null, // thunderstorms
						'5 ': null, // mixed rain and snow
						'6 ': null, // mixed rain and sleet
						'7 ': null, // mixed snow and sleet
						'8 ': null, // freezing drizzle
						'9 ': null, // drizzle
						'10': null, // freezing rain
						'11': null, // showers
						'12': null, // showers
						'13': null, // snow flurries
						'14': 'snow.png', // light snow showers
						'15': 'snow.png', // blowing snow
						'16': 'snow.png', // snow
						'17': null, // hail
						'18': null, // sleet
						'19': null, // dust
						'20': null, // foggy
						'21': null, // haze
						'22': null, // smoky
						'23': null, // blustery
						'24': null, // windy
						'25': null, // cold
						'26': 'cloudy.png', // cloudy
						'27': 'cloudy.png', // mostly cloudy (night)
						'28': 'cloudy.png', // mostly cloudy (day)
						'29': 'partly_cloudy_night.png', // partly cloudy (night)
						'30': 'partly_cloudy_day.png', // partly cloudy (day)
						'31': 'clear_night.png', // clear (night)
						'32': 'sunny_day.png', // sunny
						'33': 'fair_night.png', // fair (night)
						'34': 'fair_day.png', // fair (day)
						'35': null, // mixed rain and hail
						'36': 'sunny_day.png', // hot
						'37': null, // isolated thunderstorms
						'38': null, // scattered thunderstorms
						'39': null, // scattered thunderstorms
						'40': null, // scattered showers
						'41': 'snow.png', // heavy snow
						'42': null, // scattered snow showers
						'43': 'snow.png', // heavy snow
						'44': 'partly_cloudy_day.png', // partly cloudy
						'45': null, // thundershowers
						'46': null, // snow showers
						'47': null, // isolated thundershowers
						'3200': null	//not available
					},
				getData: function(){
					var that = this;
					$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=04107d06b8458149f1b23a7a3f1099f9&_render=json&_callback=?',function(data){
						that.data = data.value.items[0]['yweather:condition'];
						that.displayData();
					});
				},
				displayData: function(){
						var that = this;
						var code = this.data.code;
						this.element.innerHTML = s('<a href="http://weather.yahoo.com/forecast/USOH0722.html"><span class="b conditions">{conditions}</span> <span class="b temperature">{temperature}&deg; <span class="deg">F</span></span></a>',{conditions:this.data.text,temperature:this.data.temp});
						if(this.weather_codes[code]!==null){
							that.element.children[0].style.backgroundImage = s( 'url("weather/small/{0}")', [this.weather_codes[code]] );
						}
				},
				init: function(){
					this.getData();
					// refresh the data every 5 minutes
					var that=this;
					setInterval(function(){
						that.getData();
					},300000);				}
			};
			topBar.add('Weather','weather',weather);
			
			// CANVAS MODULES
			
			var today = {
				getData:function(){
					var that = this;
					$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=1cde20057afeb47edda6ffffe1d342c8&_render=json&_callback=?',function(data){
						that.data = data.value.items;
						that.displayData();
					})
				},
				displayData:function(){
					this.element.innerHTML = s('<h3>Today: {today}</h3>',{today: d(new Date(this.data[0].pubDate))} );
					for(key in this.data){
					var item = document.createElement('div');
					item.setAttribute('class','item');
					var date = new Date(this.data[key].pubDate);
						if(new Date(this.data[key].pubDate).getHours() != 0){ // if the hour is not 0
							item.innerHTML = s('<h4>{time} <a href="{link}">{title}</a></h4><p class="desc">{description}</p>',{title:this.data[key].title,description:this.data[key].description,link:this.data[key].link,time: t(date)});
						}else{ // otherwise, assume it's an all-day event
							item.innerHTML = s('<h4><a href="{link}">{title}</a></h4><p class="desc">{description}</p>',{title:this.data[key].title,description:this.data[key].description,link:this.data[key].link});
						}
						this.element.appendChild(item);
					}
					var morelink = document.createElement('p');
					morelink.setAttribute('class','morelink');
					morelink.innerHTML = '<a href="http://new.oberlin.edu/calendar">Events calendar \&#187;</a>';
					this.element.appendChild(morelink);
				},
				init: function(){
					this.getData();
				}
			}
			canvas.add('Today','today',today);
			
			var classifieds = {
				getData:function(){
					var that = this;
					$.getJSON('https://classifieds.oberlin.edu/output/page/1/5/json?callback=?',function(data){
						that.data = data;
						that.displayData();
					})
				},
				displayData:function(){
					var listings_html = '<h3>Oberlin Classifieds</h3>';
					for(key in this.data){ // for each listing
						// generate the html for that listing
						listings_html = listings_html + s('<div class="item"><h4><a href="{url}">{title}</a></h4><div class="desc">{description}<p><img class="email_image" src="{email_url}" /> {phone_number}</p></div></div>',{ title: this.data[key].title, description: this.data[key].description, url: this.data[key].url, email_url: this.data[key].email_image, phone_number: this.data[key].phone });
					}
					// add a more listings link
					listings_html = listings_html + ('<p><a href="http://classifieds.oberlin.edu/">More listings \&#187;</a></p>');
					// create an element to wrap the listings in
					var listings = document.createElement('div');
					// put the listings in it
					listings.innerHTML = listings_html;
					// clear out the module
					this.element.innerHTML = '';
					// fill the module with the listings html we just generated
					this.element.appendChild(listings);
				},
				init: function(){
					this.getData();
					// refresh the data every 5 minutes
					var that=this;
					setInterval(function(){
						that.getData();
					},300000);
				}
			}
			canvas.add('Oberlin Classifieds','classifieds',classifieds);
			
			// sidebar
			
			var bulletins = {
				getData: function(){
					var that = this;
					$.getJSON('http://xn--g6h.oberlist.com/ss/bb?callback=?',function(data){
						that.data = data;
						that.displayData();
					})
				},
				displayData: function(){
					var sidebar = document.getElementById('bulletins');
					sidebar.innerHTML = '';
					if(this.data.message==null){
						for(key in this.data){
							var div = document.createElement('div');
							div.setAttribute('class','item');
							div.innerHTML = s('<h4>{title}</h4><p class="message">{message}</p><p class="author">\&#8212; {email}</p>',{title: this.data[key].title, message: this.data[key].message, email: this.data[key].email});
							sidebar.appendChild(div);
						}
					}else{
						var p = document.createElement('p');
						p.innerHTML = this.data.message;
						sidebar.appendChild(p);
					}
				},
				init: function(){
					// make a post button
					$('#sideBar form').hide();
					$('#sideBar').append();
					var postbutton = $('<a href="#" class="postbutton">Post a note.</a>');
					$('#sideBar .titleBar').append(postbutton);
					postbutton.click(function(){
						$('#sideBar form').slideToggle();
						return false;
					})
					
					document.getElementById('bulletins').innerHTML = '<p style="text-align:center;"><img src="graphics/loader-on-white.gif" class="loader" /></p>';
					this.getData();
					// refresh the data every 1 minutes
					var that=this;
					setInterval(function(){
						that.getData();
					},60000);
				}
			}
			
			window.addEventListener('load',function(){
				topBar.init();
				canvas.init();
				bulletins.init();
			},false);
					
		})();