pageflow-embedded-video 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +22 -0
  3. data/.jshintignore +1 -0
  4. data/.jshintrc +22 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +10 -0
  7. data/README.md +63 -0
  8. data/Rakefile +17 -0
  9. data/app/assets/images/pageflow/embedded_video_pictogram.png +0 -0
  10. data/app/assets/images/pageflow/embedded_video_pictogram_small.png +0 -0
  11. data/app/assets/images/pageflow/embedded_video_sprite.png +0 -0
  12. data/app/assets/images/pageflow/mobile_overlay.png +0 -0
  13. data/app/assets/images/pageflow/ov-embedded_video.png +0 -0
  14. data/app/assets/javascript/pageflow/URI.js +52 -0
  15. data/app/assets/javascript/pageflow/embedded_video/editor/collections/embedded_videos_collection.js +23 -0
  16. data/app/assets/javascript/pageflow/embedded_video/editor/initializers/setup_collections.js +1 -0
  17. data/app/assets/javascript/pageflow/embedded_video/editor/models/embedded_video.js +55 -0
  18. data/app/assets/javascript/pageflow/embedded_video/editor/templates/embedded_video_status.jst.ejs +2 -0
  19. data/app/assets/javascript/pageflow/embedded_video/editor/templates/url_input.jst.ejs +7 -0
  20. data/app/assets/javascript/pageflow/embedded_video/editor/views/configuration_editor.js +29 -0
  21. data/app/assets/javascript/pageflow/embedded_video/editor/views/inputs/video_url_input_view.js +34 -0
  22. data/app/assets/javascript/pageflow/embedded_video/editor.js +9 -0
  23. data/app/assets/javascript/pageflow/embedded_video/page_type.js +370 -0
  24. data/app/assets/javascript/pageflow/embedded_video.js +6 -0
  25. data/app/assets/javascript/pageflow/froogaloop.js +288 -0
  26. data/app/assets/stylesheets/pageflow/embedded_video/custom.css.scss +0 -0
  27. data/app/assets/stylesheets/pageflow/embedded_video/editor.css.scss +17 -0
  28. data/app/assets/stylesheets/pageflow/embedded_video.css.scss +269 -0
  29. data/app/views/pageflow/embedded_video/page.html.erb +35 -0
  30. data/app/views/pageflow/embedded_video/page_type.json.jbuilder +2 -0
  31. data/bin/rails +8 -0
  32. data/config/locales/de.yml +45 -0
  33. data/config/locales/en.yml +34 -0
  34. data/lib/pageflow/embedded_video/configuration.rb +15 -0
  35. data/lib/pageflow/embedded_video/engine.rb +9 -0
  36. data/lib/pageflow/embedded_video/page_type.rb +12 -0
  37. data/lib/pageflow-embedded-video.rb +17 -0
  38. data/pageflow-embedded-video.gemspec +23 -0
  39. metadata +152 -0
