jQuery: generating a random link from a Google sitemap

Generating a random link from a Google sitemap is quite simple with jQuery. First of all, we need to define some method and properties within our class:

/**   @name SiteManager
      @access Public
      
      Main class to handle site contents */
      


var SiteManager = new function () {
    
    var that = this;
    
    var pageURL = location.href;
    var pageTitle = document.title;
    
    this.execute = function(callback) {
        
 
 return callback.method(callback.parameters);
    };
    
    
    var isActualContent = function(term) {
    
        
 return that.execute({
 
     parameters: pageTitle,
     method: function(parameters) {
     
         if(parameters.indexOf(term) == -1) {
  
      return true;
      
  } else {
  
      return false;
      
  }
     
     
     }
 
 
 });
    
    
    };
    
    this.XHR = function(type, url, callback) {
    
 url = 'http://www.yoursite.com/' + url;
 
 switch(type) {
     case 'xml':
       return $.get(url, callback);
       break;
       
     case 'html':
       return $.ajax({
    url: url,
    data: null,
    dataType: 'html',
    success: callback
       });
       break;
     
       
     default:
         break;     
     
 
 }
    
    
    
    };

Then we can create our custom method to fetch the data we need from the sitemap:

this.getRelatedResources = function() {
        
     var related = [];
        if(isActualContent('Home')) {
        return this.execute({
     
     parameters: [$('#content-sub > h2:first-child'), pageURL, '<div class="resource res single-res"><h3>Related resource</h3><ul><li>'],
     method: function(parameters) {
     
         return that.XHR('xml', 'sitemap.xml', function(data) {
  
      $(data).find('loc').each(function() {
      
         var text = $(this).text();

               var rawURL = parameters[1].replace('http://www.yoursite.com/', '').replace('index.html', '');
         
         var urlChunks = rawURL.split('/');
         
         var baseDir = urlChunks[0];
         var currentDir = urlChunks[1];

         
         if(text.indexOf(baseDir) != -1) {
         
             if(text.indexOf(currentDir) == -1) {
         
                 
          var re = /\/.+\.html$/;
          var link;
          
          if(re.test(text)) {
          
              link = '';
          
          } else {
          
          
              related.push(text);
          
          }
          
          
          
   
          
      }
          
         
         }
      
      });
      
      
      var currentIndex = Math.floor(Math.random() * (related.length));
      
      var html = parameters[2];
      
      html += '<a href="' + related[currentIndex] + '">Read</a></li></ul></div>';
      
      
      $(html).insertAfter('#content-sub h2:first-child + div.resource');
      
      
  });
     
     }
     
 });
 
 } else {
 
     return;
     
 }
    
    
    
    };

We fetch our sitemap through Ajax and we parse all the URLs by filtering the URL of the current page. Then all this data is pushed into the related array. Finally, we create a random index for this array and we extract only a single link. Do more with less!

Parsing a Google Sitemap with jQuery

A Google Sitemap has the following form:

<?xml version="1.0" encoding="UTF-8"?>
<urlset
      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">

<url>
  <loc>http://www.css-zibaldone.com/</loc>
  <priority>1.00</priority>
  <lastmod>2008-08-10T06:03:54+00:00</lastmod>
  <changefreq>monthly</changefreq>
</url>

<!-- more url elements -->

</urlset>

Here we're interested in the loc and lastmod elements which return the URI of the documents and the date of their last modification, respectively. First, let's set up some basic styles:

body {
    margin: 0 auto;
    width: 60%;
    padding: 2em 0;
    background: #fff;
    color: #333;
    font: 76% Arial, sans-serif;
}

a:link, a:visited {color: #080;}

h1 {
    font: normal 1.6em "Trebuchet MS", Trebuchet, sans-serif;
    color: #666;
    margin: 5px 0;
    padding-bottom: 3px;
    border-bottom: 3px solid #999;
    text-transform: uppercase;
}

#sitemap {
    margin: 1em 0;
    padding: 5px;
    border: 2px solid #c4df9b;
    background: #edf5e1;
    list-style: none;
    font-size: 1.1em;
}

#sitemap li {
    margin-bottom: 6px;
}

#sitemap li div.lastmod {
    font-family: Verdana, sans-serif;
    font-size: small;
    height: 100%;
    margin-bottom: 4px;
    padding: 3px 0;
    border-top: 1px dashed #666;
    border-bottom: 1px dashed #666;
    font-style: italic;
}

Then we add jQuery. To accomplish our task, we first need an helper function to parse the dates provided in the sitemap. It is as follows:

function formatDate(timestamp) {

   var dateParts = timestamp.split('-');
   
   var year = dateParts[0];
   var rawMonth = dateParts[1];
   var month;
   var day = dateParts[2];
   
   switch(rawMonth) {
   
       case '01':
         month = 'January';
         break;
       case '02':
         month = 'February';
         break;
       case '03':
         month = 'March';
         break;
       case '04':
         month = 'April';
         break;
       case '05':
         month = 'May';
         break;
       case '06':
         month = 'June';
         break;
        case '07':
         month = 'July';
         break;
       case '08':
         month = 'August';
         break;
       case '09':
         month = 'September';
         break;
        case '10':
         month = 'October';
         break;
       case '11':
         month = 'November';
         break;
       case '12':
         month = 'December';
         break;
       default:
         break;

   
   
   }
  
   return month + ' ' + day + ',' + ' ' + year;  
}

This function parses a date in the format yyyy-mm-dd by splitting it into three different parts which will be later returned in the format 'm d, y'. Now it's time to use jQuery to parse our sitemap effectively:

$(document).ready(function() {

    $('<ul id="sitemap"></ul>').insertAfter('h1');

    $.get('sitemap.xml', function(xml) {
    
        $(xml).find('loc').each(function() {
 
 var rawUrl = $(this).text();
 var rawLastMod = $(this).parent().find('lastmod').text();
 var $url;
 var $lastMod;
 var timestamp;
 var $li = $('<li></li>');
 
 if(rawUrl.indexOf('index.html') != -1) {
 
      $url = rawUrl.replace('index.html', '');
 
 } else {
 
      $url = rawUrl;
 
 }
 
 timestamp = rawLastMod.replace(/T.+/g, '');
 $lastMod = formatDate(timestamp);
 
 $li.html('<a href="' + $url + '">' + $url + '</a>' + '<div class="lastmod">Last Modified: ' + $lastMod + '</div>');
 
 $li.appendTo('#sitemap');
 
 
 
 
        });
    
    
    });


});

We use the $.get() method to fetch and parse our sitemap file. During parsing, we make sure that URLs passed in don't contain the string 'index.html'. If so, we remove it. We also make sure that the last modification date passed to the formatDate() function is in the format 'yyyy-mm-dd'. To do this, we remove the last part of the lastmod text node beginning with a 'T'. You can see the final result here.

PHP Google sitemap generator for static websites

A couple of months ago a client of mine asked me to create a sitemap for his website. I knew the fact that Google sitemaps are generally considered a good SEO practice in indexing websites, so I decided to create it using some Google tools. Unfortunately, I wasn't able to use the Python script provided by Google, and other online tools had some restrictions. Since this website was actually a static website with a few pages inside, I created a PHP class to create the sitemap. Here it is:

SiteMap.class.php

