Live Region: RSS Feed

Description

Simple example of a live region widget with two timers.

Example Start

RSS Feed Controls

:
:

:
:
:

Timer Controls

:
:
:

:
 

Example End

Roles

  • log
  • option
  • timer

Properties

  • aria-activedescendant
  • aria-atomic
  • aria-live
  • aria-relevant
  • aria-selected

HTML Source Code


<div class="controls">

<h2>RSS Feed Controls</h2>

<div><label for="id_rss_feed_select">Select an RSS Feed</label>:
       <select id="id_rss_feed_select">
         <option id="id_feed1"
          value="http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml" selected>Mozilla Latest Headlines
         </option>
         <option id="id_feed2"
          value="http://news.google.com/?output=rss">Google News
         </option>
         <option id="id_feed3"
           value="http://feeds.bbci.co.uk/news/rss.xml">BBC News
         </option>
         <option id="id_feed4"
          value="http://hosted.ap.org/lineups/TOPHEADS.rss?SITE=AP&SECTION=HOME">Associated Press News
         </option>
         <option id="id_feed5"
          value="http://rss.slashdot.org/Slashdot/slashdot">Slashdot
         </option>
       </select></div>

<div><label for="id_interval_select">Select an Update Interval</label>:
       <select id="id_interval_select">
         <option value="1">1 minute</option>
         <option value="5" selected >5 minutes</option>
         <option value="10">10 minutes</option>
       </select></div>
<hr />

<div><label for="id_rss_politeness_select">Politeness Level (aria-live value)</label>:
       <select id="id_rss_politeness_select">
         <option id="id_rss_polite1" value="off">off
         </option>
         <option id="id_rss_polite2" value="polite">polite
         </option>
         <option id="id_rss_polite3" value="assertive" selected>assertive
         </option>
       </select>
</div>

<div><label for="id_rss_atomic_select">Atomic Updates</label>:
       <select id="id_rss_atomic_select">
         <option id="id_atomic_select1" value="false" selected>false
         </option>
         <option id="id_atomic_select2" value="true">true
         </option>
       </select></div>

<div><label for="id_rss_relevant_select">Change Relevance</label>:
       <select id="id_rss_relevant_select">
         <option selected value="additions" selected>additions
         </option>
         <option value="removals">removals
         </option>
         <option value="text">text
         </option>
         <option value="all">all
         </option>
       </select></div>
</div>

<div class="controls">
<h2>Timer Controls</h2>
<div><label for="id_timer_politeness_select">Politeness Level (aria-live value)</label>:
       <select id="id_timer_politeness_select">
         <option id="id_timer_polite1" value="off">off
         </option>
         <option id="id_timer_polite2" value="polite" selected>polite
         </option>
         <option id="id_timer_polite3" value="assertive">assertive
         </option>
       </select>
</div>

<div><label for="id_timer_atomic_select">Atomic Updates</label>:
       <select id="id_timer_atomic_select" role="listbox">
         <option id="id_timer_atomic1" value="false">false
         </option>
         <option id="id_timer_atomic2" value="true" selected>true
         </option>
       </select></div>

<div><label for="id_timer_relevant_select">Change Relevance</label>:
       <select id="id_timer_relevant_select">
         <option id="id_timer_relevant1" value="additions" selected>additions
         </option>
         <option id="id_timer_relevant2" value="removals">removals
         </option>
         <option id="id_timer_relevant3" value="text">text
         </option>
         <option id="id_timer_relevant4" value="all" selected>all
         </option>
       </select></div>

<hr/>
  <div id="id_countdown">
    <label for="timer">Time until next update</label>:
    <div id="id_timer"></div>
  </div>
</div>

<div id="id_rss_feed_content">
  &nbsp;
</div>

CSS Source Code


div#application {
  height: 30em;
}
div.controls {
  margin-left: 10px;
  padding: 5px 10px;
  width: 21em;
  float: left;
  clear: both;
  border-bottom: 1px solid #008;
}
button,
select {
  float: right;
}
h2 {
  clear: both;
}
div.example {
  margin-top: 50px;
  margin-left: 30px;
  width: 22em;
  float: left;
}
div#region1Container {
  margin-left: 4em;
  padding: 10px;
  width: 11em;
  height: 1.6em;
  float: left;
}
div#region1Container label {
  padding-top: 10px;
  font-weight: bold;
}
div#liveregion1 {
  margin: 0;
  padding: 2px 5px;
  float: right;
  width: 2em;
  text-align: right;
  border: 1px solid black;
}
label#region2Label {
  font-weight: bold;
  font-size: 1.2em;
}
div#liveregion2 {
  padding: 2px 5px;
  width: 22em;
  border: 1px solid black;
  height: 9em;
  overflow: auto;
}

Javascript Source Code



// jQuery reference for jGFeed
(function($){$.extend({jGFeed:function(url,fnk,num,key){if(url==null){return false;}var gurl="http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&callback=?&q="+url;if(num!=null){gurl+="&num="+num;}if(key!=null){gurl+="&key="+key;}$.getJSON(gurl,function(data){if(typeof fnk=="function"){fnk.call(this,data.responseData.feed);}else{return false;}});}});})(jQuery);

var g_seconds =  0;
var OAA_EXAMPLES = OAA_EXAMPLES ||{};  // Class of Open Ajax Alliance methods used in the examples

/**
* @method ready
*
* @desc executed when page is opened. Initializes text arrays
* and other variables required to perform later functions.
*
* @return {N/A}
*/

$(document).ready(function() {

  // set the globabal second countdown variable before we create the associated
  // live region
  g_seconds =  $('#id_interval_select').val() * 60 - 1;

  var timerRegion = new OAA_EXAMPLES.liveRegion('timer', 'OAA_EXAMPLES.countdown()', 1000);
  var rssRegion = new OAA_EXAMPLES.liveRegion('id_rss_feed_content', 'getRssFeed()', $('#id_interval_select').val() * 60000);

  // do initial rss grab
  getRssFeed();

  //////////////////// bind event handlers for rssRegion controls /////////////////////////////

  // bind a change event handler for the RSS feed select
  $('#id_rss_feed_select').change(function(e) {

    if ($(this).val() != null) {
      // get the new feed
      getRssFeed();
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the interval select
  $('#id_interval_select').change(function(e) {

    if ($(this).val() != null) {
      // set the new timer interval
      rssRegion.setInterval($(this).val() * 60000);

      // update the countdown interval
      g_seconds = $(this).val() * 60 - 1;
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the politeness select
  $('#id_rss_politeness_select').change(function(e) {

    if ($(this).val() != null) {
      // set the politeness level
      rssRegion.setPoliteness($(this).val());
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the atomic updates select
  $('#id_rss_atomic_select').change(function(e) {

    if ($(this).val() != null) {
      // set the atomic update setting
      rssRegion.setAtomic($(this).val());
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the update relevance select
  $('#id_rss_relevant_select').change(function(e) {

    if ($(this).val() != null) {
      // set the update relevance setting
      rssRegion.setRelevant($(this).val());
    }

    e.stopPropagation();
    return false;
  });

  //////////////////// bind event handlers for timer controls /////////////////////////////

  // bind a change event handler for the politeness select
  $('#id_timer_politeness_select').change(function(e) {

    if ($(this).val() != null) {
      // set the politeness level
      timerRegion.setPoliteness($(this).val());
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the atomic updates select
  $('#id_timer_atomic_select').change(function(e) {

    if ($(this).val() != null) {
      // set the atomic update setting
      timerRegion.setAtomic($(this).val());
    }

    e.stopPropagation();
    return false;
  });

  // bind a change event handler for the update relevance select
  $('#id_timer_relevant_select').change(function(e) {

    if ($(this).val() != null) {
      // set the update relevance setting
      timerRegion.setRelevant($(this).val());
    }

    e.stopPropagation();
    return false;
  });
}); // end ready


/**
* @method countdown
*
* @memberOf OAA_EXAMPLES
*
* @desc a callback to update the countdown live region. This callback is passed to rssRegion.
*
* @return {N/A}
*/

OAA_EXAMPLES.countdown = function() {
  var minutes = Math.floor(g_seconds / 60);
  var seconds = g_seconds % 60;

  if (seconds < 10) {
    seconds = '0' + seconds;
  }

  if (g_seconds == 0) {
    // do nothing - countdown will be reset by the
    // rssRegion interval timer
    $('#id_timer').text('updating');
  }
  else {
  $('#id_timer').text(minutes + ':' + seconds);
    g_seconds--;
  }
}

/**
* @method getRssFeed
*
* @memberOf OAA_EXAMPLES
*
* @desc a callback to obtain the top 5 items from an RSS feed. the function checks
* the entries obtained against those that may have been obtained previously (present in the live region).
* It will remove old entries and prepend newer ones it has received. This callback is passed to rssRegion.
*
* @return {N/A}
*/

function getRssFeed(){

  var $id = $('#id_rss_feed_content');
  var $topEntry = $id.find('.entry').first();

  $.jGFeed($('#id_rss_feed_select').val(), function(feed) {
    if(!feed) {
      alert('there was an error');
    }

    // Check to see if this is a new article
    if ($topEntry) {
      var topTitle = $topEntry.find('.storyLink').text();
      var topNdx;

      // iterate through the feed entries and find the index
      // of the matching story.
      for(topNdx = 0; topNdx < feed.entries.length; topNdx++) {
        if (topTitle == feed.entries[topNdx].title) {
          break;
        }
      }

      if (topNdx == feed.entries.length) {
        // there was no match. Clear the region and
        // append all articles
        $id.empty();
      }
      else {
        // there was a match. Remove as many articles
        // as there are new ones from the feed.
        for (var ndx = 0; ndx < topNdx; ndx++) {
          // remove the last entry
          $id.find('.entry').last().remove();
        }
      }
    }

    for (var i = topNdx - 1; i >= 0; i--) {

      var entry = feed.entries[i];
      var title = entry.title;
      var link = entry.link;
      var description = entry.contentSnippet;
      var pubDate = entry.publishedDate;

      var html = '<div class="entry"><h4 class="postTitle">'
               + '<a class="storyLink" href="' + link + '" target="_blank" tabindex="0">'
        + title + '</a>';
      html += '<br><em class="date">' + pubDate + '</em></h4><div class="summary">';
      html += '<p class="description">' + description + '</p></div></div>';
        
      $id.prepend(html);
    }
  }, 5);

  // reset cowntdown
  g_seconds = $('#id_interval_select').val() * 60 - 1;

} // end getRssFeed()

///////////////////////////////// liveRegion  Widget Definition /////////////////////////////////////

/**
* @method liveRegion
*
* @memberOf OAA_EXAMPLES
*
* @desc a constructor to define an ARIA live region widget. The widget binds to a div
* on the page and accepts a callback function that is triggered at a specified interval.
*
* By default, the live region's alert level is polite, it's updates are non-atomic, additions are relevant,
* and it uses the general channel. These default values may be overridden by either specifying different
* values in the html markup or by calling the widget's corresponding update functions.
*
* @param {string} id - the html id of the div to bind to.
*
* @param {callback} func - the callback function to execute at the specified interval
*
* @param {integer} interval - the initial update interval
*
* @return N/A
*/

/**
* @constructor liveRegion
*
* @memberOf OAA_EXAMPLES
*
* @desc Define the object properties
*
* @property {string}  - the id of the text region to update
*
* @property {Array} content - an array of text strings
* @property {object} func - the callback to execute after each interval
*
* @property {integer} interval - the interval to use for the timer
*
* @property {object} timer - the interval timer object
*/

OAA_EXAMPLES.liveRegion = function(id, func, interval) {

  // define widget properties

  this.$id = $('#' + id);

  this.func = func;
  this.interval = interval;
  this.timer = null;

  // set the initial live region aria attributes. Check if the user
  // has specified values in the html markup. If so, use those
  // values.

  var tmp = this.$id.attr('aria-live');

  if (tmp) {
    this.politeness = tmp;
  }
  else {
    this.politeness = 'polite';
  }

  tmp = this.$id.attr('aria-atomic');
  if (tmp) {
    this.atomic = tmp;
  }
  else {
    this.atomic = 'false';
  }

  tmp = this.$id.attr('aria-relevant');
  if (tmp) {
    this.relevant = tmp;
  }
  else {
    this.relevant = 'additions';
  }

  this.busy = false; // set to true while the region is updating

  // create the update timer
  this.createTimer();

} // end liveRegion constructor

/**
* @method createTimer
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to create an interval timer for
* the live region widget.
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.createTimer = function() {

  if (this.timer != null) {
    // destroy the existing timer
    this.destroyTimer();
  }

  this.timer = setInterval(this.func, this.interval);

} // end createTimer()

/**
* @method destroyTimer
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to remove the interval timer for
* the live region widget.
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.destroyTimer = function() {

  if (this.timer != null) {
    clearInterval(this.timer);    
    this.timer = null;
  }

} // end createTimer()

/**
* @method setInterval
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to change the update interval
* of the live region. This function stores the new interval, clears the current
* interval timer and creates new one with the specified interval.
*
* @param {integer} interval - the new interval, in minutes, to set
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.setInterval = function(interval) {
  this.interval = interval;

  this.createTimer();

} // end setInterval()

/**
* @method setPoliteness
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to set the aria-polite attribute of
* the live region.
*
* @param {string} val - one of three possible states: 'off', 'polite', 'assertive';
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.setPoliteness = function(val) {

  this.$id.attr('aria-live', val);
} // end setPoliteness()

/**
* @method setAtomic
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to set the aria-atomic attribute of
* the live region.
*
* @param {boolean} val - true if live region updates should be atomic
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.setAtomic = function(val) {

  this.$id.attr('aria-atomic', val);

} // end setAtomic()

/**
* @method setRelevant
*
* @memberOf OAA_EXAMPLES
*
* @desc a member function to set the aria-relevant attribute of
* the live region.
*
* @param {string} val - may be 'additions', 'removals', or 'text'. It may also be a
* space seperated combination of these.
*
* @return {N/A}
*/

OAA_EXAMPLES.liveRegion.prototype.setRelevant = function(val) {

  this.$id.attr('aria-relevant', val);

} // end setRelevant()