@@ -0,0 +1,370 @@
1
+ /*global YT, URI, $f */
2
+
3
+ pageflow.pageType.register('embedded_video', _.extend({
4
+ prepareNextPageTimeout: 0,
5
+
6
+ enhance: function(pageElement, configuration) {
7
+ var that = this;
8
+
9
+ if (pageflow.features.has('mobile platform')) {
10
+ pageElement.find('.close_button, .iframe_container').click(function(event) {
11
+ event.stopPropagation();
12
+ that._pauseVideo();
13
+ pageElement.find('.iframe_container').removeClass('show');
14
+ pageflow.hideText.deactivate();
15
+ });
16
+
17
+ this._initPlaceholderImage(pageElement, configuration);
18
+ }
19
+
20
+ this.fullscreen = false;
21
+ $(document).on('fullscreenchange mozfullscreenchange webkitfullscreenchange msfullscreenchange', function() {
22
+ that.fullscreen = !that.fullscreen;
23
+ });
24
+ },
25
+
26
+ resize: function(pageElement, configuration) {
27
+ var iframeWrapper = pageElement.find('.iframe_wrapper'),
28
+ pageHeader = pageElement.find('.page_header'),
29
+ scroller = pageElement.find('.scroller'),
30
+ container = pageElement.find('.iframe_container'),
31
+ iframeOverlay = pageElement.find('.iframe_overlay'),
32
+ videoCaption = pageElement.find('.video_caption'),
33
+ widescreened = pageElement.width() > 1430,
34
+ fullWidth = configuration.full_width,
35
+ mobile = pageflow.features.has('mobile platform');
36
+
37
+ if (fullWidth && !this.fullscreen) {
38
+ widescreened = false;
39
+ }
40
+
41
+ iframeWrapper.add(scroller).toggleClass('widescreened', widescreened);
42
+
43
+ if (!this.fullscreen) {
44
+ if (widescreened || mobile) {
45
+ if (!container.find('iframe').length) {
46
+ container.append(iframeWrapper);
47
+ }
48
+ }
49
+ else {
50
+ if (!scroller.find('iframe').length) {
51
+ iframeWrapper.insertAfter(pageHeader);
52
+ }
53
+ }
54
+ }
55
+
56
+ if (widescreened) {
57
+ iframeWrapper.append(videoCaption);
58
+ }
59
+ else if (mobile) {
60
+ iframeOverlay.after(videoCaption);
61
+ }
62
+ else {
63
+ iframeWrapper.after(videoCaption);
64
+ }
65
+
66
+ scroller.scroller('refresh');
67
+ },
68
+
69
+ prepare: function(pageElement, configuration) {},
70
+
71
+ preload: function(pageElement, configuration) {
72
+ return pageflow.preload.backgroundImage(pageElement.find('.background_image'));
73
+ },
74
+
75
+ activating: function(pageElement, configuration) {
76
+ var that = this;
77
+
78
+ this.listenTo(pageflow.settings, "change:volume", function(model, value) {
79
+ that._setPlayerVolume(value);
80
+ });
81
+
82
+ this._createPlayer(pageElement, configuration);
83
+
84
+ this.resize(pageElement, configuration);
85
+ this.active = true;
86
+ },
87
+
88
+ activated: function(pageElement, configuration) {},
89
+
90
+ deactivating: function(pageElement, configuration) {
91
+ this.active = false;
92
+ this.stopListening(pageflow.settings);
93
+ this._removePlayer(pageElement);
94
+ },
95
+
96
+ deactivated: function(pageElement, configuration) {
97
+ },
98
+
99
+ update: function(pageElement, configuration) {
100
+ pageElement.find('h2 .tagline').text(configuration.get('tagline') || '');
101
+ pageElement.find('h2 .title').text(configuration.get('title') || '');
102
+ pageElement.find('h2 .subtitle').text(configuration.get('subtitle') || '');
103
+ pageElement.find('p').html(configuration.get('text') || '');
104
+
105
+ var that = this,
106
+ iframeWrapper = pageElement.find('.iframe_wrapper'),
107
+ captionElement = pageElement.find('.video_caption'),
108
+ caption = configuration.get('video_caption');
109
+
110
+ if ((caption || '').trim() !== '') {
111
+ if (!captionElement.length) {
112
+ captionElement = $('<div class="video_caption"></div>');
113
+
114
+ if (pageElement.find('.scroller iframe').length) {
115
+ captionElement.insertAfter(iframeWrapper);
116
+ } else {
117
+ captionElement.appendTo(iframeWrapper);
118
+ }
119
+ }
120
+ captionElement.text(caption || '');
121
+ } else {
122
+ captionElement.remove();
123
+ }
124
+
125
+ if (this.active) {
126
+ var currentUrl = this._getCurrentUrl(pageElement),
127
+ newUrl = configuration.get('display_embedded_video_url');
128
+
129
+ if (this._urlOrigin(currentUrl) === this._urlOrigin(newUrl)) {
130
+ this._updatePlayerSrc(pageElement, configuration);
131
+ }
132
+ else {
133
+ this._removePlayer(pageElement, function() {
134
+ that._createPlayer(pageElement, configuration.attributes);
135
+ });
136
+ }
137
+ }
138
+
139
+ pageElement.find('.shadow').css({
140
+ opacity: configuration.get('gradient_opacity') / 100
141
+ });
142
+
143
+ this.updateCommonPageCssClasses(pageElement, configuration);
144
+ this.resize(pageElement, configuration.attributes);
145
+ },
146
+
147
+ embeddedEditorViews: function() {
148
+ return {
149
+ '.background_image': {
150
+ view: pageflow.BackgroundImageEmbeddedView,
151
+ options: {propertyName: 'background_image_id'}
152
+ }
153
+ };
154
+ },
155
+
156
+ _createPlayer: function(pageElement, configuration) {
157
+ var that = this,
158
+ url = configuration.display_embedded_video_url,
159
+ origin = this._urlOrigin(url);
160
+
161
+ if (origin === 'youtube') {
162
+ this.ytApiInitialize().done(function () {
163
+ that._createYouTubePlayer(pageElement, url);
164
+ });
165
+ }
166
+ else if (origin == 'vimeo') {
167
+ that._createVimeoPlayer(pageElement, url);
168
+ }
169
+ },
170
+
171
+ ytApiInitialize: function() {
172
+ if (!window.youtubeInitialized) {
173
+ var ytApi = new $.Deferred();
174
+ window.youtubeInitialized = ytApi.promise();
175
+
176
+ window.onYouTubeIframeAPIReady = function() {
177
+ ytApi.resolve();
178
+ };
179
+
180
+ var tag = document.createElement('script');
181
+ tag.src = "https://www.youtube.com/iframe_api";
182
+ var firstScriptTag = document.getElementsByTagName('script')[0];
183
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
184
+ }
185
+ return window.youtubeInitialized;
186
+ },
187
+
188
+ _createYouTubePlayer: function(pageElement, url) {
189
+ var that = this,
190
+ div = document.createElement('div');
191
+
192
+ this.playerId = 'youtube-player-' + this._getRandom(url);
193
+
194
+ div.setAttribute('id', this.playerId);
195
+ pageElement.find('.iframe_wrapper').prepend(div);
196
+
197
+ this.ytApiInitialize().done(function() {
198
+ new YT.Player(div, {
199
+ height: '100%',
200
+ width: '100%',
201
+ videoId: that._getVideoId(url),
202
+ playerVars: {
203
+ rel: false
204
+ },
205
+ events: {
206
+ 'onReady': function(event) {
207
+ that.player = event.target;
208
+ that._setPlayerVolume(pageflow.settings.get('volume'));
209
+ }
210
+ }
211
+ });
212
+ });
213
+ },
214
+
215
+ _createVimeoPlayer: function(pageElement, url) {
216
+ var that = this,
217
+ iframe = document.createElement('iframe'),
218
+ uri = new URI('//player.vimeo.com/video/');
219
+
220
+ this.playerId = 'vimeo-player-' + this._getRandom(url);
221
+
222
+ uri.filename(that._getVideoId(url));
223
+ uri.search({api: '1', player_id: this.playerId});
224
+
225
+ $(iframe).attr({
226
+ id: this.playerId,
227
+ width: '100%',
228
+ height: '100%',
229
+ frameborder: '0',
230
+ webkitallowfullscreen: true,
231
+ mozallowfullscreen: true,
232
+ allowfullscreen: true,
233
+ src: uri.toString()
234
+ });
235
+
236
+ pageElement.find('.iframe_wrapper').prepend(iframe);
237
+
238
+ this.player = $f(iframe);
239
+
240
+ this.player.addEvent('ready', function() {
241
+ that._setPlayerVolume(pageflow.settings.get('volume'));
242
+ });
243
+ },
244
+
245
+ _updatePlayerSrc: function(pageElement, configuration) {
246
+ var that = this,
247
+ newUrl = configuration.get('display_embedded_video_url'),
248
+ p = pageElement.find('iframe'),
249
+ url = new URI(p.attr('src'));
250
+
251
+ p.attr('src', url.filename(that._getVideoId(newUrl)));
252
+ },
253
+
254
+ _setPlayerVolume: function(value) {
255
+ if (this.player) {
256
+ if (typeof this.player.setVolume === 'function') {
257
+ this.player.setVolume(value * 100);
258
+ } else if (typeof this.player.api === 'function') {
259
+ this.player.api('setVolume', value);
260
+ }
261
+ }
262
+ },
263
+
264
+ _pauseVideo: function() {
265
+ if (this.player) {
266
+ if (typeof this.player.pauseVideo === 'function') {
267
+ this.player.pauseVideo();
268
+ } else if (typeof this.player.api === 'function') {
269
+ this.player.api('pause');
270
+ }
271
+ }
272
+ },
273
+
274
+ _removePlayer: function (pageElement, callback) {
275
+ if (this.player && typeof this.player.destroy === 'function') {
276
+ this.player.destroy();
277
+ }
278
+ this.player = null;
279
+ $('#' + this.playerId, pageElement).remove();
280
+
281
+ if (typeof callback === 'function') {
282
+ callback();
283
+ }
284
+ },
285
+
286
+ _initPlaceholderImage: function(pageElement, configuration) {
287
+ var $div = $(document.createElement('div')),
288
+ pageHeader = pageElement.find('.page_header'),
289
+ container = pageElement.find('.iframe_container'),
290
+ url = configuration.display_embedded_video_url;
291
+
292
+ $div.addClass('iframe_overlay ' + this._urlOrigin(url));
293
+
294
+ this._setBackgroundImage(url, $div);
295
+ pageHeader.after($div);
296
+
297
+ $div.click(function(event) {
298
+ event.preventDefault();
299
+ container.addClass('show');
300
+ pageflow.hideText.activate();
301
+ });
302
+ },
303
+
304
+ _setBackgroundImage: function(url, element) {
305
+ var origin = this._urlOrigin(url),
306
+ videoId = this._getVideoId(url),
307
+ imageUrl = '';
308
+
309
+ if (origin === 'youtube') {
310
+ imageUrl = 'http://img.youtube.com/vi/' + videoId + '/hqdefault.jpg';
311
+ element.css('background-image', 'url("' + imageUrl + '")');
312
+ }
313
+ else if (origin === 'vimeo') {
314
+ var src = "http://vimeo.com/api/v2/video/" + videoId + ".json";
315
+
316
+ $.getJSON(src, function(data) {
317
+ element.css('background-image', 'url("' + data[0].thumbnail_large + '")');
318
+ });
319
+ }
320
+ },
321
+
322
+ _getCurrentUrl: function(pageElement) {
323
+ return pageElement.find('iframe').attr('src');
324
+ },
325
+
326
+ _urlOrigin: function(url) {
327
+ var uri = new URI(url),
328
+ domain = uri.domain(true);
329
+
330
+ if (['youtu.be', 'youtube.com'].indexOf(domain) >= 0) {
331
+ return 'youtube';
332
+ }
333
+ else if (domain === 'vimeo.com') {
334
+ return 'vimeo';
335
+ }
336
+
337
+ return '';
338
+ },
339
+
340
+ _getVideoId: function(url) {
341
+ var uri = new URI(url),
342
+ domain = uri.domain(true);
343
+
344
+ if (['youtu.be', 'vimeo.com'].indexOf(domain) >= 0) {
345
+ return uri.filename();
346
+ }
347
+ else if (domain === 'youtube.com') {
348
+ if (uri.directory() === '/embed') {
349
+ return uri.filename();
350
+ }
351
+ else {
352
+ return uri.search(true).v;
353
+ }
354
+ }
355
+
356
+ return '';
357
+ },
358
+
359
+ _getRandom: function(string) {
360
+ string = string + new Date().getTime();
361
+ var hash = 0, i, chr, len;
362
+ if (string === 0) return hash;
363
+ for (i = 0, len = string.length; i < len; i++) {
364
+ chr = string.charCodeAt(i);
365
+ hash = ((hash << 5) - hash) + chr;
366
+ hash |= 0; // Convert to 32bit integer
367
+ }
368
+ return hash;
369
+ }
370
+ }, pageflow.commonPageCssClasses));
@@ -0,0 +1,6 @@
1
+ //= require_self
2
+ //= require ./embedded_video/page_type
3
+ //= require ./URI.js
4
+ //= require ./froogaloop.js
5
+
6
+ pageflow.embeddedVideo = pageflow.embeddedVideo || {};
@@ -0,0 +1,288 @@
1
+ // Init style shamelessly stolen from jQuery http://jquery.com
2
+ var Froogaloop = (function(){
3
+ // Define a local copy of Froogaloop
4
+ function Froogaloop(iframe) {
5
+ // The Froogaloop object is actually just the init constructor
6
+ return new Froogaloop.fn.init(iframe);
7
+ }
8
+
9
+ var eventCallbacks = {},
10
+ hasWindowEvent = false,
11
+ slice = Array.prototype.slice,
12
+ playerDomain = '';
13
+
14
+ Froogaloop.fn = Froogaloop.prototype = {
15
+ element: null,
16
+ isReady: false,
17
+
18
+ init: function(iframe) {
19
+ if (typeof iframe === "string") {
20
+ iframe = document.getElementById(iframe);
21
+ }
22
+
23
+ this.element = iframe;
24
+
25
+ // Register message event listeners
26
+ playerDomain = getDomainFromUrl(this.element.getAttribute('src'));
27
+
28
+ return this;
29
+ },
30
+
31
+ /*
32
+ * Calls a function to act upon the player.
33
+ *
34
+ * @param {string} method The name of the Javascript API method to call. Eg: 'play'.
35
+ * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
36
+ * or callback function when the method returns a value.
37
+ */
38
+ api: function(method, valueOrCallback) {
39
+ if (!this.element || !method) {
40
+ return false;
41
+ }
42
+
43
+ var self = this,
44
+ element = self.element,
45
+ target_id = element.id !== '' ? element.id : null,
46
+ params = !isFunction(valueOrCallback) ? valueOrCallback : null,
47
+ callback = isFunction(valueOrCallback) ? valueOrCallback : null;
48
+
49
+ // Store the callback for get functions
50
+ if (callback) {
51
+ storeCallback(method, callback, target_id);
52
+ }
53
+
54
+ postMessage(method, params, element);
55
+ return self;
56
+ },
57
+
58
+ /*
59
+ * Registers an event listener and a callback function that gets called when the event fires.
60
+ *
61
+ * @param eventName (String): Name of the event to listen for.
62
+ * @param callback (Function): Function that should be called when the event fires.
63
+ */
64
+ addEvent: function(eventName, callback) {
65
+ if (!this.element) {
66
+ return false;
67
+ }
68
+
69
+ var self = this,
70
+ element = self.element,
71
+ target_id = element.id !== '' ? element.id : null;
72
+
73
+
74
+ storeCallback(eventName, callback, target_id);
75
+
76
+ // The ready event is not registered via postMessage. It fires regardless.
77
+ if (eventName != 'ready') {
78
+ postMessage('addEventListener', eventName, element);
79
+ }
80
+ else if (eventName == 'ready' && this.isReady) {
81
+ callback.call(null, target_id);
82
+ }
83
+
84
+ return self;
85
+ },
86
+
87
+ /*
88
+ * Unregisters an event listener that gets called when the event fires.
89
+ *
90
+ * @param eventName (String): Name of the event to stop listening for.
91
+ */
92
+ removeEvent: function(eventName) {
93
+ if (!this.element) {
94
+ return false;
95
+ }
96
+
97
+ var self = this,
98
+ element = self.element,
99
+ target_id = element.id !== '' ? element.id : null,
100
+ removed = removeCallback(eventName, target_id);
101
+
102
+ // The ready event is not registered
103
+ if (eventName != 'ready' && removed) {
104
+ postMessage('removeEventListener', eventName, element);
105
+ }
106
+ }
107
+ };
108
+
109
+ /**
110
+ * Handles posting a message to the parent window.
111
+ *
112
+ * @param method (String): name of the method to call inside the player. For api calls
113
+ * this is the name of the api method (api_play or api_pause) while for events this method
114
+ * is api_addEventListener.
115
+ * @param params (Object or Array): List of parameters to submit to the method. Can be either
116
+ * a single param or an array list of parameters.
117
+ * @param target (HTMLElement): Target iframe to post the message to.
118
+ */
119
+ function postMessage(method, params, target) {
120
+ if (!target.contentWindow.postMessage) {
121
+ return false;
122
+ }
123
+
124
+ var url = target.getAttribute('src').split('?')[0],
125
+ data = JSON.stringify({
126
+ method: method,
127
+ value: params
128
+ });
129
+
130
+ if (url.substr(0, 2) === '//') {
131
+ url = window.location.protocol + url;
132
+ }
133
+
134
+ target.contentWindow.postMessage(data, url);
135
+ }
136
+
137
+ /**
138
+ * Event that fires whenever the window receives a message from its parent
139
+ * via window.postMessage.
140
+ */
141
+ function onMessageReceived(event) {
142
+ var data, method;
143
+
144
+ try {
145
+ data = JSON.parse(event.data);
146
+ method = data.event || data.method;
147
+ }
148
+ catch(e) {
149
+ //fail silently... like a ninja!
150
+ }
151
+
152
+ if (method == 'ready' && !this.isReady) {
153
+ this.isReady = true;
154
+ }
155
+
156
+ // Handles messages from moogaloop only
157
+ if (event.origin != playerDomain) {
158
+ return false;
159
+ }
160
+
161
+ var value = data.value,
162
+ eventData = data.data,
163
+ target_id = target_id === '' ? null : data.player_id,
164
+
165
+ callback = getCallback(method, target_id),
166
+ params = [];
167
+
168
+ if (!callback) {
169
+ return false;
170
+ }
171
+
172
+ if (value !== undefined) {
173
+ params.push(value);
174
+ }
175
+
176
+ if (eventData) {
177
+ params.push(eventData);
178
+ }
179
+
180
+ if (target_id) {
181
+ params.push(target_id);
182
+ }
183
+
184
+ return params.length > 0 ? callback.apply(null, params) : callback.call();
185
+ }
186
+
187
+
188
+ /**
189
+ * Stores submitted callbacks for each iframe being tracked and each
190
+ * event for that iframe.
191
+ *
192
+ * @param eventName (String): Name of the event. Eg. api_onPlay
193
+ * @param callback (Function): Function that should get executed when the
194
+ * event is fired.
195
+ * @param target_id (String) [Optional]: If handling more than one iframe then
196
+ * it stores the different callbacks for different iframes based on the iframe's
197
+ * id.
198
+ */
199
+ function storeCallback(eventName, callback, target_id) {
200
+ if (target_id) {
201
+ if (!eventCallbacks[target_id]) {
202
+ eventCallbacks[target_id] = {};
203
+ }
204
+ eventCallbacks[target_id][eventName] = callback;
205
+ }
206
+ else {
207
+ eventCallbacks[eventName] = callback;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Retrieves stored callbacks.
213
+ */
214
+ function getCallback(eventName, target_id) {
215
+ if (target_id) {
216
+ return eventCallbacks[target_id][eventName];
217
+ }
218
+ else {
219
+ return eventCallbacks[eventName];
220
+ }
221
+ }
222
+
223
+ function removeCallback(eventName, target_id) {
224
+ if (target_id && eventCallbacks[target_id]) {
225
+ if (!eventCallbacks[target_id][eventName]) {
226
+ return false;
227
+ }
228
+ eventCallbacks[target_id][eventName] = null;
229
+ }
230
+ else {
231
+ if (!eventCallbacks[eventName]) {
232
+ return false;
233
+ }
234
+ eventCallbacks[eventName] = null;
235
+ }
236
+
237
+ return true;
238
+ }
239
+
240
+ /**
241
+ * Returns a domain's root domain.
242
+ * Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted
243
+ *
244
+ * @param url (String): Url to test against.
245
+ * @return url (String): Root domain of submitted url
246
+ */
247
+ function getDomainFromUrl(url) {
248
+ if (url.substr(0, 2) === '//') {
249
+ url = window.location.protocol + url;
250
+ }
251
+
252
+ var url_pieces = url.split('/'),
253
+ domain_str = '';
254
+
255
+ for(var i = 0, length = url_pieces.length; i < length; i++) {
256
+ if(i<3) {domain_str += url_pieces[i];}
257
+ else {break;}
258
+ if(i<2) {domain_str += '/';}
259
+ }
260
+
261
+ return domain_str;
262
+ }
263
+
264
+ function isFunction(obj) {
265
+ return !!(obj && obj.constructor && obj.call && obj.apply);
266
+ }
267
+
268
+ function isArray(obj) {
269
+ return toString.call(obj) === '[object Array]';
270
+ }
271
+
272
+ // Give the init function the Froogaloop prototype for later instantiation
273
+ Froogaloop.fn.init.prototype = Froogaloop.fn;
274
+
275
+ // Listens for the message event.
276
+ // W3C
277
+ if (window.addEventListener) {
278
+ window.addEventListener('message', onMessageReceived, false);
279
+ }
280
+ // IE
281
+ else {
282
+ window.attachEvent('onmessage', onMessageReceived);
283
+ }
284
+
285
+ // Expose froogaloop to the global object
286
+ return (window.Froogaloop = window.$f = Froogaloop);
287
+
288
+ })();
@@ -0,0 +1,17 @@
1
+ .embedded_video_status {
2
+ .state {
3
+ display: none;
4
+ }
5
+
6
+ &.failed {
7
+ .failed.state {
8
+ display: block;
9
+ }
10
+ }
11
+
12
+ &.processing {
13
+ .processing.state {
14
+ display: block;
15
+ }
16
+ }
17
+ }