SoundManager 2 Demo: “Page as playlist” UI
http://schillmania.com/projects/soundmanager2/
An example of a Muxtape.com-style UI, where an
unordered list of MP3 links becomes a playlist
Flash 9 “MovieStar” edition supports MPEG4
audio as well.
Requires SoundManager 2 Javascript API.
|
/*jslint white: false, onevar: true, undef: true, nomen: false, eqeqeq: true, plusplus: false, bitwise: true, newcap: true, immed: true */
/*global soundManager, window, document, navigator, setTimeout, attachEvent, Metadata, PP_CONFIG */
var pagePlayer = null;
function PagePlayer() {
var self = this,
pl = this,
sm = soundManager, // soundManager instance
_event,
vuDataCanvas = null,
controlTemplate = null,
_head = document.getElementsByTagName('head')[0],
spectrumContainer = null,
|
sniffing for favicon stuff, IE workarounds and touchy-feely devices
|
ua = navigator.userAgent,
supportsFavicon = (ua.match(/(opera|firefox)/i)),
isTouchDevice = (ua.match(/ipad|ipod|iphone/i)),
cleanup;
|
configuration options
note that if Flash 9 is required, you must set soundManager.flashVersion = 9 in your script before this point.
|
this.config = {
usePeakData: false, // [Flash 9 only]: show peak data
useWaveformData: false, // [Flash 9 only]: enable sound spectrum (raw waveform data) - WARNING: CPU-INTENSIVE: may set CPUs on fire.
useEQData: false, // [Flash 9 only]: enable sound EQ (frequency spectrum data) - WARNING: Also CPU-intensive.
fillGraph: false, // [Flash 9 only]: draw full lines instead of only top (peak) spectrum points
allowRightClick: true, // let users right-click MP3 links ("save as...", etc.) or discourage (can't prevent.)
useThrottling: true, // try to rate-limit potentially-expensive calls (eg. dragging position around)
autoStart: false, // begin playing first sound when page loads
playNext: true, // stop after one sound, or play through list until end
updatePageTitle: true, // change the page title while playing sounds
emptyTime: '-:--', // null/undefined timer values (before data is available)
useFavIcon: false // try to show peakData in address bar (Firefox + Opera) - may be too CPU heavy
};
this.css = { // CSS class names appended to link during various states
sDefault: 'sm2_link', // default state
sLoading: 'sm2_loading',
sPlaying: 'sm2_playing',
sPaused: 'sm2_paused'
};
this.sounds = [];
this.soundsByObject = [];
this.lastSound = null;
this.soundCount = 0;
this.strings = [];
this.dragActive = false;
this.dragExec = new Date();
this.dragTimer = null;
this.pageTitle = document.title;
this.lastWPExec = new Date();
this.lastWLExec = new Date();
this.vuMeterData = [];
this.oControls = null;
this._mergeObjects = function(oMain,oAdd) {
|
non-destructive merge
|
var o1 = {}, o2, i, o; // clone o1
for (i in oMain) {
if (oMain.hasOwnProperty(i)) {
o1[i] = oMain[i];
}
}
o2 = (typeof oAdd === 'undefined'?{}:oAdd);
for (o in o2) {
if (typeof o1[o] === 'undefined') {
o1[o] = o2[o];
}
}
return o1;
};
_event = (function() {
var old = (window.attachEvent && !window.addEventListener),
_slice = Array.prototype.slice,
evt = {
add: (old?'attachEvent':'addEventListener'),
remove: (old?'detachEvent':'removeEventListener')
};
function getArgs(oArgs) {
var args = _slice.call(oArgs), len = args.length;
if (old) {
args[1] = 'on' + args[1]; // prefix
if (len > 3) {
args.pop(); // no capture
}
} else if (len === 3) {
args.push(false);
}
return args;
}
function apply(args, sType) {
var element = args.shift(),
method = [evt[sType]];
if (old) {
element[method](args[0], args[1]);
} else {
element[method].apply(element, args);
}
}
function add() {
apply(getArgs(arguments), 'add');
}
function remove() {
apply(getArgs(arguments), 'remove');
}
return {
'add': add,
'remove': remove
};
}());
|
event + DOM utilities
|
this.hasClass = function(o, cStr) {
return (typeof(o.className)!=='undefined'?new RegExp('(^|\\s)'+cStr+'(\\s|$)').test(o.className):false);
};
this.addClass = function(o, cStr) {
if (!o || !cStr || self.hasClass(o,cStr)) {
return false; // safety net
}
o.className = (o.className?o.className+' ':'')+cStr;
};
this.removeClass = function(o, cStr) {
if (!o || !cStr || !self.hasClass(o,cStr)) {
return false;
}
o.className = o.className.replace(new RegExp('( '+cStr+')|('+cStr+')','g'),'');
};
this.select = function(className, oParent) {
var result = self.getByClassName(className, 'div', oParent||null);
return (result ? result[0] : null);
};
this.getByClassName = (document.querySelectorAll ? function(className, tagNames, oParent) { // tagNames: string or ['div', 'p'] etc.
var pattern = ('.'+className), qs;
if (tagNames) {
tagNames = tagNames.split(' ');
}
qs = (tagNames.length > 1 ? tagNames.join(pattern+', ') : tagNames[0]+pattern);
return (oParent?oParent:document).querySelectorAll(qs);
} : function(className, tagNames, oParent) {
var node = (oParent?oParent:document), matches = [], i, j, nodes = [];
if (tagNames) {
tagNames = tagNames.split(' ');
}
if (tagNames instanceof Array) {
for (i=tagNames.length; i--;) {
if (!nodes || !nodes[tagNames[i]]) {
nodes[tagNames[i]] = node.getElementsByTagName(tagNames[i]);
}
}
for (i=tagNames.length; i--;) {
for (j=nodes[tagNames[i]].length; j--;) {
if (self.hasClass(nodes[tagNames[i]][j], className)) {
matches.push(nodes[tagNames[i]][j]);
}
}
}
} else {
nodes = node.all||node.getElementsByTagName('*');
for (i=0, j=nodes.length; i<j; i++) {
if (self.hasClass(nodes[i],className)) {
matches.push(nodes[i]);
}
}
}
return matches;
});
this.isChildOfClass = function(oChild, oClass) {
if (!oChild || !oClass) {
return false;
}
while (oChild.parentNode && !self.hasClass(oChild,oClass)) {
oChild = oChild.parentNode;
}
return (self.hasClass(oChild,oClass));
};
this.getParentByNodeName = function(oChild, sParentNodeName) {
if (!oChild || !sParentNodeName) {
return false;
}
sParentNodeName = sParentNodeName.toLowerCase();
while (oChild.parentNode && sParentNodeName !== oChild.parentNode.nodeName.toLowerCase()) {
oChild = oChild.parentNode;
}
return (oChild.parentNode && sParentNodeName === oChild.parentNode.nodeName.toLowerCase()?oChild.parentNode:null);
};
this.getOffX = function(o) {
|
http://www.xs4all.nl/~ppk/js/findpos.html
|
var curleft = 0;
if (o.offsetParent) {
while (o.offsetParent) {
curleft += o.offsetLeft;
o = o.offsetParent;
}
}
else if (o.x) {
curleft += o.x;
}
return curleft;
};
this.getTime = function(nMSec, bAsString) {
|
convert milliseconds to mm:ss, return as object literal or string
|
var nSec = Math.floor(nMSec/1000),
min = Math.floor(nSec/60),
sec = nSec-(min*60);
|
if (min === 0 && sec === 0) return null; // return 0:00 as null
|
return (bAsString?(min+':'+(sec<10?'0'+sec:sec)):{'min':min,'sec':sec});
};
this.getSoundByObject = function(o) {
return (typeof self.soundsByObject[o.id] !== 'undefined'?self.soundsByObject[o.id]:null);
};
this.getPreviousItem = function(o) {
|
given playlist item, find previous and then
|
if (o.previousElementSibling) {
o = o.previousElementSibling;
} else {
o = o.previousSibling; // move from original node..
while (o && o.previousSibling && o.previousSibling.nodeType !== 1) {
o = o.previousSibling;
}
}
if (o.nodeName.toLowerCase() !== 'li') {
return null;
} else {
return o.getElementsByTagName('a')[0];
}
};
this.playPrevious = function(oSound) {
if (!oSound) {
oSound = self.lastSound;
}
if (!oSound) {
return false;
}
var previousItem = self.getPreviousItem(oSound._data.oLI);
if (previousItem) {
pl.handleClick({target:previousItem}); // fake a click event - aren't we sneaky. ;)
}
return previousItem;
};
this.getNextItem = function(o) {
|
given playlist item, find next and then
|
if (o.nextElementSibling) {
o = o.nextElementSibling;
} else {
o = o.nextSibling; // move from original node..
while (o && o.nextSibling && o.nextSibling.nodeType !== 1) {
o = o.nextSibling;
}
}
if (o.nodeName.toLowerCase() !== 'li') {
return null;
} else {
return o.getElementsByTagName('a')[0];
}
};
this.playNext = function(oSound) {
if (!oSound) {
oSound = self.lastSound;
}
if (!oSound) {
return false;
}
var nextItem = self.getNextItem(oSound._data.oLI);
if (nextItem) {
pl.handleClick({target:nextItem}); // fake a click event - aren't we sneaky. ;)
}
return nextItem;
};
this.setPageTitle = function(sTitle) {
if (!self.config.updatePageTitle) {
return false;
}
try {
document.title = (sTitle?sTitle+' - ':'')+self.pageTitle;
} catch(e) {
|
oh well
|
self.setPageTitle = function() {
return false;
};
}
};
this.events = {
|
handlers for sound events as they’re started/stopped/played
|
play: function() {
pl.removeClass(this._data.oLI,this._data.className);
this._data.className = pl.css.sPlaying;
pl.addClass(this._data.oLI,this._data.className);
self.setPageTitle(this._data.originalTitle);
},
stop: function() {
pl.removeClass(this._data.oLI,this._data.className);
this._data.className = '';
this._data.oPosition.style.width = '0px';
self.setPageTitle();
self.resetPageIcon();
},
pause: function() {
if (pl.dragActive) {
return false;
}
pl.removeClass(this._data.oLI,this._data.className);
this._data.className = pl.css.sPaused;
pl.addClass(this._data.oLI,this._data.className);
self.setPageTitle();
self.resetPageIcon();
},
resume: function() {
if (pl.dragActive) {
return false;
}
pl.removeClass(this._data.oLI,this._data.className);
this._data.className = pl.css.sPlaying;
pl.addClass(this._data.oLI,this._data.className);
},
finish: function() {
pl.removeClass(this._data.oLI,this._data.className);
this._data.className = '';
this._data.oPosition.style.width = '0px';
|
play next if applicable
|
if (self.config.playNext) {
pl.playNext(this);
} else {
self.setPageTitle();
self.resetPageIcon();
}
},
whileloading: function() {
function doWork() {
this._data.oLoading.style.width = (((this.bytesLoaded/this.bytesTotal)*100)+'%'); // theoretically, this should work.
if (!this._data.didRefresh && this._data.metadata) {
this._data.didRefresh = true;
this._data.metadata.refresh();
}
}
if (!pl.config.useThrottling) {
doWork.apply(this);
} else {
var d = new Date();
if (d && d-self.lastWLExec > 50 || this.bytesLoaded === this.bytesTotal) {
doWork.apply(this);
self.lastWLExec = d;
}
}
},
onload: function() {
if (!this.loaded) {
var oTemp = this._data.oLI.getElementsByTagName('a')[0],
oString = oTemp.innerHTML,
oThis = this;
oTemp.innerHTML = oString+' <span style="font-size:0.5em"> | Load failed, d\'oh! '+(sm.sandbox.noRemote?' Possible cause: Flash sandbox is denying remote URL access.':(sm.sandbox.noLocal?'Flash denying local filesystem access':'404?'))+'</span>';
setTimeout(function(){
oTemp.innerHTML = oString;
|
pl.events.finish.apply(oThis); // load next
|
},5000);
} else {
if (this._data.metadata) {
this._data.metadata.refresh();
}
}
},
whileplaying: function() {
var d = null;
if (pl.dragActive || !pl.config.useThrottling) {
self.updateTime.apply(this);
if (sm.flashVersion >= 9) {
if (pl.config.usePeakData && this.instanceOptions.usePeakData) {
self.updatePeaks.apply(this);
}
if (pl.config.useWaveformData && this.instanceOptions.useWaveformData || pl.config.useEQData && this.instanceOptions.useEQData) {
self.updateGraph.apply(this);
}
}
if (this._data.metadata) {
d = new Date();
if (d && d-self.lastWPExec>500) {
this._data.metadata.refreshMetadata(this);
self.lastWPExec = d;
}
}
this._data.oPosition.style.width = (((this.position/self.getDurationEstimate(this))*100)+'%');
} else {
d = new Date();
if (d-self.lastWPExec>30) {
self.updateTime.apply(this);
if (sm.flashVersion >= 9) {
if (pl.config.usePeakData && this.instanceOptions.usePeakData) {
self.updatePeaks.apply(this);
}
if (pl.config.useWaveformData && this.instanceOptions.useWaveformData || pl.config.useEQData && this.instanceOptions.useEQData) {
self.updateGraph.apply(this);
}
}
if (this._data.metadata) {
this._data.metadata.refreshMetadata(this);
}
this._data.oPosition.style.width = (((this.position/self.getDurationEstimate(this))*100)+'%');
self.lastWPExec = d;
}
}
}
}; // events{}
this.setPageIcon = function(sDataURL) {
if (!self.config.useFavIcon || !self.config.usePeakData || !sDataURL) {
return false;
}
var link = document.getElementById('sm2-favicon');
if (link) {
_head.removeChild(link);
link = null;
}
if (!link) {
link = document.createElement('link');
link.id = 'sm2-favicon';
link.rel = 'shortcut icon';
link.type = 'image/png';
link.href = sDataURL;
document.getElementsByTagName('head')[0].appendChild(link);
}
};
this.resetPageIcon = function() {
if (!self.config.useFavIcon) {
return false;
}
var link = document.getElementById('favicon');
if (link) {
link.href = '/favicon.ico';
}
};
this.updatePeaks = function() {
var o = this._data.oPeak,
oSpan = o.getElementsByTagName('span');
oSpan[0].style.marginTop = (13-(Math.floor(15*this.peakData.left))+'px');
oSpan[1].style.marginTop = (13-(Math.floor(15*this.peakData.right))+'px');
if (sm.flashVersion > 8 && self.config.useFavIcon && self.config.usePeakData) {
self.setPageIcon(self.vuMeterData[parseInt(16*this.peakData.left,10)][parseInt(16*this.peakData.right,10)]);
}
};
this.updateGraph = function() {
if (pl.config.flashVersion < 9 || (!pl.config.useWaveformData && !pl.config.useEQData)) {
return false;
}
var sbC = this._data.oGraph.getElementsByTagName('div'),
scale, i, offset;
if (pl.config.useWaveformData) {
|
raw waveform
|
scale = 8; // Y axis (+/- this distance from 0)
for (i=255; i--;) {
sbC[255-i].style.marginTop = (1+scale+Math.ceil(this.waveformData.left[i]*-scale))+'px';
}
} else {
|
eq spectrum
|
offset = 9;
for (i=255; i--;) {
sbC[255-i].style.marginTop = ((offset*2)-1+Math.ceil(this.eqData[i]*-offset))+'px';
}
}
};
this.resetGraph = function() {
if (!pl.config.useEQData || pl.config.flashVersion<9) {
return false;
}
var sbC = this._data.oGraph.getElementsByTagName('div'),
scale = (!pl.config.useEQData?'9px':'17px'),
nHeight = (!pl.config.fillGraph?'1px':'32px'),
i;
for (i=255; i--;) {
sbC[255-i].style.marginTop = scale; // EQ scale
sbC[255-i].style.height = nHeight;
}
};
this.updateTime = function() {
var str = self.strings.timing.replace('%s1',self.getTime(this.position,true));
str = str.replace('%s2',self.getTime(self.getDurationEstimate(this),true));
this._data.oTiming.innerHTML = str;
};
this.getTheDamnTarget = function(e) {
return (e.target||(window.event?window.event.srcElement:null));
};
this.withinStatusBar = function(o) {
return (self.isChildOfClass(o,'playlist')) && (self.isChildOfClass(o,'controls'));
};
this.handleClick = function(e) {
|
a sound (or something) was clicked – determine what and handle appropriately
|
if (e.button === 2) {
if (!pl.config.allowRightClick) {
pl.stopEvent(e);
}
return pl.config.allowRightClick; // ignore right-clicks
}
var o = self.getTheDamnTarget(e),
sURL, soundURL, thisSound, oControls, oLI, str;
if (!o) {
return true;
}
if (self.dragActive) {
self.stopDrag(); // to be safe
}
if (self.withinStatusBar(o)) {
|
self.handleStatusClick(e);
|
return false;
}
if (o.nodeName.toLowerCase() !== 'a') {
o = self.getParentByNodeName(o,'a');
}
if (!o) {
|
not a link
|
|
OK, we’re dealing with a link
|
sURL = o.getAttribute('href');
if (!o.href || (!sm.canPlayLink(o) && !self.hasClass(o,'playable')) || self.hasClass(o,'exclude')) {
|
do nothing, don’t return anything.
|
|
we have something we’re interested in.
|
|
find and init parent UL, if need be
|
self.initUL(self.getParentByNodeName(o, 'ul'));
|
and decorate the link too, if needed
|
self.initItem(o);
soundURL = o.href;
thisSound = self.getSoundByObject(o);
if (thisSound) {
|
sound already exists
|
self.setPageTitle(thisSound._data.originalTitle);
if (thisSound === self.lastSound) {
|
..and was playing (or paused) and isn’t in an error state
|
if (thisSound.readyState !== 2) {
if (thisSound.playState !== 1) {
|
not yet playing
|
thisSound.play();
} else {
thisSound.togglePause();
}
} else {
sm._writeDebug('Warning: sound failed to load (security restrictions, 404 or bad format)',2);
}
} else {
|
..different sound
|
if (self.lastSound) {
self.stopSound(self.lastSound);
}
if (spectrumContainer) {
thisSound._data.oTimingBox.appendChild(spectrumContainer);
}
thisSound.togglePause(); // start playing current
}
} else {
|
create sound
|
thisSound = sm.createSound({
id:o.id,
url:decodeURI(soundURL),
onplay:self.events.play,
onstop:self.events.stop,
onpause:self.events.pause,
onresume:self.events.resume,
onfinish:self.events.finish,
type:(o.type||null),
whileloading:self.events.whileloading,
whileplaying:self.events.whileplaying,
onmetadata:self.events.metadata,
onload:self.events.onload
});
|
append control template
|
oControls = self.oControls.cloneNode(true);
oLI = o.parentNode;
oLI.appendChild(oControls);
if (spectrumContainer) {
oLI.appendChild(spectrumContainer);
}
self.soundsByObject[o.id] = thisSound;
|
tack on some custom data
|
thisSound._data = {
oLink: o, // DOM reference within SM2 object event handlers
oLI: oLI,
oControls: self.select('controls',oLI),
oStatus: self.select('statusbar',oLI),
oLoading: self.select('loading',oLI),
oPosition: self.select('position',oLI),
oTimingBox: self.select('timing',oLI),
oTiming: self.select('timing',oLI).getElementsByTagName('div')[0],
oPeak: self.select('peak',oLI),
oGraph: self.select('spectrum-box',oLI),
className: self.css.sPlaying,
originalTitle: o.innerHTML,
metadata: null
};
if (spectrumContainer) {
thisSound._data.oTimingBox.appendChild(spectrumContainer);
}
|
“Metadata”
|
if (thisSound._data.oLI.getElementsByTagName('ul').length) {
thisSound._data.metadata = new Metadata(thisSound);
}
|
set initial timer stuff (before loading)
|
str = self.strings.timing.replace('%s1',self.config.emptyTime);
str = str.replace('%s2',self.config.emptyTime);
thisSound._data.oTiming.innerHTML = str;
self.sounds.push(thisSound);
if (self.lastSound) {
self.stopSound(self.lastSound);
}
self.resetGraph.apply(thisSound);
thisSound.play();
}
self.lastSound = thisSound; // reference for next call
return self.stopEvent(e);
}
};
this.handleMouseDown = function(e) {
|
a sound link was clicked
|
if (isTouchDevice && e.touches) {
e = e.touches[0];
}
if (e.button === 2) {
if (!pl.config.allowRightClick) {
pl.stopEvent(e);
}
return pl.config.allowRightClick; // ignore right-clicks
}
var o = self.getTheDamnTarget(e);
if (!o) {
return true;
}
if (!self.withinStatusBar(o)) {
return true;
}
self.dragActive = true;
self.lastSound.pause();
self.setPosition(e);
if (!isTouchDevice) {
_event.add(document,'mousemove',self.handleMouseMove);
} else {
_event.add(document,'touchmove',self.handleMouseMove);
}
self.addClass(self.lastSound._data.oControls,'dragging');
return self.stopEvent(e);
};
this.handleMouseMove = function(e) {
if (isTouchDevice && e.touches) {
e = e.touches[0];
}
|
set position accordingly
|
if (self.dragActive) {
if (self.config.useThrottling) {
|
be nice to CPU/externalInterface
|
var d = new Date();
if (d-self.dragExec>20) {
self.setPosition(e);
} else {
window.clearTimeout(self.dragTimer);
self.dragTimer = window.setTimeout(function(){self.setPosition(e);},20);
}
self.dragExec = d;
} else {
|
oh the hell with it
|
self.setPosition(e);
}
} else {
self.stopDrag();
}
e.stopPropagation = true;
return false;
};
this.stopDrag = function(e) {
if (self.dragActive) {
self.removeClass(self.lastSound._data.oControls,'dragging');
if (!isTouchDevice) {
_event.remove(document,'mousemove',self.handleMouseMove);
} else {
_event.remove(document,'touchmove',self.handleMouseMove);
}
if (!pl.hasClass(self.lastSound._data.oLI,self.css.sPaused)) {
self.lastSound.resume();
}
self.dragActive = false;
return self.stopEvent(e);
}
};
this.handleStatusClick = function(e) {
self.setPosition(e);
if (!pl.hasClass(self.lastSound._data.oLI,self.css.sPaused)) {
self.resume();
}
return self.stopEvent(e);
};
this.stopEvent = function(e) {
if (typeof e !== 'undefined') {
if (typeof e.preventDefault !== 'undefined') {
e.preventDefault();
} else {
e.stopPropagation = true;
e.returnValue = false;
}
}
return false;
};
this.setPosition = function(e) {
|
called from slider control
|
var oThis = self.getTheDamnTarget(e),
x, oControl, oSound, nMsecOffset;
if (!oThis) {
return true;
}
oControl = oThis;
while (!self.hasClass(oControl,'controls') && oControl.parentNode) {
oControl = oControl.parentNode;
}
oSound = self.lastSound;
x = parseInt(e.clientX,10);
|
play sound at this position
|
nMsecOffset = Math.floor((x-self.getOffX(oControl)-4)/(oControl.offsetWidth)*self.getDurationEstimate(oSound));
if (!isNaN(nMsecOffset)) {
nMsecOffset = Math.min(nMsecOffset,oSound.duration);
}
if (!isNaN(nMsecOffset)) {
oSound.setPosition(nMsecOffset);
}
};
this.stopSound = function(oSound) {
sm._writeDebug('stopping sound: '+oSound.id);
sm.stop(oSound.id);
if (!isTouchDevice) { // iOS 4.2+ security blocks onfinish() -> playNext() if we set a .src in-between(?)
sm.unload(oSound.id);
}
};
this.getDurationEstimate = function(oSound) {
if (oSound.instanceOptions.isMovieStar) {
return (oSound.duration);
} else {
return (!oSound._data.metadata || !oSound._data.metadata.data.givenDuration ? (oSound.durationEstimate||0) : oSound._data.metadata.data.givenDuration);
}
};
this.createVUData = function() {
var i=0, j=0,
canvas = vuDataCanvas.getContext('2d'),
vuGrad = canvas.createLinearGradient(0, 16, 0, 0),
bgGrad, outline;
vuGrad.addColorStop(0,'rgb(0,192,0)');
vuGrad.addColorStop(0.30,'rgb(0,255,0)');
vuGrad.addColorStop(0.625,'rgb(255,255,0)');
vuGrad.addColorStop(0.85,'rgb(255,0,0)');
bgGrad = canvas.createLinearGradient(0, 16, 0, 0);
outline = 'rgba(0,0,0,0.2)';
bgGrad.addColorStop(0,outline);
bgGrad.addColorStop(1,'rgba(0,0,0,0.5)');
for (i=0; i<16; i++) {
self.vuMeterData[i] = [];
}
for (i=0; i<16; i++) {
for (j=0; j<16; j++) {
|
reset/erase canvas
|
vuDataCanvas.setAttribute('width',16);
vuDataCanvas.setAttribute('height',16);
|
draw new stuffs
|
canvas.fillStyle = bgGrad;
canvas.fillRect(0,0,7,15);
canvas.fillRect(8,0,7,15);
/*
|
shadow
|
canvas.fillStyle = 'rgba(0,0,0,0.1)';
canvas.fillRect(1,15-i,7,17-(17-i));
canvas.fillRect(9,15-j,7,17-(17-j));
*/
canvas.fillStyle = vuGrad;
canvas.fillRect(0,15-i,7,16-(16-i));
canvas.fillRect(8,15-j,7,16-(16-j));
|
and now, clear out some bits.
|
canvas.clearRect(0,3,16,1);
canvas.clearRect(0,7,16,1);
canvas.clearRect(0,11,16,1);
self.vuMeterData[i][j] = vuDataCanvas.toDataURL('image/png');
|
for debugging VU images
|
/*
var o = document.createElement('img');
o.style.marginRight = '5px';
o.src = self.vuMeterData[i][j];
document.documentElement.appendChild(o);
*/
}
}
};
this.testCanvas = function() {
|
canvas + toDataURL();
|
var c = document.createElement('canvas'),
ctx = null, ok;
if (!c || typeof c.getContext === 'undefined') {
return null;
}
ctx = c.getContext('2d');
if (!ctx || typeof c.toDataURL !== 'function') {
return null;
}
|
just in case..
|
try {
ok = c.toDataURL('image/png');
} catch(e) {
|
no canvas or no toDataURL()
|
|
assume we’re all good.
|
return c;
};
this.initItem = function(oNode) {
if (!oNode.id) {
oNode.id = 'pagePlayerMP3Sound'+(self.soundCount++);
}
self.addClass(oNode,self.css.sDefault); // add default CSS decoration
};
this.initUL = function(oULNode) {
|
set up graph box stuffs
|
if (sm.flashVersion >= 9) {
self.addClass(oULNode,self.cssBase);
}
};
this.init = function(oConfig) {
if (oConfig) {
|
allow overriding via arguments object
|
sm._writeDebug('pagePlayer.init(): Using custom configuration');
this.config = this._mergeObjects(oConfig,this.config);
} else {
sm._writeDebug('pagePlayer.init(): Using default configuration');
}
var i, spectrumBox, sbC, oF, oClone, oTiming;
|
apply externally-defined override, if applicable
|
this.cssBase = []; // optional features added to ul.playlist
|
apply some items to SM2
|
sm.useFlashBlock = true;
if (sm.flashVersion >= 9) {
sm.defaultOptions.usePeakData = this.config.usePeakData;
sm.defaultOptions.useWaveformData = this.config.useWaveformData;
sm.defaultOptions.useEQData = this.config.useEQData;
if (this.config.usePeakData) {
this.cssBase.push('use-peak');
}
if (this.config.useWaveformData || this.config.useEQData) {
this.cssBase.push('use-spectrum');
}
this.cssBase = this.cssBase.join(' ');
if (this.config.useFavIcon) {
vuDataCanvas = self.testCanvas();
if (vuDataCanvas && supportsFavicon) {
|
these browsers support dynamically-updating the favicon
|
self.createVUData();
} else {
|
browser doesn’t support doing this
|
this.config.useFavIcon = false;
}
}
} else if (this.config.usePeakData || this.config.useWaveformData || this.config.useEQData) {
sm._writeDebug('Page player: Note: soundManager.flashVersion = 9 is required for peak/waveform/EQ features.');
}
controlTemplate = document.createElement('div');
controlTemplate.innerHTML = [
|
control markup inserted dynamically after each page player link
if you want to change the UI layout, this is the place to do it.
|
' <div class="controls">',
' <div class="statusbar">',
' <div class="loading"></div>',
' <div class="position"></div>',
' </div>',
' </div>',
' <div class="timing">',
' <div id="sm2_timing" class="timing-data">',
' <span class="sm2_position">%s1</span> / <span class="sm2_total">%s2</span>',
' </div>',
' </div>',
' <div class="peak">',
' <div class="peak-box"><span class="l"></span><span class="r"></span></div>',
' </div>',
' <div class="spectrum-container">',
' <div class="spectrum-box">',
' <div class="spectrum"></div>',
' </div>',
' </div>'
].join('\n');
if (sm.flashVersion >= 9) {
|
create the spectrum box ish
|
spectrumContainer = self.select('spectrum-container',controlTemplate);
|
take out of template, too
|
spectrumContainer = controlTemplate.removeChild(spectrumContainer);
spectrumBox = self.select('spectrum-box',spectrumContainer);
sbC = spectrumBox.getElementsByTagName('div')[0];
oF = document.createDocumentFragment();
oClone = null;
for (i=256; i--;) {
oClone = sbC.cloneNode(false);
oClone.style.left = (i)+'px';
oF.appendChild(oClone);
}
spectrumBox.removeChild(sbC);
spectrumBox.appendChild(oF);
} else {
|
flash 8-only, take out the spectrum container and peak elements
|
controlTemplate.removeChild(self.select('spectrum-container',controlTemplate));
controlTemplate.removeChild(self.select('peak',controlTemplate));
}
self.oControls = controlTemplate.cloneNode(true);
oTiming = self.select('timing-data',controlTemplate);
self.strings.timing = oTiming.innerHTML;
oTiming.innerHTML = '';
oTiming.id = '';
function doEvents(action) { // action: add / remove
_event[action](document,'click',self.handleClick);
if (!isTouchDevice) {
_event[action](document,'mousedown',self.handleMouseDown);
_event[action](document,'mouseup',self.stopDrag);
} else {
_event[action](document,'touchstart',self.handleMouseDown);
_event[action](document,'touchend',self.stopDrag);
}
_event[action](window, 'unload', cleanup);
}
cleanup = function() {
doEvents('remove');
};
doEvents('add');
sm._writeDebug('pagePlayer.init(): Ready',1);
if (self.config.autoStart) {
|
grab the first ul.playlist link
|
pl.handleClick({target:pl.getByClassName('playlist', 'ul')[0].getElementsByTagName('a')[0]});
}
};
}
soundManager.useFlashBlock = true;
soundManager.onready(function() {
pagePlayer = new PagePlayer();
pagePlayer.init(typeof PP_CONFIG !== 'undefined' ? PP_CONFIG : null);
});
|