• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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