Index: polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html |
diff --git a/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html b/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2e420bdef361a7b8f068f505d0f488185ef47451 |
--- /dev/null |
+++ b/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html |
@@ -0,0 +1,390 @@ |
+<link rel="import" href="../polymer/polymer.html"> |
+<link rel="import" href="google-cast-sender-api.html"> |
+ |
+<!-- TODO(tschaeff): |
+- ratechange to change playbackspeed (if even possible) |
+--> |
+<script> |
+ (function() { |
+ // Set static variable for the timeupdate delay. |
+ var _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY = 250; |
+/** |
+The `google-castable-video` element enables your HTML5 videos to be casted to any Chromecast. |
+ |
+It behaves exactly like an HTML5 video element except for some added methods and events. |
+ |
+Instead of listening for the video element's `timeupdate` event please listen for the `google-castable-video-timeupdate` event. This event is fired if the video is playing locally and on the Chromecast device. |
+ |
+##### Example |
+ |
+ <video is="google-castable-video"> |
+ <source src="video.mp4" type="video/mp4"> |
+ </video> |
+ |
+@demo |
+*/ |
+ Polymer({ |
+ |
+ is: 'google-castable-video', |
+ |
+ extends: 'video', |
+ |
+ properties: { |
+ /** |
+ * The appId that references which receiver the Chromecast will load. |
+ * |
+ * @default chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID |
+ */ |
+ appId: { |
+ type: String, |
+ value: null |
+ } |
+ }, |
+ |
+ listeners: { |
+ 'seeked': '_onSeeked', |
+ 'volumechange': '_onVolumechange', |
+ 'timeupdate': '_onTimeupdate' |
+ }, |
+ |
+ /** |
+ * The real paused state for local and cast playback. |
+ * |
+ * @property bothPaused |
+ * @type bool |
+ * @default true |
+ */ |
+ _bothPaused: true, |
+ get bothPaused() { |
+ return this._bothPaused; |
+ }, |
+ |
+ /** |
+ * Sets or returns the current playback position (in seconds). |
+ * Since the local video is paused when the video is playing on the Chromecast device |
+ * the objects currentTime property doesn't represent the actual currentTime of the video |
+ * playing on the Chromecast device. To always get the actual position please use bothCurrentTime. |
+ * |
+ * @property bothCurrentTime |
+ * @type number |
+ * @default 0 |
+ */ |
+ get bothCurrentTime() { |
+ if (this._casting && this._castMedia) { |
+ return this._castMedia.getEstimatedTime(); |
+ } else { |
+ return this.currentTime; |
+ } |
+ }, |
+ |
+ set bothCurrentTime(val) { |
+ return this.currentTime = val; |
+ }, |
+ |
+ /** |
+ * The mode state depending on whether the video is playing locally or on the cast device. |
+ * |
+ * @property casting |
+ * @type bool |
+ * @default false |
+ */ |
+ _casting: false, |
+ get casting() { |
+ return this._casting; |
+ }, |
+ |
+ /** |
+ * Returns if any Chromecast is available. |
+ * |
+ * @property receiverAvailable |
+ * @type bool |
+ * @default false |
+ */ |
+ _receiverAvailable: false, |
+ get receiverAvailable() { |
+ return this._receiverAvailable; |
+ }, |
+ |
+ /** |
+ * The `chrome.cast.Media` object. |
+ * |
+ * @property castMedia |
+ * @type chrome.cast.Media |
+ * @default null |
+ */ |
+ _castMedia: null, |
+ get castMedia() { |
+ return this._castMedia; |
+ }, |
+ |
+ /** |
+ * The `chrome.cast.Session` object. |
+ * |
+ * @property session |
+ * @type chrome.cast.Session |
+ * @default null |
+ */ |
+ _session: null, |
+ get session() { |
+ return this._session; |
+ }, |
+ |
+ ready: function() { |
+ // Initialize the cast api. |
+ window['__onGCastApiAvailable'] = function(loaded, errorInfo) { |
+ if (loaded) { |
+ this._initializeCastApi(); |
+ } else { |
+ this._triggerError('INITIALIZE_ERROR'); |
+ } |
+ }.bind(this); |
+ }, |
+ |
+ // Called internally when the cast sender api has been loaded. |
+ _initializeCastApi: function() { |
+ if (this.appId === null || typeof this.appId === "undefined") { |
+ this.appId = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID; |
+ // TODO ... process for selecting styled media receiver |
+ } |
+ var sessionRequest = new chrome.cast.SessionRequest(this.appId); |
+ var apiConfig = new chrome.cast.ApiConfig(sessionRequest, |
+ function(e){ |
+ // The sessionListener. |
+ this._triggerCasting(true); |
+ this._session = e; |
+ if (this._session.media.length) { |
+ this._onMediaDiscovered.call(this, 'onRequestSessionSuccess', this._session.media[0]); |
+ } |
+ // Bind the session update listener. |
+ this._session.addUpdateListener(this._sessionUpdateListener.bind(this)); |
+ // Set interval for cast timeupdate. |
+ this._timeupdateInterval = setInterval(function(){ |
+ if (this._castMedia && this._castMedia.playerState === 'PLAYING') { |
+ this._triggerTimeupdate(this._castMedia.getEstimatedTime()); |
+ this._bothPaused = false; |
+ } else { |
+ this._bothPaused = true; |
+ } |
+ }.bind(this), _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY); |
+ // Start playing on cast if playing locally. |
+ if (!this.paused) { |
+ this.play(); |
+ this.pause(false); |
+ } |
+ }.bind(this), |
+ function(e){ |
+ // The receiverListener |
+ if (e === chrome.cast.ReceiverAvailability.AVAILABLE) { |
+ this._triggerAvailability(true); |
+ } else { |
+ this._triggerAvailability(false); |
+ } |
+ }.bind(this)); |
+ chrome.cast.initialize(apiConfig, function(){ |
+ // The onInitSuccess method. |
+ /** |
+ * The `google-castable-video-initialized` event is fired when |
+ * the cast client API has been initialized. |
+ * |
+ * @event google-castable-video-initialized |
+ */ |
+ this.fire('google-castable-video-initialized'); |
+ }.bind(this), function(){ |
+ this._triggerError('INITIALIZE_ERROR'); |
+ }); |
+ }, |
+ |
+ /** |
+ * Call this when the user clicks the cast icon. |
+ * Opens the cast extension to create a session with the selected receiver. |
+ * |
+ * @method launchSessionManager |
+ */ |
+ launchSessionManager: function(){ |
+ if (this._receiverAvailable) { |
+ // Create the session with the receiver. |
+ chrome.cast.requestSession(function(e){ |
+ // The onRequestSessionSuccess handler gets executed when we're connected. |
+ this._triggerCasting(true); |
+ this._session = e; |
+ this._session.addUpdateListener(this._sessionUpdateListener.bind(this)); |
+ // If video is playing start playing on chromecast at same position. |
+ if (!this.paused) { |
+ this.play(); |
+ this.pause(false); |
+ } |
+ // Set interval for cast timeupdate. |
+ this._timeupdateInterval = setInterval(function(){ |
+ if (this._castMedia && this._castMedia.playerState === 'PLAYING') { |
+ this._triggerTimeupdate(this._castMedia.getEstimatedTime()); |
+ this._bothPaused = false; |
+ } else { |
+ this._bothPaused = true; |
+ } |
+ }.bind(this), _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY); |
+ }.bind(this)); |
+ } |
+ }, |
+ |
+ // Internal method gets called when the cast session status changes. |
+ _sessionUpdateListener: function(isAlive){ |
+ if (!isAlive) { |
+ this._triggerCasting(false); |
+ this._synchronizeMedia(true); |
+ // If video was playing on the receiver start playing locally. |
+ if (this._castMedia.playerState === 'PLAYING') { |
+ this.play(); |
+ } |
+ this._castMedia = null; |
+ this._session = null; |
+ // The session died so remove the timeupdate interval. |
+ clearInterval(this._timeupdateInterval); |
+ } |
+ }, |
+ |
+ // Internal method gets called when media was set through `launchsession` |
+ // or was already playing on cast device. |
+ _onMediaDiscovered: function(how, media) { |
+ this._castMedia = media; |
+ if (how === 'loadMedia') { |
+ this._synchronizeMedia(false); |
+ } |
+ }, |
+ |
+ // Internal method to synchronize the media objects. |
+ _synchronizeMedia: function(castMaster){ |
+ if (castMaster) { |
+ var position = this._castMedia.getEstimatedTime(); |
+ this.currentTime = position; |
+ } else { |
+ var position = this.currentTime; |
+ var req = new chrome.cast.media.SeekRequest(); |
+ req.currentTime = position; |
+ this._castMedia.seek(req); |
+ } |
+ }, |
+ /** |
+ * Call the `play` method from your controls. |
+ * |
+ * @method play |
+ */ |
+ play: function(cast){ |
+ if ((cast != undefined && !cast) || (!cast && !this._casting)) { |
+ Object.getPrototypeOf(Object.getPrototypeOf(this)).play.call(this); |
+ // this.super(); |
+ } else { |
+ // Handle cast media. |
+ if (!this._castMedia) { |
+ var mediaInfo = new chrome.cast.media.MediaInfo(this.currentSrc); |
+ [].forEach.call( // loop through DOM video sources to find the contentType of the current source |
+ document.querySelectorAll("video source"), |
+ function(el) { |
+ // the HTML5 video API resolves the DOM 'src' attribute to an absolute URL for the value of 'currentSrc'; to make it matchable, then, we can only rely on the last segment of the URL |
+ if (el.getAttribute("src").split('/').pop()===this.currentSrc.split('/').pop()) { |
+ mediaInfo.contentType = el.getAttribute('type'); |
+ } |
+ } |
+ ); |
+ var request = new chrome.cast.media.LoadRequest(mediaInfo); |
+ this._session.loadMedia(request, |
+ this._onMediaDiscovered.bind(this, 'loadMedia'), |
+ function(e){ |
+ this._triggerError('LOAD_MEDIA_ERROR'); |
+ }.bind(this) |
+ ); |
+ } else { |
+ this._castMedia.play(); |
+ } |
+ } |
+ }, |
+ |
+ /** |
+ * Call the `pause` method from your controls. |
+ * |
+ * @method pause |
+ */ |
+ pause: function(cast){ |
+ if ((cast != undefined && !cast) || (!cast && !this._casting)) { |
+ Object.getPrototypeOf(Object.getPrototypeOf(this)).pause.call(this); |
+ // this.super(); |
+ } else { |
+ this._castMedia.pause(); |
+ } |
+ }, |
+ |
+ /** |
+ * The `google-castable-video-timeupdate` event is fired whenever |
+ * the video's playback position changes. |
+ * |
+ * @event google-castable-video-timeupdate |
+ * @param {Object} detail |
+ * @param {number} detail.currentTime The current video position. |
+ */ |
+ _triggerTimeupdate: function(position) { |
+ this.fire('google-castable-video-timeupdate', { currentTime: position }); |
+ }, |
+ |
+ /** |
+ * The `google-castable-video-error` event is fired whenever |
+ * an error occurs. |
+ * |
+ * @event google-castable-video-error |
+ * @param {Object} detail |
+ * @param {string} detail.error The error type. |
+ */ |
+ _triggerError: function(description) { |
+ this.fire('google-castable-video-error', { error: description }); |
+ }, |
+ |
+ /** |
+ * The `google-castable-video-receiver-status` event is fired whenever |
+ * the availability of Chromecasts changes. Use this to show or hide the cast icon. |
+ * |
+ * @event google-castable-video-receiver-status |
+ * @param {Object} detail |
+ * @param {bool} detail.available Shows if receivers are available. |
+ */ |
+ _triggerAvailability: function(availability) { |
+ this._receiverAvailable = availability; |
+ this.fire('google-castable-video-receiver-status', { available: availability }); |
+ }, |
+ |
+ /** |
+ * The `google-castable-video-casting` event is fired whenever the |
+ * connection status to a Chromecast changes. Use this to change the cast icon. |
+ * |
+ * @event google-castable-video-casting |
+ * @param {Object} detail |
+ * @param {bool} detail.casting True if connected. |
+ */ |
+ _triggerCasting: function(casting) { |
+ this._casting = casting; |
+ this.fire('google-castable-video-casting', { casting: casting }); |
+ }, |
+ |
+ // Redirecting `seeked` event to Chromecast. |
+ _onSeeked: function(){ |
+ if (this._casting) { |
+ var req = new chrome.cast.media.SeekRequest(); |
+ req.currentTime = this.currentTime; |
+ this._castMedia.seek(req); |
+ } |
+ }, |
+ |
+ // Redirecting `volumechange` event to Chromecast. |
+ _onVolumechange: function(){ |
+ if (this._casting) { |
+ var volume = new chrome.cast.Volume(this.volume, this.muted); |
+ var volumeRequest = new chrome.cast.media.VolumeRequest(volume); |
+ this._castMedia.setVolume(volumeRequest); |
+ } |
+ }, |
+ |
+ // Redirecting `timeupdate` event to `google-castable-video-timeupdate`. |
+ _onTimeupdate: function(){ |
+ this._triggerTimeupdate(this.currentTime); |
+ this._bothPaused = this.paused; |
+ } |
+ }); |
+ })(); |
+</script> |