Anybody who is old enough to remember sites from before Wordpress and CMS'es knows that the number one hardship of owning and running a site was updating it. You had to go through and update every file, move content from one document to another, it all took too long and was too much trouble.
Now we all use blogging engines and CMS'es because they remove this hardship. But what do you do if you have a site and implementing one of the many engines available would be too much hassle or over-kill? You create a simple system that updates the frequent areas that you change.
This is a guide we'll be using our podcast site FS-Air. Using only PHP and jQuery we'll create everything you see on the site. This tutorial assumes you have a understanding of HTML, CSS, jQuery, and PHP.
Step 0: The Pre-face
To begin with, we are going to be using an XHTML/CSS valid website with the source layed out below. You will notice that we make reference to the scripts "automatic.php" (line 1, our PHP we will be working with first), "jquery-1.2.6.js" (line 28, the jQuery library we will be working with), "audio-player.js" (line 26, script required by our audio embed later on, and "fs-air.js" (line 29, our JavaScript file from which we will be working later on).
<?php include('automatic.php'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<meta name="author" content="Fire G" />
<meta name="description" content="Internet Podcast brought to you by Fire Studios, become a master of the web" />
<meta name="keywords" content="FS-Air, podcast, website, internet, Fire G, James White, Andrew Turner" />
<title>FS-Air</title>
<link href="style.css" rel="stylesheet" type="text/css" media="all" />
<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="feed" />
<script src="scripts/audio-player/audio-player.js" type="text/javascript"></script>
<script src="scripts/jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="scripts/fs-air.js" type="text/javascript"></script>
</head>
<body>
<div id="contain">
<div id="main">
<div id="header">
<h1>FS-Air</h1>
<a href="http://fs-air.org" title="FS-Air"><img src="images/logo.png" alt="FS-Air" /></a>
</div>
<div id="content">
<img src="images/speaker.png" alt="" id="top-speaker" />
<h2>Latest Episode</h2>
<div id="player" align="center">
</div>
<div id="show-notes">
<a title="show/hide the show notes" id="toggle-show-hide">Show Notes</a>
<div id="show-hide">
</div>
</div>
<div id="download">
<p><strong>Download/Subscribe</strong></p>
<div id="services">
<a title="iTunes" class="service">iTunes</a>
<a title="mp3" class="service">mp3</a>
<a title="Last.fm" class="service">Last.fm</a>
</div>
</div>
<div id="episode-list">
<h3>Past Episodes</h3>
<ul class="first">
<li style="display: none;"></li>
</ul>
<ul class="second">
<li style="display: none;"></li>
</ul>
</div>
<div id="footer">
<p>Copyright © 2008 <a href="http://fs-air.org" title="FS-Air">FS-Air.org</a> | All Rights Reserved <br />
Official Podcast of <a href="http://fire-studios.com" title="Fire Studios">Fire Studios</a></p>
</div>
</div>
</div>
<div id="sidebar">
<h4>Have an <br /><em>Idea</em>/<em>Suggestion</em>/<br /><em>Question</em> for for the podcast? Send it in!</h4>
<div id="contact-form">
<form name="contact" action="" method="post">
<label for="name" id="name_label">Name</label> <br />
<input type="text" name="name" id="name" size="29" value="" />
<label for="email" id="email_label">Email (not disclosed)</label> <br />
<input type="text" name="email" id="email" size="29" value="" />
<label for="website" id="website_label">Website</label> <br />
<input type="text" name="website" id="website" size="29" value="" />
<label for="message" id="message_label">Idea/Suggestion/Question</label> <br />
<textarea name="message" id="message" rows="10" cols="22"></textarea>
<input type="submit" name="submit" id="submit" value="Submit!" />
</form>
</div>
</div>
</div>
</body>
</html>
And our show-notes are layed out like so:
<span id="title">Episode 7: Social Networking</span> <h6>This week's panel:</h6> <ul> <li>Fire G - Host (<a href="http://fire-studios.com" title="Fire Studios">Fire Studios</a>)</li> <li>Andrew Turner - Panelist (<a href="http://burningstonecold.com" title="Burning Stone Cold">Burning Stone Cold</a>)</li> <li>James White - Panelist (<a href="http://james-blogs.com/" title="James' Blogs">James' Blog</a>)</li> </ul> <p>This weeks podcast is about social networking. A term that is embedded in our society so much now. We talk about the most popular social networking sites, the types of social networks out there and a interesting question of why you should join a social network.</p> <h6>Why join a social network?</h6> <p>A primary reason for many would be to stay in contact with friends, say old school friends, people in other countries or states or even just to keep in contact with current friend that you see everyday, whatever floats your boat! With social networking you have control on who you want to be friends with. A slightly less common but none the less popular reason, is to meet new people which in many cases will become great online friends. A prime example of this, is us three. Fire G, Andrew and I all met each other online, not exactly by social networking sites, but on a forum which is slightly different but the none the less similar, but we all now keep in contact with each other by various sites such as Twitter.</p> <h6>The main social networking sites:</h6> <p>In the beginning there we're only a handful of social networking sites, but now the whole internet social scene has thrived and the demand for it is high.The main social networking sites are:</p> <ul> <li>LinkedIn</li> <li>Last.FM</li> <li>MySpace</li> <li>FaceBook</li> <li>Bebo</li> <li>Twitter (We didn't actually mention it in the podcast, but it's popular, we all have it!)</li> </ul> <p>MySpace and Facebook would probably be the main two that appear if you asked the question "Name a social networking site" and the reason for this is simply popularity and well grounded they are into the whole internet social scene, you could argue that this also because they have been around for quite a while now. So they are essentially the leaders in the social networking areas. However this doesn't mean they are the best, it's all about a users opinion.</p> <h6>Different types of social networking and how it's incorporated into sites</h6> <p>To someone who doesn't follow the web that closely you would probably think social networking = friends. You partially correct, but there also different types of social networking sites incorporated into existing social networking sites. For example there are music social networking sites out there now, catering the need of music bands that maybe want to get noticed so they can use social networking to help them achieve this task.</p> <h6>The main types of social networking:</h6> <ul> <li>Music </li> <li>Professional</li> <li>General </li> <li>And of course friends</li> </ul> <p>These different groups exist to satisfy a large audience of people that may not always be looking at the whole friends aspect of social networking.</p> <h6>Problems with social networking:</h6> <p>Lets not forget social networking is and always will be a great invention, but it does have problems, one major issue is that social networking sites throw to much information at you and this can become really confusing and cause problems. This can link to another problem that occurs on some social networking sites and that is being to personal collecting to much information about you and then having some information (which you may not know about) scattered around the internet, because if you think about. Once information is on the internet, it's going to be there for a while. Even if you delete it. Fragments of it will still be scattered around and you won't be able to stop it.</p> <p>And as a final statement on problems with social networking can I just say ADS! Freaking Ads. Too many ads are flashing around and popping up on social networking sites. I know these sites have to make revenue, but come on guys. It's a bit over the top! And sometimes the ads are inappropiate for younger users which could be using a site at the time.</p> <h6>The future of social networking:</h6> <p>The web now entering the web 3 generation as well as the prospect of CSS3 coming soon the future is bright (But it's not orange) we predict that it could be a possibility that social networking sites will merge together and combine their services to bring a better user experience and reach a larger audience. In addition, we think (and it is semi-already happening) that social networking sites will improve their sites user experience and optimize their sites. Referencing to a change made by MySpace, they updated there whole layout which was more organized, cleaner looking and more functional. I don't want to sound like some man who's promoting MySpace, but there new layout was a defiant plus compared to their old one. Of course MySpace are not the only site doing it. We also think that social networking sites will become more professional and take users seriously. I know sites take user feedback and respond to it and base some of there changes upon the feedback they received, but it's not done enough and we think social networking sites will get there users more involved in how a site is run, the look and feel of it and of course the user experience.</p> <p>Well that's it for Season 1! We'll be back next week with a new series, new podcast format, and maybe some new panelists! From everyone at the FS and FS-Air team, Thanks for visiting, listening or just supporting us!</p>
Step 1: Plan out our System
We are going to be using PHP to read our episode directory containing our mp3s and show-notes (as text files) and performing 3 actions:
- Generate a list of episodes based on the mp3s
- Read the first line of the text files and get the episode title
- Dynamically generate a XML file containing all the information gathered
Then we will be using jQuery to finish the job by doing the following actions:
- Get the last episode in the list and generate the embed code, get the download links, and load show-notes
- create a list of the episodes for instant listening
- replace the current playing episode with the selected episode on click

Step 2: The PHP
We are going to be using OOP since regular PHP could not perform the functions we need it too. Let us go through this step-by-step to avoid any errors later on.
First we have to start with some OOP basics of writing a class with which we'll define all our functions, then we'll go ahead and write our output functions.
<?php
class episodeBuilder
{
// Our Code goes here
}
$episodeBuilder = new episodeBuilder( "episodes/", "list.xml" );
$episodeBuilder->constructXmlDocument();
$episodeBuilder->writeXmlDocument();
?>
Where you see 'episodeBuilder( "episodes/", "list.xml" )', that's the function we'll be using to tell our script the location of our files, "episodes/" and the name of our ending .xml file, "list.xml". The last 2 lines will initiate the processes we define in our class in this next part.
All of the PHP we will be writing from here on will be inside of our class "episodeBuilder" so our output functions have access to it all.
Now that we have our root class and output functions, we are going to need to set-up a few variables to use later on. We will be adding the prefix of "private" so that it is only manipulate-able by our script.
private $episodeDirectory; private $directoryContents; private $innerXml; private $outputXmlFile;
Once we have all this in place, we can start writing our functions to create our product. Our first function will open our location directory, read the files in that directory, generate a list of episodes from the files in that directory, and order them based on filename and extension. The syntaxes will be clear to those who know PHP, but for those who are not as knowledgeable, the code has been documented to show which parts of the code do which function.
public function __construct( $episodeDirectory, $outputXmlFile )
{
/* INIT */
if ( file_exists( $episodeDirectory ) && is_dir( $episodeDirectory ) ) // Makes sure location is real
{
$this->episodeDirectory = $episodeDirectory;
$this->outputXmlFile = $outputXmlFile;
/* Get all the files from this directory */
$dir = new DirectoryIterator( $this->episodeDirectory );
foreach ( $dir as $file ) // Loop through our files
{
if ( ! $file->isDot() ) // Don't include "." and ".." in results
{
$fileName = $file->getFileName();
$fileExt = substr( $fileName, strrpos( $fileName, '.' ) + 1 );
$fileNoExt = substr( $fileName, 0, strrpos( $fileName, '.' ) );
/* Add this file to the list */
$this->directoryContents[ $fileExt ][ $fileNoExt ] = array( $fileName, $fileNoExt );
}
}
/* Order them */ // Not going to explain
foreach ( $this->directoryContents as $k => $v )
{
$sorted = array();
$keys = array_keys( $this->directoryContents[ $fileExt ] );
natcasesort( $keys );
foreach ( $keys as $key )
{
$sorted[ $key ] = $this->directoryContents[ $fileExt ][ $key ];
}
$this->directoryContents[ $fileExt ] = $sorted;
}
}
}
Once we have our list, we need to write a function that will use that list to create our XML layout. We will use this as our end goal for our XML formatting.
<?xml version="1.0" encoding="iso-8859-1"?> <episodes> <episode id="episode-1"> <title>Episode 1: Coding in School</title> </episode> <episode id="episode-2"> <title>Episode 2: Website Startup & Launch</title> </episode> <episode id="episode-3"> <title>Episode 3: Generating Traffic</title> </episode> <episode id="episode-4"> <title>Episode 4: Developing to Standards</title> </episode> <episode id="episode-5"> <title>Episode 5: Search Engine Optimization</title> </episode> <episode id="episode-6"> <title>Episode 6: The Future of the Web</title> </episode> <episode id="episode-7"> <title>Episode 7: Social Networking</title> </episode> </episodes>
We will match this by looping through our list of files for those ending with ".mp3" and use the filename of that file as the ID of the "episode" node. Then we will go through our list looking for a ".txt" file with the same filename as the mp3 we are currently at in the list. Then we'll read the first the corresponding .txt file and get the title of the episode based on the first line of the text file. Again, the code is documented.
private function _getXmlDocument() // creates the the outer, non-dynamic part of the XML
{
static $document;
if ( ! $document )
{
$xmlHeader = '<?xml version="1.0" encoding="iso-8859-1"?>' . "\n";
$xmlHeader .= '<episodes>' . "\n";
$this->innerXml = $xmlHeader . $this->innerXml;
$this->innerXml .= '</episodes>';
$document = $this->innerXml;
}
return $document;
}
private function _manualTextBuild( $file ) // Retrieves first line value
{
$fileName = $file[0];
$fileName = $this->episodeDirectory . '/' . $fileName;
if ( $contents = @file_get_contents( $fileName ) ) // Get contents of File
{
$lines = explode( "\n", $contents ); // break file into array
$line = strip_tags( $lines[0] ); // Remove HTML tags from first line
return trim( $line ); // Return first line
}
}
public function constructXmlDocument()
{
/* INIT */
if ( sizeof( $this->directoryContents['mp3'] ) !== sizeof( $this->directoryContents['txt'] ) ) // Makes sure # of .mp3 match # of .txt
{
return;
}
else
{
/* Build MP3 files first */
foreach ( $this->directoryContents['mp3'] as $file => $mp3 ) // Loop through mp3s
{
$this->innerXml .= "\t" . '<episode id="' .$mp3[1]. '">' . "\n"; // Write first part of our desired XML code
/* Build TXT files */
if ( isset( $this->directoryContents['txt'][ $file ] ) ) // If .txt has contents
{
$fileName = $this->_manualTextBuild( $this->directoryContents['txt'][ $file ] ); // Get first line
if ( $fileName ) // If we have a value from the first line
{
$this->innerXml .= "\t\t" . '<title>' .$fileName. '</title>' . "\n"; // write part 2 of our XML
}
}
$this->innerXml .= "\t" . '</episode>' . "\n"; // Finish off our XML formatting
}
}
}
Now that we have our code being generated, it is time to write the functions that will output our code. This will use simple "fopen", "fwrite", and "fclose" commands.
public function writeXmlDocument()
{
if ( $FH = fopen( $this->outputXmlFile, 'w' ) ) // if our XML is open for editing
{
if ( fwrite( $FH, $this->_getXmlDocument() ) ) // write all our generate code
{
return true;
}
fclose( $FH ); // Close the file
}
return false; // Stop the function
}
And here's our final code all brought together (non-documented).
<?php
class episodeBuilder
{
private $episodeDirectory;
private $directoryContents;
private $innerXml;
private $outputXmlFile;
public function __construct( $episodeDirectory, $outputXmlFile )
{
/* INIT */
if ( file_exists( $episodeDirectory ) && is_dir( $episodeDirectory ) )
{
$this->episodeDirectory = $episodeDirectory;
$this->outputXmlFile = $outputXmlFile;
/* Get all the files from this directory */
$dir = new DirectoryIterator( $this->episodeDirectory );
foreach ( $dir as $file )
{
if ( ! $file->isDot() )
{
$fileName = $file->getFileName();
$fileExt = substr( $fileName, strrpos( $fileName, '.' ) + 1 );
$fileNoExt = substr( $fileName, 0, strrpos( $fileName, '.' ) );
/* Add this file to the list */
$this->directoryContents[ $fileExt ][ $fileNoExt ] = array( $fileName, $fileNoExt );
}
}
/* Order them */
foreach ( $this->directoryContents as $k => $v )
{
$sorted = array();
$keys = array_keys( $this->directoryContents[ $fileExt ] );
natcasesort( $keys );
foreach ( $keys as $key )
{
$sorted[ $key ] = $this->directoryContents[ $fileExt ][ $key ];
}
$this->directoryContents[ $fileExt ] = $sorted;
}
}
}
public function constructXmlDocument()
{
/* INIT */
if ( sizeof( $this->directoryContents['mp3'] ) !== sizeof( $this->directoryContents['txt'] ) )
{
return;
}
else
{
/* Build MP3 files first */
foreach ( $this->directoryContents['mp3'] as $file => $mp3 )
{
$this->innerXml .= "\t" . '<episode id="' .$mp3[1]. '">' . "\n";
/* Build TXT files */
if ( isset( $this->directoryContents['txt'][ $file ] ) )
{
$fileName = $this->_manualTextBuild( $this->directoryContents['txt'][ $file ] );
if ( $fileName )
{
$this->innerXml .= "\t\t" . '<title>' .$fileName. '</title>' . "\n";
}
}
$this->innerXml .= "\t" . '</episode>' . "\n";
}
}
}
public function writeXmlDocument()
{
if ( $FH = fopen( $this->outputXmlFile, 'w' ) )
{
if ( fwrite( $FH, $this->_getXmlDocument() ) )
{
return true;
}
fclose( $FH );
}
return false;
}
private function _getXmlDocument()
{
static $document;
if ( ! $document )
{
$xmlHeader = '<?xml version="1.0" encoding="iso-8859-1"?>' . "\n";
$xmlHeader .= '<episodes>' . "\n";
$this->innerXml = $xmlHeader . $this->innerXml;
$this->innerXml .= '</episodes>';
$document = $this->innerXml;
}
return $document;
}
private function _manualTextBuild( $file )
{
$fileName = $file[0];
$fileName = $this->episodeDirectory . '/' . $fileName;
if ( $contents = @file_get_contents( $fileName ) )
{
$lines = explode( "\n", $contents );
$line = strip_tags( $lines[0] );
return trim( $line );
}
}
}
$episodeBuilder = new episodeBuilder( "episodes/", "list.xml" );
$episodeBuilder->constructXmlDocument();
$episodeBuilder->writeXmlDocument();
?>
Step 3: The jQuery
Once we have our PHP creating the XML file, we can start using jQuery to create all the content for our site. Remember from our list earlier that we want to create a list of all our episodes, load the newest episode first, replace the currently playing episode with the episode chosen by the user from the list of episodes, and load the download links for the current episode.
Let us begin by creating a new JavaScript file and writing the necessary code to activate jQuery.
$(document).ready(function(event) {
// Our Code Goes Here
});
First thing we're going to do with our jQuery is hide the show-notes area once the page loads so that the user doesn't have to scroll through it to view the previous episodes.
$("#show-hide").slideUp();
Now the next thing we want to do is load the latest episode from our XML file. Luckily, jQuery is excellent at working with XML. We can simple use the "$.ajax" function and then go from there. We will configure it to do a "get" request to our "list.xml" file, which we created using the PHP and then tell it that we want to read the file as XML (which it is).
$.ajax({
type: "GET",
url: "list.xml",
dataType: "xml",
success: function(loadList) {
// Our code goes here
});
Once our file has been successfully reached and loaded we can start using the data in our jQuery, so let’s use the ":last" selector to grab the last iteration of "episode", giving us the newest episode we have released.
$(loadList).find('episode:last').each(function(latest){
// Our Code Goes here
})
Now that we have that the block of XML we need, it's time to start creating the corresponding html! First we'll get the episode number by reading the ID attribute of the XML block we're at, then we'll store that information in a variable with which we'll use to configure our urls and embed sources, and second we'll read the title of the episode based off of the value of our subnode, <title>...</title>.
var filename = $(this).attr('id'); // grab episode name
var title = $(this).find("title").text(); // grab title of episode
var source = "episodes/"+filename+".mp3"; // use episode name to get correct mp3
var notes = "episodes/"+filename+".txt"; // use episode name to get correct show-notes
// Media Player embed, uses source and title variables
$("#player").empty().append("<h4 id='episode-title'>"+title+"</h4>")
$("#player").append("<object type='application/x-shockwave-flash' data='scripts/audio-player/player.swf' id='audioplayer1' height='24' width='490'><param name='movie' value='scripts/audio-player/player.swf' /><param name='FlashVars' value='playerID=1&bg=0x8eafd1&leftbg=0x498BD4&lefticon=0x666666&rightbg=0x498BD4&rightbghover=0x1f6a9a&righticon=0xF1F1F1&righticonhover=0xFFFFFF&text=0x1f6a9a&track=0xfafafa&border=0x2b7caf&volslider=0xffffff&voltrack=0x4a4a4a&loader=0xb7f57d&skip=0xeeeeee&loop=no&autostart=no&animation=no&encode=no&initialvolume=60&remaining=no&noinfo=no&buffer=5&checkpolicy=yes&rtl=no&soundFile="+source+"&titles="+title+"&artits=FS-Air' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='wmode' value='transparent' /></object>")
// load in our show-notes, but keep them hidden
$("#show-hide").empty().slideUp().load(notes)
// Insert our download links
$("#services").empty().append("<a href='http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=298622846' title='iTunes' class='service'>iTunes</a>")
$("#services").append("<a href="+source+" title='mp3' class='service'>mp3</a>")
$("#services").append("<a href='http://www.last.fm/music/FS-Air' title='Last.fm' class='service'>Last.fm</a>")
Next on our "To Achieve" list is to load our episodes into a list on the page so that the user can click on an episode and change to that episode. Now for our page layout, we have our odd numbered posts being listed on the left, and our evens on the right in 2 separate divs. To achieve this we'll be making use of 2 more jQuery selectors ":odd" and ":even". They do exactly what their names imply! To do this we will use our "list.xml" file, but why waste time with another $.ajax call, let us just piggyback off our current one!
// Find the Even numbered episodes and insert them
$(loadList).find('episode:even').each(function(evens){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.first').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})
// Find the Odd numbered episodes and insert them
$(loadList).find('episode:odd').each(function(odds){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.second').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})

This is turning out to be much simpler than you thought aye? Here is how our code looks so far all together (un-documented).
// Start Dynamic Functions
$(document).ready(function(event) {
// Hide Show Notes
$("#show-hide").slideUp();
// End Hide Show Notes
// Load Latest Episode
$.ajax({
type: "GET",
url: "list.xml",
dataType: "xml",
success: function(loadList) {
$(loadList).find('episode:last').each(function(latest){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
var source = "episodes/"+filename+".mp3";
var notes = "episodes/"+filename+".txt";
$("#player").empty().append("<h4 id='episode-title'>"+title+"</h4>")
$("#player").append("<object type='application/x-shockwave-flash' data='scripts/audio-player/player.swf' id='audioplayer1' height='24' width='490'><param name='movie' value='scripts/audio-player/player.swf' /><param name='FlashVars' value='playerID=1&bg=0x8eafd1&leftbg=0x498BD4&lefticon=0x666666&rightbg=0x498BD4&rightbghover=0x1f6a9a&righticon=0xF1F1F1&righticonhover=0xFFFFFF&text=0x1f6a9a&track=0xfafafa&border=0x2b7caf&volslider=0xffffff&voltrack=0x4a4a4a&loader=0xb7f57d&skip=0xeeeeee&loop=no&autostart=no&animation=no&encode=no&initialvolume=60&remaining=no&noinfo=no&buffer=5&checkpolicy=yes&rtl=no&soundFile="+source+"&titles="+title+"&artits=FS-Air' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='wmode' value='transparent' /></object>")
$("#show-hide").empty().slideUp().load(notes)
$("#services").empty().append("<a href='http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=298622846' title='iTunes' class='service'>iTunes</a>")
$("#services").append("<a href="+source+" title='mp3' class='service'>mp3</a>")
$("#services").append("<a href='http://www.last.fm/music/FS-Air' title='Last.fm' class='service'>Last.fm</a>")
})
$(loadList).find('episode:even').each(function(evens){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.first').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})
$(loadList).find('episode:odd').each(function(odds){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.second').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})
}
}); // End Load List
}); // End Dynamic Functions
Now we set-up our events and functions for the user to activate. These are Showing/hiding the show-notes and Changing to the Selected Episode. To save us from having to set-up another JavaScript file, let us just create another jQuery start function in our current file and continue from there.
}); // End Dynamic Functions
// Start called funcions
$(document).ready(function(){
//our code goes here
}); //End Called Functions
Our first objective, hiding and showing the show-notes, will be accomplished through a simple jQuery command ".slideToggle();". This will give us the added bonus of a sliding animation with our showing and hiding.
// Show/Hide Notes
$("#toggle-show-hide").click(function() { // Once the user clicks
$("#show-hide").slideToggle(); // Yeah, that's it!
}); // End Show/Hide Notes
Our second objective, to change to the selected episode, is exactly like our dynamic loading script with one change, the selector. Instead of "episode:last" we're going to use "episode[id='+choice+']" which will match based on the ID of the "<episode..." in the XML, note that "choice" is a variable, not a function, so we are going to store the ID of the selected episode with "event.target.id". Here is how our code should look:
// Change to Selected Episode
$('.episode-change').live("click", function(event) {
var choice = event.target.id; // Get the ID of the selected Episode
$.ajax({ //EXACTLY the same as our dynamic code
type: "GET",
url: "list.xml",
dataType: "xml",
success: function(xml) {
// Match our result based on IDs
$(xml).find('episode[id='+choice+']').each(function(i){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
var source = "episodes/"+filename+".mp3";
var notes = "episodes/"+filename+".txt";
$("#player").empty().append("<h4 id='episode-title'>"+title+"</h4>")
$("#player").append("<object type='application/x-shockwave-flash' data='scripts/audio-player/player.swf' id='audioplayer1' height='24' width='490'><param name='movie' value='scripts/audio-player/player.swf' /><param name='FlashVars' value='playerID=1&bg=0x8eafd1&leftbg=0x498BD4&lefticon=0x666666&rightbg=0x498BD4&rightbghover=0x1f6a9a&righticon=0xF1F1F1&righticonhover=0xFFFFFF&text=0x1f6a9a&track=0xfafafa&border=0x2b7caf&volslider=0xffffff&voltrack=0x4a4a4a&loader=0xb7f57d&skip=0xeeeeee&loop=no&autostart=no&animation=no&encode=no&initialvolume=60&remaining=no&noinfo=no&buffer=5&checkpolicy=yes&rtl=no&soundFile="+source+"&titles="+title+"&artits=FS-Air' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='wmode' value='transparent' /></object>")
$("#show-hide").empty().slideUp().load(notes)
$("#services").empty().append("<a href='#' title='iTunes' class='service'>iTunes</a>")
$("#services").append("<a href="+source+" title='mp3' class='service'>mp3</a>")
$("#services").append("<a href='http://www.last.fm/music/FS-Air' title='Last.fm' class='service'>Last.fm</a>")
});
}
});
}); // End Change to Selected Episode
And now our completed jQuery:
// Start Dynamic Functions
$(document).ready(function(event) {
// Hide Show Notes
$("#show-hide").slideUp();
// End Hide Show Notes
// Load Latest Episode
$.ajax({
type: "GET",
url: "list.xml",
dataType: "xml",
success: function(loadList) {
$(loadList).find('episode:last').each(function(latest){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
var source = "episodes/"+filename+".mp3";
var notes = "episodes/"+filename+".txt";
$("#player").empty().append("<h4 id='episode-title'>"+title+"</h4>")
$("#player").append("<object type='application/x-shockwave-flash' data='scripts/audio-player/player.swf' id='audioplayer1' height='24' width='490'><param name='movie' value='scripts/audio-player/player.swf' /><param name='FlashVars' value='playerID=1&bg=0x8eafd1&leftbg=0x498BD4&lefticon=0x666666&rightbg=0x498BD4&rightbghover=0x1f6a9a&righticon=0xF1F1F1&righticonhover=0xFFFFFF&text=0x1f6a9a&track=0xfafafa&border=0x2b7caf&volslider=0xffffff&voltrack=0x4a4a4a&loader=0xb7f57d&skip=0xeeeeee&loop=no&autostart=no&animation=no&encode=no&initialvolume=60&remaining=no&noinfo=no&buffer=5&checkpolicy=yes&rtl=no&soundFile="+source+"&titles="+title+"&artits=FS-Air' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='wmode' value='transparent' /></object>")
$("#show-hide").empty().slideUp().load(notes)
$("#services").empty().append("<a href='http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=298622846' title='iTunes' class='service'>iTunes</a>")
$("#services").append("<a href="+source+" title='mp3' class='service'>mp3</a>")
$("#services").append("<a href='http://www.last.fm/music/FS-Air' title='Last.fm' class='service'>Last.fm</a>")
})
$(loadList).find('episode:even').each(function(evens){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.first').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})
$(loadList).find('episode:odd').each(function(odds){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
$('#episode-list ul.second').append('<li class="episode-change" id="'+filename+'">'+title+'</li>')
})
}
}); // End Load List
}); // End Dynamic Functions
// Start called funcions
$(document).ready(function(){
// Show/Hide Notes
$("#toggle-show-hide").click(function() {
$("#show-hide").slideToggle();
}); // End Show/Hide Notes
// Change to Selected Episode
$('.episode-change').live("click", function(event) {
var choice = event.target.id;
$.ajax({
type: "GET",
url: "list.xml",
dataType: "xml",
success: function(xml) {
$(xml).find('episode[id='+choice+']').each(function(i){
var filename = $(this).attr('id');
var title = $(this).find("title").text();
var source = "episodes/"+filename+".mp3";
var notes = "episodes/"+filename+".txt";
$("#player").empty().append("<h4 id='episode-title'>"+title+"</h4>")
$("#player").append("<object type='application/x-shockwave-flash' data='scripts/audio-player/player.swf' id='audioplayer1' height='24' width='490'><param name='movie' value='scripts/audio-player/player.swf' /><param name='FlashVars' value='playerID=1&bg=0x8eafd1&leftbg=0x498BD4&lefticon=0x666666&rightbg=0x498BD4&rightbghover=0x1f6a9a&righticon=0xF1F1F1&righticonhover=0xFFFFFF&text=0x1f6a9a&track=0xfafafa&border=0x2b7caf&volslider=0xffffff&voltrack=0x4a4a4a&loader=0xb7f57d&skip=0xeeeeee&loop=no&autostart=no&animation=no&encode=no&initialvolume=60&remaining=no&noinfo=no&buffer=5&checkpolicy=yes&rtl=no&soundFile="+source+"&titles="+title+"&artits=FS-Air' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='wmode' value='transparent' /></object>")
$("#show-hide").empty().slideUp().load(notes)
$("#services").empty().append("<a href='#' title='iTunes' class='service'>iTunes</a>")
$("#services").append("<a href="+source+" title='mp3' class='service'>mp3</a>")
$("#services").append("<a href='http://www.last.fm/music/FS-Air' title='Last.fm' class='service'>Last.fm</a>")
});
}
});
}); // End Change to Selected Episode
}); //End Called Functions
And we're done!
Conclusion:
As you can see, site automation is the ultimate convince for site owners and developers! We just brought our actions required number down from 6 to 1, uploading the episode mp3 and show-notes! I hope you all learned something about using PHP OOP to help streamline your PHP development and making it more robust, and also using jQuery to work with your DOM and also a remote XML file!


Share the Love






Nick Tompson
January 28th
Man my PHP code kicks ass in this.
James
January 28th
I see my lovely show notes got to be part of this tutorial :D I feel proud. I'll be honest some of that went straight over my head, but not because of this tutorial, because I don't have the attenion span to learn about jQuery and XML etc. But automating a system like this is pretty big! Well done!
Fire G
January 28th
@Nick Tompson: Yeah, thanks for helping me through that! Still can't believe how regular PHP just wouldn't return the results we wanted...