Mercurial > ff-herdict-preso
diff js/ff-herdict-preso.js @ 7:7b67d3e40479
Refactored content and DOM structure, refactored code into a syncVisualsWithAudio() function.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Wed, 10 Feb 2010 12:07:27 -0800 |
parents | 37a4eb5f9695 |
children | c9bb7e3e1821 |
line wrap: on
line diff
--- a/js/ff-herdict-preso.js Wed Feb 10 10:49:43 2010 -0800 +++ b/js/ff-herdict-preso.js Wed Feb 10 12:07:27 2010 -0800 @@ -34,61 +34,82 @@ * * ***** END LICENSE BLOCK ***** */ +// This function synchronizes any visual HTML content with the current +// position of audio content. It's a generic function that can be used +// to provide e.g. the visuals for a narrated slide-show, or +// subtitles for a movie. +// +// The options object should contain the following keys: +// +// audio - CSS selector that points to a single HTML5 +// <audio> element. The visuals will be synchronized +// with this. Required. +// +// visuals - CSS selector that points to a collection +// of DOM elements, each of which is a visual +// to be displayed at some point in the audio. Required. +// +// visibleClass - Class to add to a visual element when it is +// being displayed. Defaults to "visible". +// +// syncAttr - Attribute name on each visual element that +// identifies the time into the audio, in seconds, at +// which the visual element it's attached to should +// be displayed. Every visual element must have +// this attribute, or else an exception will be +// thrown when this function is called. Defaults to +// "data-at". +function syncVisualsWithAudio(options) { + var syncInfo = []; + var audio = $(options.audio).get(0); + var visuals = $(options.visuals); + var visibleClass = options.visibleClass || "visible"; + var syncAttr = options.syncAttr || "data-at"; + + visuals.each( + function() { + var rawTimestamp = $(this).attr(syncAttr); + var timestamp = parseFloat(rawTimestamp); + if (isNaN(timestamp)) + throw new Error("bad '" + syncAttr + "' attribute: " + + rawTimestamp); + syncInfo.push({timestamp: timestamp, visual: this}); + }); + + // Return the DOM element that should be displayed as the active + // visual the given number of seconds into the audio. + function findVisualForTime(timestamp) { + var bestVisual; + + for (var i = 0; i < syncInfo.length; i++) { + var info = syncInfo[i]; + if (info.timestamp <= timestamp) + bestVisual = info.visual; + } + + return bestVisual; + } + + // Potentially change the current visual depending on + // how far we are into the movie. + function maybeChangeVisual() { + var visual = findVisualForTime(audio.currentTime); + if (visual && !$(visual).hasClass(visibleClass)) { + visuals.removeClass(visibleClass); + $(visual).addClass(visibleClass); + } + } + + audio.addEventListener("timeupdate", maybeChangeVisual, false); + maybeChangeVisual(); +} + $(window).ready( function() { - // Ordered list of slides in the presentation. Each element - // is a JS object containing the following keys: - // - // timestamp - number of seconds into the presentation - // at which the slide should be shown. - // - // element - the DOM element containing the slide's - // content; has the HTML class 'slide'. - var slides = []; - - // The main HTML5 audio element that stores our presentation's - // soundtrack. - var audio = $("audio#main").get(0); - - // Build the 'slides' list by analyzing all slides in the DOM - // and parsing out their metadata, throwing an exception - // if we encounter any errors. - $("#slides .slide").each( - function() { - var rawTimestamp = $(this).attr("data-at"); - var timestamp = parseFloat(rawTimestamp); - if (isNaN(timestamp)) - throw new Error("bad 'data-at' attribute: " + rawTimestamp); - slides.push({timestamp: timestamp, element: this}); - }); - - // Return the DOM element that should be displayed as the active - // slide the given number of seconds into the movie. - function findSlideElementForTime(timestamp) { - var bestElement; - - for (var i = 0; i < slides.length; i++) { - var slide = slides[i]; - if (slide.timestamp < timestamp) - bestElement = slide.element; - } - - return bestElement; - } - - // Potentially change the current slide depending on - // how far we are into the movie. - function maybeChangeSlide() { - var element = findSlideElementForTime(audio.currentTime); - if (element && !$(element).hasClass("visible")) { - $("#slides .visible").removeClass("visible"); - $(element).addClass("visible"); - } - } - // Whenever the current time in the audio soundtrack - // changes, alter the current visual if necessary. This + // changes, alter the current slide if necessary. This // effectively makes the audio UI work just like a // movie UI. - audio.addEventListener("timeupdate", maybeChangeSlide, false); + syncVisualsWithAudio({audio: "audio#main", + visuals: "#slides > div"}); });