1/** 2 * SoundManager 2 Demo: Play MP3 links via button 3 * ---------------------------------------------- 4 * 5 * http://schillmania.com/projects/soundmanager2/ 6 * 7 * A simple demo making MP3s playable "inline" 8 * and easily styled/customizable via CSS. 9 * 10 * A variation of the "play mp3 links" demo. 11 * 12 * Requires SoundManager 2 Javascript API. 13 */ 14 15/*jslint white: false, onevar: true, undef: true, nomen: false, eqeqeq: true, plusplus: false, bitwise: true, regexp: false, newcap: true, immed: true */ 16/*global document, window, soundManager, navigator */ 17 18function BasicMP3Player() { 19 var self = this, 20 pl = this, 21 sm = soundManager, // soundManager instance 22 isTouchDevice = (navigator.userAgent.match(/ipad|iphone/i)), 23 isIE = (navigator.userAgent.match(/msie/i)); 24 this.excludeClass = 'button-exclude'; // CSS class for ignoring MP3 links 25 this.links = []; 26 this.sounds = []; 27 this.soundsByURL = {}; 28 this.indexByURL = {}; 29 this.lastSound = null; 30 this.soundCount = 0; 31 32 this.config = { 33 // configuration options 34 playNext: false, // stop after one sound, or play through list until end 35 autoPlay: false // start playing the first sound right away 36 }; 37 38 this.css = { 39 // CSS class names appended to link during various states
40 sDefault: 'sm2_button', // default state 41 sLoading: 'sm2_loading', 42 sPlaying: 'sm2_playing', 43 sPaused: 'sm2_paused' 44 }; 45 46 // event + DOM utils 47 48 this.includeClass = this.css.sDefault; 49 50 this.addEventHandler = (typeof window.addEventListener !== 'undefined' ? function(o, evtName, evtHandler) { 51 return o.addEventListener(evtName,evtHandler,false); 52 } : function(o, evtName, evtHandler) { 53 o.attachEvent('on'+evtName,evtHandler); 54 }); 55 56 this.removeEventHandler = (typeof window.removeEventListener !== 'undefined' ? function(o, evtName, evtHandler) { 57 return o.removeEventListener(evtName,evtHandler,false); 58 } : function(o, evtName, evtHandler) { 59 return o.detachEvent('on'+evtName,evtHandler); 60 }); 61 62 this.classContains = function(o,cStr) { 63 return (typeof(o.className)!=='undefined'?o.className.match(new RegExp('(\\s|^)'+cStr+'(\\s|$)')):false); 64 }; 65 66 this.addClass = function(o,cStr) { 67 if (!o || !cStr || self.classContains(o,cStr)) { 68 return false; 69 } 70 o.className = (o.className?o.className+' ':'')+cStr; 71 }; 72 73 this.removeClass = function(o,cStr) { 74 if (!o || !cStr || !self.classContains(o,cStr)) { 75 return false; 76 } 77 o.className = o.className.replace(new RegExp('( '+cStr+')|('+cStr+')','g'),''); 78 }; 79 80 this.getSoundByURL = function(sURL) { 81 return (typeof self.soundsByURL[sURL] !== 'undefined' ? self.soundsByURL[sURL] : null); 82 }; 83 84 this.isChildOfNode = function(o,sNodeName) { 85 if (!o || !o.parentNode) { 86 return false; 87 } 88 sNodeName = sNodeName.toLowerCase(); 89 do { 90 o = o.parentNode; 91 } while (o && o.parentNode && o.nodeName.toLowerCase() !== sNodeName); 92 return (o.nodeName.toLowerCase() === sNodeName ? o : null); 93 }; 94 95 this.events = { 96 97 // handlers for sound events as they're started/stopped/played 98 99 play: function() { 100 pl.removeClass(this._data.oLink,this._data.className); 101 this._data.className = pl.css.sPlaying; 102 pl.addClass(this._data.oLink,this._data.className); 103 }, 104 105 stop: function() { 106 pl.removeClass(this._data.oLink,this._data.className); 107 this._data.className = ''; 108 }, 109 110 pause: function() { 111 pl.removeClass(this._data.oLink,this._data.className); 112 this._data.className = pl.css.sPaused; 113 pl.addClass(this._data.oLink,this._data.className); 114 }, 115 116 resume: function() { 117 pl.removeClass(this._data.oLink,this._data.className); 118 this._data.className = pl.css.sPlaying; 119 pl.addClass(this._data.oLink,this._data.className); 120 }, 121 122 finish: function() { 123 pl.removeClass(this._data.oLink,this._data.className); 124 this._data.className = ''; 125 if (pl.config.playNext) { 126 var nextLink = (pl.indexByURL[this._data.oLink.href]+1); 127 if (nextLink<pl.links.length) { 128 pl.handleClick({'target':pl.links[nextLink]}); 129 } 130 } 131 } 132 133 }; 134 135 this.stopEvent = function(e) { 136 if (typeof e !== 'undefined' && typeof e.preventDefault !== 'undefined') { 137 e.preventDefault(); 138 } else if (typeof window.event !== 'undefined') { 139 window.event.returnValue = false; 140 } 141 return false; 142 }; 143 144 this.getTheDamnLink = (isIE) ? function(e) { 145 // I really didn't want to have to do this. 146 return (e && e.target ? e.target : window.event.srcElement); 147 } : function(e) { 148 return e.target; 149 }; 150 151 this.handleClick = function(e) { 152 // a sound link was clicked 153 if (typeof e.button !== 'undefined' && e.button>1) { 154 // ignore right-click 155 return true; 156 } 157 var o = self.getTheDamnLink(e), 158 sURL, 159 soundURL, 160 thisSound; 161 if (o.nodeName.toLowerCase() !== 'a') { 162 o = self.isChildOfNode(o,'a'); 163 if (!o) { 164 return true; 165 } 166 } 167 sURL = o.getAttribute('href'); 168 if (!o.href || !soundManager.canPlayLink(o) || self.classContains(o,self.excludeClass)) { 169 return true; // pass-thru for non-MP3/non-links 170 } 171 if (!self.classContains(o,self.includeClass)) { 172 return true; 173 } 174 sm._writeDebug('handleClick()'); 175 soundURL = (o.href); 176 thisSound = self.getSoundByURL(soundURL); 177 if (thisSound) { 178 // already exists 179 if (thisSound === self.lastSound) { 180 // and was playing (or paused) 181 thisSound.togglePause(); 182 } else { 183 // different sound 184 thisSound.togglePause(); // start playing current 185 sm._writeDebug('sound different than last sound: '+self.lastSound.id); 186 if (self.lastSound) { 187 self.stopSound(self.lastSound); 188 } 189 } 190 } else { 191 // create sound 192 thisSound = sm.createSound({ 193 id:'basicMP3Sound'+(self.soundCount++), 194 url:soundURL, 195 onplay:self.events.play, 196 onstop:self.events.stop, 197 onpause:self.events.pause, 198 onresume:self.events.resume, 199 onfinish:self.events.finish, 200 type:(o.type||null) 201 }); 202 // tack on some custom data 203 thisSound._data = { 204 oLink: o, // DOM node for reference within SM2 object event handlers 205 className: self.css.sPlaying 206 }; 207 self.soundsByURL[soundURL] = thisSound; 208 self.sounds.push(thisSound); 209 if (self.lastSound) { 210 // stop last sound 211 self.stopSound(self.lastSound); 212 } 213 thisSound.play(); 214 } 215 self.lastSound = thisSound; // reference for next call 216 return self.stopEvent(e); 217 }; 218 219 this.stopSound = function(oSound) { 220 soundManager.stop(oSound.id); 221 if (!isTouchDevice) { // iOS 4.2+ security blocks onfinish() -> playNext() if we set a .src in-between(?) 222 soundManager.unload(oSound.id); 223 } 224 }; 225 226 this.init = function() { 227 sm._writeDebug('basicMP3Player.init()'); 228 var i, j, 229 foundItems = 0, 230 oLinks = document.getElementsByTagName('a'); 231 // grab all links, look for .mp3 232 for (i=0, j=oLinks.length; i<j; i++) { 233 if (self.classContains(oLinks[i],self.css.sDefault) && !self.classContains(oLinks[i],self.excludeClass)) { 234 // self.addClass(oLinks[i],self.css.sDefault); // add default CSS decoration - good if you're lazy and want ALL MP3/playable links to do this 235 self.links[foundItems] = (oLinks[i]); 236 self.indexByURL[oLinks[i].href] = foundItems; // hack for indexing 237 foundItems++; 238 } 239 } 240 if (foundItems>0) { 241 self.addEventHandler(document,'click',self.handleClick); 242 if (self.config.autoPlay) { 243 self.handleClick({target:self.links[0],preventDefault:function(){}}); 244 } 245 } 246 sm._writeDebug('basicMP3Player.init(): Found '+foundItems+' relevant items.'); 247 }; 248 249 this.init(); 250 251} 252 253var basicMP3Player = null; 254 255// use HTML5 audio for MP3/MP4, if available 256soundManager.preferFlash = false;
257 258soundManager.onready(function() { 259 // soundManager.createSound() etc. may now be called 260 basicMP3Player = new BasicMP3Player(); 261});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.