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(function() { 16 17 /* global document, window, soundManager, navigator */ 18 19 'use strict'; 20 21 function BasicMP3Player() { 22 var self = this, 23 pl = this, 24 sm = soundManager, // soundManager instance 25 isTouchDevice = (navigator.userAgent.match(/ipad|iphone/i)), 26 isIE = (navigator.userAgent.match(/msie/i)); 27 this.excludeClass = 'button-exclude'; // CSS class for ignoring MP3 links 28 this.links = []; 29 this.sounds = []; 30 this.soundsByURL = {}; 31 this.indexByURL = {}; 32 this.lastSound = null; 33 this.soundCount = 0; 34 35 this.config = { 36 // configuration options 37 playNext: false, // stop after one sound, or play through list until end 38 autoPlay: false // start playing the first sound right away 39 }; 40 41 this.css = { 42 // CSS class names appended to link during various states
43 sDefault: 'sm2_button', // default state 44 sLoading: 'sm2_loading', 45 sPlaying: 'sm2_playing', 46 sPaused: 'sm2_paused' 47 }; 48 49 // event + DOM utils 50 51 this.includeClass = this.css.sDefault; 52 53 this.addEventHandler = (typeof window.addEventListener !== 'undefined' ? function(o, evtName, evtHandler) { 54 return o.addEventListener(evtName, evtHandler, false); 55 } : function(o, evtName, evtHandler) { 56 o.attachEvent('on' + evtName, evtHandler); 57 }); 58 59 this.removeEventHandler = (typeof window.removeEventListener !== 'undefined' ? function(o, evtName, evtHandler) { 60 return o.removeEventListener(evtName, evtHandler, false); 61 } : function(o, evtName, evtHandler) { 62 return o.detachEvent('on' + evtName, evtHandler); 63 }); 64 65 this.classContains = function(o, cStr) { 66 return (typeof o.className !== 'undefined' ? o.className.match(new RegExp('(\\s|^)' + cStr + '(\\s|$)')) : false); 67 }; 68 69 this.addClass = function(o, cStr) { 70 if (!o || !cStr || self.classContains(o, cStr)) { 71 return; 72 } 73 o.className = (o.className ? o.className + ' ' : '') + cStr; 74 }; 75 76 this.removeClass = function(o, cStr) { 77 if (!o || !cStr || !self.classContains(o, cStr)) { 78 return; 79 } 80 o.className = o.className.replace(new RegExp('( ' + cStr + ')|(' + cStr + ')', 'g'), ''); 81 }; 82 83 this.getSoundByURL = function(sURL) { 84 return (typeof self.soundsByURL[sURL] !== 'undefined' ? self.soundsByURL[sURL] : null); 85 }; 86 87 this.isChildOfNode = function(o, sNodeName) { 88 if (!o || !o.parentNode) { 89 return null; 90 } 91 sNodeName = sNodeName.toLowerCase(); 92 do { 93 o = o.parentNode; 94 } while (o && o.parentNode && o.nodeName.toLowerCase() !== sNodeName); 95 return (o.nodeName.toLowerCase() === sNodeName ? o : null); 96 }; 97 98 this.events = { 99 100 // handlers for sound events as they're started/stopped/played 101 102 play: function() { 103 pl.removeClass(this._data.oLink, this._data.className); 104 this._data.className = pl.css.sPlaying; 105 pl.addClass(this._data.oLink, this._data.className); 106 }, 107 108 stop: function() { 109 pl.removeClass(this._data.oLink, this._data.className); 110 this._data.className = ''; 111 }, 112 113 pause: function() { 114 pl.removeClass(this._data.oLink, this._data.className); 115 this._data.className = pl.css.sPaused; 116 pl.addClass(this._data.oLink, this._data.className); 117 }, 118 119 resume: function() { 120 pl.removeClass(this._data.oLink, this._data.className); 121 this._data.className = pl.css.sPlaying; 122 pl.addClass(this._data.oLink, this._data.className); 123 }, 124 125 finish: function() { 126 if ($('.record-key').hasClass('active')) { 127 $('.record-key').removeClass('active'); 128 } 129 130 if ($('.player-main').hasClass('active')) { 131 $('.player-main').removeClass('active'); 132 } 133 134 if ($('.bubble-wrap').hasClass('active')) { 135 $('.bubble-wrap').removeClass('active'); 136 } 137 138 if ($('.bubble-wrap-right').hasClass('active')) { 139 $('.bubble-wrap-right').removeClass('active'); 140 } 141 142 pl.removeClass(this._data.oLink, this._data.className); 143 this._data.className = ''; 144 if (pl.config.playNext) { 145 var nextLink = (pl.indexByURL[this._data.oLink.href] + 1); 146 if (nextLink < pl.links.length) { 147 pl.handleClick({ 148 target: pl.links[nextLink] 149 }); 150 } 151 } 152 } 153 154 }; 155 156 this.stopEvent = function(e) { 157 if (typeof e !== 'undefined' && typeof e.preventDefault !== 'undefined') { 158 e.preventDefault(); 159 } else if (typeof window.event !== 'undefined') { 160 window.event.returnValue = false; 161 } 162 return false; 163 }; 164 165 this.getTheDamnLink = (isIE) ? function(e) { 166 // I really didn't want to have to do this. 167 return (e && e.target ? e.target : window.event.srcElement); 168 } : function(e) { 169 return e.target; 170 }; 171 172 this.handleClick = function(e) { 173 // a sound link was clicked 174 if (typeof e.button !== 'undefined' && e.button > 1) { 175 // ignore right-click 176 return true; 177 } 178 var o = self.getTheDamnLink(e), 179 soundURL, 180 thisSound; 181 if (o.nodeName.toLowerCase() !== 'a') { 182 o = self.isChildOfNode(o, 'a'); 183 if (!o) { 184 return true; 185 } 186 } 187 if (!o.href || !soundManager.canPlayLink(o) || self.classContains(o, self.excludeClass)) { 188 return true; // pass-thru for non-MP3/non-links 189 } 190 if (!self.classContains(o, self.includeClass)) { 191 return true; 192 } 193 sm._writeDebug('handleClick()'); 194 soundURL = (o.href); 195 thisSound = self.getSoundByURL(soundURL); 196 if (thisSound) { 197 // already exists 198 if (thisSound === self.lastSound) { 199 // and was playing (or paused) 200 thisSound.togglePause(); 201 } else { 202 // different sound 203 thisSound.togglePause(); // start playing current 204 sm._writeDebug('sound different than last sound: ' + self.lastSound.id); 205 if (self.lastSound) { 206 self.stopSound(self.lastSound); 207 } 208 } 209 } else { 210 // create sound 211 thisSound = sm.createSound({ 212 id: 'basicMP3Sound' + (self.soundCount++), 213 url: soundURL, 214 onplay: self.events.play, 215 onstop: self.events.stop, 216 onpause: self.events.pause, 217 onresume: self.events.resume, 218 onfinish: self.events.finish, 219 type: (o.type || null) 220 }); 221 // tack on some custom data 222 thisSound._data = { 223 oLink: o, // DOM node for reference within SM2 object event handlers 224 className: self.css.sPlaying 225 }; 226 self.soundsByURL[soundURL] = thisSound; 227 self.sounds.push(thisSound); 228 if (self.lastSound) { 229 // stop last sound 230 self.stopSound(self.lastSound); 231 } 232 thisSound.play(); 233 } 234 self.lastSound = thisSound; // reference for next call 235 return self.stopEvent(e); 236 }; 237 238 this.stopSound = function(oSound) { 239 soundManager.stop(oSound.id); 240 if (!isTouchDevice) { // iOS 4.2+ security blocks onfinish() -> playNext() if we set a .src in-between(?) 241 soundManager.unload(oSound.id); 242 } 243 }; 244 245 this.init = function() { 246 sm._writeDebug('basicMP3Player.init()'); 247 var i, j, 248 foundItems = 0, 249 oLinks = document.getElementsByTagName('a'); 250 // grab all links, look for .mp3 251 for (i = 0, j = oLinks.length; i < j; i++) { 252 if (self.classContains(oLinks[i], self.css.sDefault) && !self.classContains(oLinks[i], self.excludeClass)) { 253 // 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 254 self.links[foundItems] = (oLinks[i]); 255 self.indexByURL[oLinks[i].href] = foundItems; // hack for indexing 256 foundItems++; 257 } 258 } 259 if (foundItems > 0) { 260 self.addEventHandler(document, 'click', self.handleClick); 261 if (self.config.autoPlay) { 262 self.handleClick({ 263 target: self.links[0], 264 preventDefault: function() {} 265 }); 266 } 267 } 268 sm._writeDebug('basicMP3Player.init(): Found ' + foundItems + ' relevant items.'); 269 }; 270 271 this.init(); 272 273 } 274 275 var basicMP3Player = null; 276 277 window.basicMP3Player = basicMP3Player; 278 279 soundManager.setup({ 280 preferFlash: false, 281 onready: function() { 282 basicMP3Player = new BasicMP3Player(); 283 } 284 285 286 }); 287 288}());
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.