//
// Function liveRegion() is 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 (id string) id is the html id of the div to bind to.
//
// @param (func callback) func is the callback function to execute at the specified interval
//
// @param (interval integer) interval is the initial update interval
//
// @return N/A
//
function liveRegion(id, func, interval) {
// define widget properties
this.$id = $('#' + id); // the jquery object of the live region
this.func = func; // the callback to execute after each interval
this.interval = interval; // the interval to use for the timer
this.timer = null; // the interval timer object
// set the initial live region aria attributes. Check if the user
// has specified values in the html markup. If so, use those
// values.
this.busy = false; // set to true while the region is updating
// create the update timer
this.createTimer();
} // end liveRegion constructor
//
// Function createTimer() is a member function to create an interval timer for
// the live region widget.
//
// @return N/A
//
liveRegion.prototype.createTimer = function() {
if (this.timer != null) {
// destroy the existing timer
this.destroyTimer();
}
//
// Function destroyTimer() is a member function to remove the interval timer for
// the live region widget.
//
// @return N/A
//
liveRegion.prototype.destroyTimer = function() {
if (this.timer != null) {
clearInterval(this.timer);
this.timer = null;
}
} // end createTimer()
//
// Function setInterval() is 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 (interval integer) interval is the new interval, in minutes, to set
//
// @return N/A
//
liveRegion.prototype.setInterval = function(interval) {
this.interval = interval;
this.createTimer();
} // end setInterval()
//
// Function setPoliteness() is a member function to set the aria-polite attribute of
// the live region.
//
// @param (val string) val is one of three possible states: 'off', 'polite', 'assertive';
//
// @return N/A
//
liveRegion.prototype.setPoliteness = function(val) {
this.$id.attr('aria-live', val);
} // end setPoliteness()
//
// Function setAtomic() is a member function to set the aria-atomic attribute of
// the live region.
//
// @param (val boolean) val is true if live region updates should be atomic
//
// @return N/A
//
liveRegion.prototype.setAtomic = function(val) {
this.$id.attr('aria-atomic', val);
} // end setAtomic()
//
// Function setRelevant() is a member function to set the aria-relevant attribute of
// the live region.
//
// @param (val string) val may be 'additions', 'removals', or 'text'. It may also be a
// space seperated combination of these.
//
// @return N/A
//
liveRegion.prototype.setRelevant = function(val) {
<script type="text/javascript">
var g_seconds = 0;
$(document).ready(function() {
// call init() to reset the select elements - in case of page reload
init();
// set the globabal second countdown variable before we create the associated
// live region
g_seconds = $('#intervalSelect').val() * 60 - 1;
var timerRegion = new liveRegion('timer', 'countdown()', 1000);
var rssRegion = new liveRegion('rssFeedContent', 'getRssFeed()', $('#intervalSelect').val() * 60000);
// do initial rss grab
getRssFeed();
//////////////////// bind event handlers for rssRegion controls /////////////////////////////
// bind a change event handler for the RSS feed select
$('#rssFeedSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// get the new feed
getRssFeed();
}
e.stopPropagation();
return false;
});
// bind a change event handler for the interval select
$('#intervalSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the new timer interval
rssRegion.setInterval($newItem.val() * 60000);
// bind a change event handler for the politeness select
$('#rssPolitenessSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the politeness level
rssRegion.setPoliteness($newItem.val());
}
e.stopPropagation();
return false;
});
// bind a change event handler for the atomic updates select
$('#rssAtomicSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the atomic update setting
rssRegion.setAtomic($newItem.val());
}
e.stopPropagation();
return false;
});
// bind a change event handler for the update relevance select
$('#rssRelevantSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the update relevance setting
rssRegion.setRelevant($newItem.val());
}
e.stopPropagation();
return false;
});
//////////////////// bind event handlers for timer controls /////////////////////////////
// bind a change event handler for the politeness select
$('#timerPolitenessSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the politeness level
timerRegion.setPoliteness($newItem.val());
}
e.stopPropagation();
return false;
});
// bind a change event handler for the atomic updates select
$('#timerAtomicSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the atomic update setting
timerRegion.setAtomic($newItem.val());
}
e.stopPropagation();
return false;
});
// bind a change event handler for the update relevance select
$('#timerRelevantSelect').change(function(e) {
var $newItem = null;
// select the new item
$newItem = selectItem($(this), $(this).find('option'), $(this).val());
if ($newItem != null) {
// set the update relevance setting
timerRegion.setRelevant($newItem.val());
}
e.stopPropagation();
return false;
});
}); // end ready
//
// Function init() is a function to initialize the select elements to match their activedescendant values. This
// ensures that any page reloads do not introduce erroneous aria attribute values
function init() {
var elems = new Array('rssFeedSelect', 'intervalSelect', 'rssPolitenessSelect', 'rssAtomicSelect',
'rssRelevantSelect', 'timerPolitenessSelect', 'timerAtomicSelect', 'timerRelevantSelect');
var $id = null;
var val = null;
for (ndx in elems) {
// get the jQuery object of select to set
$id = $('#' + elems[ndx]);
// get the value of the active descendant
val = $('#' + $id.attr('aria-activedescendant')).val();
// set the value of the select
$id.val(val);
}
} // end init()
//
// Function selectItem() is a function to iterate through an option list to find a new option and select it.
// The function also updates the aria-seleced attribute of the items.
//
// @param ($select object) $select is the jquery object of the select that the option list is part of.
//
// @param ($list object) $list is the list of options to iterate through.
//
// @param (val string) val is the value to find in the list.
//
// @return (object) returns the jquery object of the new item selected. Returns NULL if not found.
//
function selectItem($select, $list, val) {
var $prevItem = $('#' + $select.attr('aria-activedescendant'));
var $newItem = null;
// find the list item associated with the new value
$list.each(function() {
if ($(this).val() == val) {
$newItem = $(this);
}
});
if ($newItem == null) {
// no new item found
return null;
}
// set the aria-selected attribute to false for the previously selected item
$prevItem.attr('aria-selected', 'false')
// set the aria-selected attribute to true for the new item
$newItem.attr('aria-selected', 'true')
// update the active descendent of the select
$select.attr('aria-activedescendant', $newItem.attr('id'));
// return the new item selected
return $newItem;
} // end selectItem()
//
// Function countdown() is a callback to update the countdown live region. This callback is passed to
// rssRegion.
//
// @return N/A
//
function countdown () {
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
$('#timer').text('updating');
}
else {
$('#timer').text(minutes + ':' + seconds);
g_seconds--;
}
}
//
// Function getRssFeed() is 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.
//
// Note: This function uses the jquery.jGFeed plugin in order to get around the same-domain security
// barrier for AJAX.
//
// @return N/A
//
function getRssFeed() {
var $id = $('#rssFeedContent');
var $topEntry = $id.find('.entry').first();
$.jGFeed($('#rssFeedSelect').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>';