<?php
   class SiteMap {
    private $_re; 
    protected $_seen = array();
    
    
    /** @param String a PCRE */
    
    
    public function setPCRE($regexp) {
    
        $this->_re = $regexp;
 
    }
    
            
    /**@param String monthly, daily, weekly, etc. It's optional for a sitemap
              @return String The <changefreq/> element **/
    
    public function setChangeFrequency($freq) {
       if(isset($freq)) {
       
          if(is_string($freq)) {
    
            return '<changefreq>' . $freq . '</changefreq>' . "\n";
     
   } else {
       return '';
   }       
      
       }
       
       
       
       
    
    }
    
    /** @param String A number between 0.0 and 1.0. It's optional for a sitemap
               @return String The <priority/> element */
    
    public function setPriority($priority) {
    
         if(isset($priority)) {
 
      if(is_string($priority)) {
 
       return '<priority>' . $priority . '</priority>' . "\n";
      }
      else {
        return '';
      }
 }
   
   
   
 
    }
    
    
    /** @param String An <urlset/> element
              @return String The root element and its namespace */
        
    public function setNS($ns) {
    
    
        if(isset($ns)) {
       
          if(is_string($ns)) {
    
            return '<urlset ' . $ns . '>' . "\n";
     
   } else {
       return '<urlset>' . "\n";
   }       
      
       }
    
    
    }
    
    /** @return Bool Check if the directory separator is / or not */
    
    public function isWindows() {
       if(DIRECTORY_SEPARATOR == '\\') {
           return true;
        } else {
           return false;
        }
    } 
    
    
    
    
    
    /** @param String The name of a directory
              @return Array An array of documents that matches the PCRE defined in SiteMap::RE **/

    public function searchDir($dir) {
        $pages = array();
        $dirs = array();
        $this->_seen[realpath($dir)] = true;
        try {
            foreach (new RecursiveIteratorIterator(new
                RecursiveDirectoryIterator($dir)) as $file) {
                if ($file->isFile() && $file->isReadable() && (! isset($this->_seen[$file->getPathname()]))) {
                    $this->_seen[$file->getPathname()] = true;
      $doc_url = $file->getPathname();
      
      
      
                if (preg_match($this->_re, $doc_url)) {
                
  $uri = substr_replace($file->getPathname(), '', 0, strlen($_SERVER['DOCUMENT_ROOT']));
  if($this->isWindows()) {
        $uri = str_replace(DIRECTORY_SEPARATOR, '/', $uri);
  }
  $lastmod = strftime('%m-%d-%Y', filemtime($doc_url));
  
  
                if (preg_match($this->_re, $doc_url)) {
                    array_push($pages, array($uri, $lastmod));
                } else {
                    array_push($pages, array($uri,$uri));
                }
      }

        }
            }

        } catch (Exception $e) {
            // Problem
        }

    return $pages;
   }

}

Basically, this class uses PHP directory iterators to work with static HTML files. It extracts their URLs and the time of the last modification using regular expressions and return them as an array (through the searchDir() method). Here's a basic usage:

Use of the SiteMap class

<?php
header('Content-Type: text/xml');
require_once('SiteMap.class.php');


$sitemap = new SiteMap();
$sitemap->setPCRE('/\.html$/');
$changefreq = $sitemap->setChangeFrequency('daily');
$priority = $sitemap->setPriority('1.0');


$matching_pages = array();
$search_dirs = array('/');  // Insert '/' if you want to start from the root

foreach ($search_dirs as $dir) {
    if($sitemap->isWindows()) {
       $matching_pages = array_merge($matching_pages,$sitemap->searchDir($_SERVER['DOCUMENT_ROOT'] . $dir));
    } else {
        $matching_pages = array_merge($matching_pages,$sitemap->searchDir($_SERVER['DOCUMENT_ROOT'] . '/'. $dir));
    }
}


echo '<?xml version="1.0" encoding="UTF-8"?>' ."\n";     


if(count($matching_pages) > 0){

    echo $sitemap->setNS('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"');
    
    foreach ($matching_pages as $k => $v) {
        if(preg_match('/index\.html|index\.php/', $v[0])) {
            $v[0] = preg_replace('/index\.html|index\.php/', '', $v[0]);
        }
 
 
        
        echo sprintf("<url>\n<loc>http://{$_SERVER['HTTP_HOST']}%s</loc>\n<lastmod>%s</lastmod>\n%s\n%s</url>\n", $v[0], $v[1], $changefreq, $priority);     
        
    }
    
    echo '</urlset>';
} else {
    echo '<error>No documents found.</error>';
    
} 
?>

The above script generates the sitemap. Notice, however, that this class is especially useful for small or medium websites. If you want to use it on bigger sites, you'd better to set a timeout limit for the script because of the memory limits of your PHP configuration (set in php.ini).