/** * 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. */ var pagePlayer = null; function PagePlayer() { /* global soundManager, window, document, navigator, setTimeout, Metadata, PP_CONFIG */ 'use strict'; 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' ? o.className.match(new RegExp('(\\s|^)' + cStr + '(\\s|$)')) : false); }; this.addClass = function(o, cStr) { if (!o || !cStr || self.hasClass(o, cStr)) { return; // safety net } o.className = (o.className ? o.className + ' ' : '') + cStr; }; this.removeClass = function(o, cStr) { if (!o || !cStr || !self.hasClass(o, cStr)) { return; } 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 || document).querySelectorAll(qs); } : function(className, tagNames, oParent) { var node = (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; } 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; } 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; } try { document.title = (sTitle ? sTitle + ' - ' : '') + self.pageTitle; } catch(e) { // oh well self.setPageTitle = function() {}; } }; 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; } 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; } 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; oTemp.innerHTML = oString + ' | 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?')) + ''; setTimeout(function() { oTemp.innerHTML = oString; }, 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; } 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; } 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; } 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; } 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), 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 return true; } // OK, we're dealing with a link if (!o.href || (!sm.canPlayLink(o) && !self.hasClass(o, 'playable')) || self.hasClass(o, 'exclude')) { // do nothing, don't return anything. return true; } // 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; self.stopEvent(e); return false; } return true; }; this.handleStatusClick = function(e) { self.setPosition(e); if (!pl.hasClass(self.lastSound._data.oLI, self.css.sPaused)) { self.resume(); } self.stopEvent(e); return false; }; 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; } 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); } 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; if (!c || typeof c.getContext === 'undefined') { return null; } ctx = c.getContext('2d'); if (!ctx || typeof c.toDataURL !== 'function') { return null; } // just in case.. try { c.toDataURL('image/png'); } catch(e) { // no canvas or no toDataURL() return null; } // 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.html5Only && 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. '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', ' %s1 / %s2', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ', '
    ' ].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); });