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