Skip to content

Instantly share code, notes, and snippets.

@ryngonzalez
Created December 15, 2012 02:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryngonzalez/4290730 to your computer and use it in GitHub Desktop.
Save ryngonzalez/4290730 to your computer and use it in GitHub Desktop.
A small library for HTML5 Audio that executes a given function at particular time during playback.
/**
* Copyright 2012 - Ryan Gonzalez - @ryngonzalez - ryan@ionizedmedia.com
*
* Director.js
*
* Takes an audio element and an
* object of functions and corresponding
* times to execute them during
* audio playback.
*
* @version 0.0.1
*/
;(function(exports){
"use strict";
/**
* Set variables for easy access.
*/
var document = exports.document;
/**
* Create a director object.
*/
exports.director = function(selector, opts) {
return new Director(director.select(selector), opts);
};
/**
* Director version. SUPER ALPHA.
*/
exports.director.version = '0.0.1'
/**
* Default element selection utilized by `director(selector)`.
* Must be an audio element.
*
* Override to implement your own selection, for example
* with jQuery one might write:
*
* director.select = function(selector) {
* return jQuery(selector).get(0);
* };
*
* @param {Object|String} selector
* @return {Element}
* @api public
*/
director.select = function(selector){
if ('string' != typeof selector) return selector;
var element = document.getElementById(selector) ||
document.querySelectorAll(selector)[0];
if(element.tagName != "AUDIO") return selector;
return element;
};
/**
* Creates a new EventEmitter with an empty object of callbacks.
*/
function EventEmitter() {
this.callbacks = {};
}
/**
* Attach `fn` to a given `event` to be executed later.
*
* @params {String} event
* @params {Function} fn
*/
EventEmitter.prototype.on = function(event, fn) {
(this.callbacks[event] = this.callbacks[event] || []).push(fn);
return this;
};
/**
* For a given event, execute all of its callbacks, with optional arguments.
*
* @params {String} event
* @params {Mixed} args
*/
EventEmitter.prototype.emit = function(event) {
var args = Array.prototype.slice.call(arguments, 1),
callbacks = this.callbacks[event];
if (callbacks) {
for (var i = 0; i < callbacks.length ; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
EventEmitter.extend = function(object){
var props = ['on', 'emit'];
for(var i = 0; i < props.length; i ++){
destObject.prototype[props[i]] = EventEmitter.prototype[props[i]];
}
};
/**
* Initilizes a new `Director` from a given `el`.
*
* @params {String} el
* @api public
*/
// exports.youtube = {
// event: "timeupdate",
// play: function(media) {
// media.playVideo();
// EventEmitter.extend(media);
// setTimeout(function(){
// media.emit("timeupdate");
// }, 10);
// },
// currentTime: function(media) { return media.getCurrentTime(); }
// }
var audio = {
event: "timeupdate",
play: function(media) { return media.play(); },
pause: function(media) { return media.pause(); },
paused: function(media) { return media.paused; },
currentTime: function(media) { return media.currentTime; },
}
exports.Director = function Director(el, opts) {
if(!(this instanceof Director)) return new Director(el);
EventEmitter.call(this);
this.el = director.select(el);
this.module = audio;
this._actions = [];
this._duration = 0;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
Director.prototype = new EventEmitter;
Director.prototype.constructor = Director;
/**
* Perform this `fn` at `time` of the audio selected.
*
* @params {Object} options
* @params {Number} time
* @params {Function} fn
* @params {Number} duration
* @api public
*/
Director.prototype.at = function (options) {
this._actions.push(options);
return this;
}
/**
* Sorts the array based on timestamps.
*
* @api public
*/
Director.prototype.sort = function () {
this._actions.sort(function(a,b){
return a.time - b.time;
});
return this;
}
/**
* Duration collects the total duration of the actions attached to the audio.
*
* @api private
*/
Director.prototype.duration = function () {
if (this._actions.length) {
var duration = 0;
for (var i = this._actions.length - 1; i >= 0; i--) {
duration += this._actions[i].duration;
};
return this._duration = duration;
}
return 0;
}
/**
* Defer the given `fn` until the animation
* is complete. `fn` may be one of the following:
*
* - a function to invoke
* - an instanceof `Director` to call `.end()`
* - nothing, to return a clone of this `Director` instance for chaining
*
* @param {Function|Director} fn
* @return {Director} for chaining
* @api public
*/
Director.prototype.then = function(fn){
// invoke .end()
if ('function' == typeof fn) {
this.on('end', fn);
}
return this;
};
/**
* Plays the song, then performs the actions attached.
*
* @api public
*/
Director.prototype.play = function (fn) {
var module = this.module,
media = this.el,
self = this;
// self.duration();
console.log(media.paused);
console.log(module);
module.paused(media) ? module.play(media) : module.pause(media)
var action = this._actions.shift();
this.on(action.time, action.fn);
if (module.currentTime(media) === 0) {
media.addEventListener(module.event, function() {
if (module.currentTime(media) > action.time) {
self.emit(action.time);
setTimeout(function(){
self.emit('end');
if (self._actions.length) {
action = self._actions.shift();
} else { action = {}; }
self.on(action.time, action.fn);
}, action.duration);
}
});
}
return this;
}
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment