1 2// Init style shamelessly stolen from jQuery http://jquery.com 3var Froogaloop = (function(){ 4 // Define a local copy of Froogaloop 5 function Froogaloop(iframe) { 6 // The Froogaloop object is actually just the init constructor 7 return new Froogaloop.fn.init(iframe); 8 } 9 10 var eventCallbacks = {}, 11 hasWindowEvent = false, 12 isReady = false, 13 slice = Array.prototype.slice, 14 playerDomain = ''; 15 16 Froogaloop.fn = Froogaloop.prototype = { 17 element: null, 18 19 init: function(iframe) { 20 if (typeof iframe === "string") { 21 iframe = document.getElementById(iframe); 22 } 23 24 this.element = iframe; 25 26 // Register message event listeners 27 playerDomain = getDomainFromUrl(this.element.getAttribute('src')); 28 29 return this; 30 }, 31 32 /* 33 * Calls a function to act upon the player. 34 * 35 * @param {string} method The name of the Javascript API method to call. Eg: 'play'. 36 * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method 37 * or callback function when the method returns a value. 38 */ 39 api: function(method, valueOrCallback) { 40 if (!this.element || !method) { 41 return false; 42 } 43 44 var self = this, 45 element = self.element, 46 target_id = element.id !== '' ? element.id : null, 47 params = !isFunction(valueOrCallback) ? valueOrCallback : null, 48 callback = isFunction(valueOrCallback) ? valueOrCallback : null; 49 50 // Store the callback for get functions 51 if (callback) { 52 storeCallback(method, callback, target_id); 53 } 54 55 postMessage(method, params, element); 56 return self; 57 }, 58 59 /* 60 * Registers an event listener and a callback function that gets called when the event fires. 61 * 62 * @param eventName (String): Name of the event to listen for. 63 * @param callback (Function): Function that should be called when the event fires. 64 */ 65 addEvent: function(eventName, callback) { 66 if (!this.element) { 67 return false; 68 } 69 70 var self = this, 71 element = self.element, 72 target_id = element.id !== '' ? element.id : null; 73 74 75 storeCallback(eventName, callback, target_id); 76 77 // The ready event is not registered via postMessage. It fires regardless. 78 if (eventName != 'ready') { 79 postMessage('addEventListener', eventName, element); 80 } 81 else if (eventName == 'ready' && isReady) { 82 callback.call(null, target_id); 83 } 84 85 return self; 86 }, 87 88 /* 89 * Unregisters an event listener that gets called when the event fires. 90 * 91 * @param eventName (String): Name of the event to stop listening for. 92 */ 93 removeEvent: function(eventName) { 94 if (!this.element) { 95 return false; 96 } 97 98 var self = this, 99 element = self.element, 100 target_id = element.id !== '' ? element.id : null, 101 removed = removeCallback(eventName, target_id); 102 103 // The ready event is not registered 104 if (eventName != 'ready' && removed) { 105 postMessage('removeEventListener', eventName, element); 106 } 107 } 108 }; 109 110 /** 111 * Handles posting a message to the parent window. 112 * 113 * @param method (String): name of the method to call inside the player. For api calls 114 * this is the name of the api method (api_play or api_pause) while for events this method 115 * is api_addEventListener. 116 * @param params (Object or Array): List of parameters to submit to the method. Can be either 117 * a single param or an array list of parameters. 118 * @param target (HTMLElement): Target iframe to post the message to. 119 */ 120 function postMessage(method, params, target) { 121 if (!target.contentWindow.postMessage) { 122 return false; 123 } 124 125 var url = target.getAttribute('src').split('?')[0], 126 data = JSON.stringify({ 127 method: method, 128 value: params 129 }); 130 131 if (url.substr(0, 2) === '//') { 132 url = window.location.protocol + url; 133 } 134 135 target.contentWindow.postMessage(data, url); 136 } 137 138 /** 139 * Event that fires whenever the window receives a message from its parent 140 * via window.postMessage. 141 */ 142 function onMessageReceived(event) { 143 var data, method; 144 145 try { 146 data = JSON.parse(event.data); 147 method = data.event || data.method; 148 } 149 catch(e) { 150 //fail silently... like a ninja! 151 } 152 153 if (method == 'ready' && !isReady) { 154 isReady = true; 155 } 156 157 // Handles messages from moogaloop only 158 if (event.origin != playerDomain) { 159 return false; 160 } 161 162 var value = data.value, 163 eventData = data.data, 164 target_id = target_id === '' ? null : data.player_id, 165 166 callback = getCallback(method, target_id), 167 params = []; 168 169 if (!callback) { 170 return false; 171 } 172 173 if (value !== undefined) { 174 params.push(value); 175 } 176 177 if (eventData) { 178 params.push(eventData); 179 } 180 181 if (target_id) { 182 params.push(target_id); 183 } 184 185 return params.length > 0 ? callback.apply(null, params) : callback.call(); 186 } 187 188 189 /** 190 * Stores submitted callbacks for each iframe being tracked and each 191 * event for that iframe. 192 * 193 * @param eventName (String): Name of the event. Eg. api_onPlay 194 * @param callback (Function): Function that should get executed when the 195 * event is fired. 196 * @param target_id (String) [Optional]: If handling more than one iframe then 197 * it stores the different callbacks for different iframes based on the iframe's 198 * id. 199 */ 200 function storeCallback(eventName, callback, target_id) { 201 if (target_id) { 202 if (!eventCallbacks[target_id]) { 203 eventCallbacks[target_id] = {}; 204 } 205 eventCallbacks[target_id][eventName] = callback; 206 } 207 else { 208 eventCallbacks[eventName] = callback; 209 } 210 } 211 212 /** 213 * Retrieves stored callbacks. 214 */ 215 function getCallback(eventName, target_id) { 216 if (target_id) { 217 return eventCallbacks[target_id][eventName]; 218 } 219 else { 220 return eventCallbacks[eventName]; 221 } 222 } 223 224 function removeCallback(eventName, target_id) { 225 if (target_id && eventCallbacks[target_id]) { 226 if (!eventCallbacks[target_id][eventName]) { 227 return false; 228 } 229 eventCallbacks[target_id][eventName] = null; 230 } 231 else { 232 if (!eventCallbacks[eventName]) { 233 return false; 234 } 235 eventCallbacks[eventName] = null; 236 } 237 238 return true; 239 } 240 241 /** 242 * Returns a domain's root domain. 243 * Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted 244 * 245 * @param url (String): Url to test against. 246 * @return url (String): Root domain of submitted url 247 */ 248 function getDomainFromUrl(url) { 249 if (url.substr(0, 2) === '//') { 250 url = window.location.protocol + url; 251 } 252 253 var url_pieces = url.split('/'), 254 domain_str = ''; 255 256 for(var i = 0, length = url_pieces.length; i < length; i++) { 257 if(i<3) {domain_str += url_pieces[i];} 258 else {break;} 259 if(i<2) {domain_str += '/';} 260 } 261 262 return domain_str; 263 } 264 265 function isFunction(obj) { 266 return !!(obj && obj.constructor && obj.call && obj.apply); 267 } 268 269 function isArray(obj) { 270 return toString.call(obj) === '[object Array]'; 271 } 272 273 // Give the init function the Froogaloop prototype for later instantiation 274 Froogaloop.fn.init.prototype = Froogaloop.fn; 275 276 // Listens for the message event. 277 // W3C 278 if (window.addEventListener) { 279 window.addEventListener('message', onMessageReceived, false); 280 } 281 // IE 282 else { 283 window.attachEvent('onmessage', onMessageReceived); 284 } 285 286 // Expose froogaloop to the global object 287 return (window.Froogaloop = window.$f = Froogaloop); 288 289})(); 290