pageflow-embedded-video 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }