[iOS] add optimized fullscreen API
[WebKit-https.git] / Source / WebCore / Modules / mediacontrols / mediaControlsiOS.js
1 function createControls(root, video, host)
2 {
3     return new ControllerIOS(root, video, host);
4 };
5
6 function ControllerIOS(root, video, host)
7 {
8     this.doingSetup = true;
9     this.hasWirelessPlaybackTargets = false;
10     this._pageScaleFactor = 1;
11     this.isListeningForPlaybackTargetAvailabilityEvent = false;
12     Controller.call(this, root, video, host);
13
14     this.updateWirelessTargetAvailable();
15     this.updateWirelessPlaybackStatus();
16     this.setNeedsTimelineMetricsUpdate();
17
18     host.controlsDependOnPageScaleFactor = true;
19     this.doingSetup = false;
20 };
21
22 /* Enums */
23 ControllerIOS.StartPlaybackControls = 2;
24
25 /* Globals */
26 ControllerIOS.gWirelessImage = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 245"><g fill="#1060FE"><path d="M193.6,6.3v121.6H6.4V6.3H193.6 M199.1,0.7H0.9v132.7h198.2V0.7L199.1,0.7z"/><path d="M43.5,139.3c15.8,8,35.3,12.7,56.5,12.7s40.7-4.7,56.5-12.7H43.5z"/></g><g text-anchor="middle" font-family="Helvetica Neue"><text x="100" y="204" fill="white" font-size="24">##DEVICE_TYPE##</text><text x="100" y="234" fill="#5C5C5C" font-size="21">##DEVICE_NAME##</text></g></svg>';
27 ControllerIOS.gSimulateWirelessPlaybackTarget = false; // Used for testing when there are no wireless targets.
28
29 ControllerIOS.prototype = {
30     addVideoListeners: function() {
31         Controller.prototype.addVideoListeners.call(this);
32
33         this.listenFor(this.video, 'webkitbeginfullscreen', this.handleFullscreenChange);
34         this.listenFor(this.video, 'webkitendfullscreen', this.handleFullscreenChange);
35         this.listenFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange);
36         this.listenFor(this.video, 'webkitpresentationmodechanged', this.handlePresentationModeChange);
37     },
38
39     removeVideoListeners: function() {
40         Controller.prototype.removeVideoListeners.call(this);
41
42         this.stopListeningFor(this.video, 'webkitbeginfullscreen', this.handleFullscreenChange);
43         this.stopListeningFor(this.video, 'webkitendfullscreen', this.handleFullscreenChange);
44         this.stopListeningFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange);
45         this.stopListeningFor(this.video, 'webkitpresentationmodechanged', this.handlePresentationModeChange);
46
47         this.setShouldListenForPlaybackTargetAvailabilityEvent(false);
48     },
49
50     createBase: function() {
51         Controller.prototype.createBase.call(this);
52
53         var startPlaybackButton = this.controls.startPlaybackButton = document.createElement('button');
54         startPlaybackButton.setAttribute('pseudo', '-webkit-media-controls-start-playback-button');
55         startPlaybackButton.setAttribute('aria-label', this.UIString('Start Playback'));
56
57         this.listenFor(this.base, 'gesturestart', this.handleBaseGestureStart);
58         this.listenFor(this.base, 'gesturechange', this.handleBaseGestureChange);
59         this.listenFor(this.base, 'gestureend', this.handleBaseGestureEnd);
60         this.listenFor(this.base, 'touchstart', this.handleWrapperTouchStart);
61         this.stopListeningFor(this.base, 'mousemove', this.handleWrapperMouseMove);
62         this.stopListeningFor(this.base, 'mouseout', this.handleWrapperMouseOut);
63
64         this.listenFor(document, 'visibilitychange', this.handleVisibilityChange);
65     },
66
67     shouldHaveStartPlaybackButton: function() {
68         var allowsInline = this.host.mediaPlaybackAllowsInline;
69
70         if (this.isPlaying)
71             return false;
72
73         if (this.isAudio() && allowsInline)
74             return false;
75
76         if (this.isFullScreen())
77             return false;
78
79         if (!this.video.currentSrc && this.video.error)
80             return false;
81
82         if (!this.video.controls && allowsInline)
83             return false;
84
85         if (this.video.currentSrc && this.video.error)
86             return true;
87
88         if (!this.doingSetup && !this.host.userGestureRequired && allowsInline)
89             return false;
90
91         return true;
92     },
93
94     shouldHaveControls: function() {
95         if (this.shouldHaveStartPlaybackButton())
96             return false;
97
98         return Controller.prototype.shouldHaveControls.call(this);
99     },
100
101     shouldHaveAnyUI: function() {
102         return this.shouldHaveStartPlaybackButton() || Controller.prototype.shouldHaveAnyUI.call(this) || this.currentPlaybackTargetIsWireless();
103     },
104
105     currentPlaybackTargetIsWireless: function() {
106         return ControllerIOS.gSimulateWirelessPlaybackTarget || (('webkitCurrentPlaybackTargetIsWireless' in this.video) && this.video.webkitCurrentPlaybackTargetIsWireless);
107     },
108
109     updateWirelessPlaybackStatus: function() {
110         if (this.currentPlaybackTargetIsWireless()) {
111             var backgroundImageSVG = "url('" + ControllerIOS.gWirelessImage + "')";
112
113             var deviceName = "";
114             var deviceType = "";
115             var type = this.host.externalDeviceType;
116             if (type == "airplay") {
117                 deviceType = this.UIString('##WIRELESS_PLAYBACK_DEVICE_TYPE##');
118                 deviceName = this.UIString('##WIRELESS_PLAYBACK_DEVICE_NAME##', '##DEVICE_NAME##', this.host.externalDeviceDisplayName || "Apple TV");
119             } else if (type == "tvout") {
120                 deviceType = this.UIString('##TVOUT_DEVICE_TYPE##');
121                 deviceName = this.UIString('##TVOUT_DEVICE_NAME##');
122             }
123
124             backgroundImageSVG = backgroundImageSVG.replace('##DEVICE_TYPE##', deviceType);
125             backgroundImageSVG = backgroundImageSVG.replace('##DEVICE_NAME##', deviceName);
126
127             this.controls.inlinePlaybackPlaceholder.style.backgroundImage = backgroundImageSVG;
128             this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', deviceType + ", " + deviceName);
129
130             this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
131             this.controls.wirelessTargetPicker.classList.add(this.ClassNames.active);
132         } else {
133             this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
134             this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.active);
135         }
136     },
137
138     updateWirelessTargetAvailable: function() {
139         if (ControllerIOS.gSimulateWirelessPlaybackTarget || this.hasWirelessPlaybackTargets)
140             this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.hidden);
141         else
142             this.controls.wirelessTargetPicker.classList.add(this.ClassNames.hidden);
143     },
144
145     createControls: function() {
146         Controller.prototype.createControls.call(this);
147
148         var panelCompositedParent = this.controls.panelCompositedParent = document.createElement('div');
149         panelCompositedParent.setAttribute('pseudo', '-webkit-media-controls-panel-composited-parent');
150
151         var inlinePlaybackPlaceholder = this.controls.inlinePlaybackPlaceholder = document.createElement('div');
152         inlinePlaybackPlaceholder.setAttribute('pseudo', '-webkit-media-controls-inline-playback-placeholder');
153         inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
154         inlinePlaybackPlaceholder.setAttribute('aria-label', this.UIString('Display Optimized Full Screen'));
155
156         var wirelessTargetPicker = this.controls.wirelessTargetPicker = document.createElement('button');
157         wirelessTargetPicker.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-picker-button');
158         wirelessTargetPicker.setAttribute('aria-label', this.UIString('Choose Wireless Display'));
159         this.listenFor(wirelessTargetPicker, 'touchstart', this.handleWirelessPickerButtonTouchStart);
160         this.listenFor(wirelessTargetPicker, 'touchend', this.handleWirelessPickerButtonTouchEnd);
161         this.listenFor(wirelessTargetPicker, 'touchcancel', this.handleWirelessPickerButtonTouchCancel);
162
163         if (!ControllerIOS.gSimulateWirelessPlaybackTarget)
164             wirelessTargetPicker.classList.add(this.ClassNames.hidden);
165
166         this.listenFor(this.controls.startPlaybackButton, 'touchstart', this.handleStartPlaybackButtonTouchStart);
167         this.listenFor(this.controls.startPlaybackButton, 'touchend', this.handleStartPlaybackButtonTouchEnd);
168         this.listenFor(this.controls.startPlaybackButton, 'touchcancel', this.handleStartPlaybackButtonTouchCancel);
169
170         this.listenFor(this.controls.panel, 'touchstart', this.handlePanelTouchStart);
171         this.listenFor(this.controls.panel, 'touchend', this.handlePanelTouchEnd);
172         this.listenFor(this.controls.panel, 'touchcancel', this.handlePanelTouchCancel);
173         this.listenFor(this.controls.playButton, 'touchstart', this.handlePlayButtonTouchStart);
174         this.listenFor(this.controls.playButton, 'touchend', this.handlePlayButtonTouchEnd);
175         this.listenFor(this.controls.playButton, 'touchcancel', this.handlePlayButtonTouchCancel);
176         this.listenFor(this.controls.fullscreenButton, 'touchstart', this.handleFullscreenTouchStart);
177         this.listenFor(this.controls.fullscreenButton, 'touchend', this.handleFullscreenTouchEnd);
178         this.listenFor(this.controls.fullscreenButton, 'touchcancel', this.handleFullscreenTouchCancel);
179         this.listenFor(this.controls.optimizedFullscreenButton, 'touchstart', this.handleOptimizedFullscreenTouchStart);
180         this.listenFor(this.controls.optimizedFullscreenButton, 'touchend', this.handleOptimizedFullscreenTouchEnd);
181         this.listenFor(this.controls.optimizedFullscreenButton, 'touchcancel', this.handleOptimizedFullscreenTouchCancel);
182         this.stopListeningFor(this.controls.playButton, 'click', this.handlePlayButtonClicked);
183     },
184
185     setControlsType: function(type) {
186         if (type === this.controlsType)
187             return;
188         Controller.prototype.setControlsType.call(this, type);
189
190         if (type === ControllerIOS.StartPlaybackControls)
191             this.addStartPlaybackControls();
192         else
193             this.removeStartPlaybackControls();
194
195         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
196     },
197
198     addStartPlaybackControls: function() {
199         this.base.appendChild(this.controls.startPlaybackButton);
200     },
201
202     removeStartPlaybackControls: function() {
203         if (this.controls.startPlaybackButton.parentNode)
204             this.controls.startPlaybackButton.parentNode.removeChild(this.controls.startPlaybackButton);
205     },
206
207     configureInlineControls: function() {
208         this.controls.panel.appendChild(this.controls.playButton);
209         this.controls.panel.appendChild(this.controls.statusDisplay);
210         this.controls.panel.appendChild(this.controls.timelineBox);
211         this.controls.panel.appendChild(this.controls.wirelessTargetPicker);
212         if (!this.isLive) {
213             this.controls.timelineBox.appendChild(this.controls.currentTime);
214             this.controls.timelineBox.appendChild(this.controls.timeline);
215             this.controls.timelineBox.appendChild(this.controls.remainingTime);
216         }
217         if (!this.isAudio()) {
218             this.controls.panel.appendChild(this.controls.fullscreenButton);
219             if ('webkitSupportsPresentationMode' in this.video && this.video.webkitSupportsPresentationMode('optimized'))
220                 this.controls.panel.appendChild(this.controls.optimizedFullscreenButton);
221         }
222     },
223
224     configureFullScreenControls: function() {
225         // Do nothing
226     },
227
228     hideControls: function() {
229         Controller.prototype.hideControls.call(this);
230         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
231     },
232
233     showControls: function() {
234         Controller.prototype.showControls.call(this);
235         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
236     },
237
238     addControls: function() {
239         this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
240         this.base.appendChild(this.controls.panelCompositedParent);
241         this.controls.panelCompositedParent.appendChild(this.controls.panel);
242         this.setNeedsTimelineMetricsUpdate();
243     },
244
245     updateControls: function() {
246         if (this.shouldHaveStartPlaybackButton())
247             this.setControlsType(ControllerIOS.StartPlaybackControls);
248         else if (this.presentationMode() === "fullscreen")
249             this.setControlsType(Controller.FullScreenControls);
250         else
251             this.setControlsType(Controller.InlineControls);
252
253         this.setNeedsTimelineMetricsUpdate();
254     },
255
256     updateTime: function() {
257         Controller.prototype.updateTime.call(this);
258         this.updateProgress();
259     },
260
261     progressFillStyle: function() {
262         return 'rgba(0, 0, 0, 0.5)';
263     },
264
265     updateProgress: function(forceUpdate) {
266         Controller.prototype.updateProgress.call(this, forceUpdate);
267
268         if (!forceUpdate && this.controlsAreHidden())
269             return;
270
271         var width = this.timelineWidth;
272         var height = this.timelineHeight;
273
274         // Magic number, matching the value for ::-webkit-media-controls-timeline::-webkit-slider-thumb
275         // in mediaControlsiOS.css. Since we cannot ask the thumb for its offsetWidth as it's in its own
276         // shadow dom, just hard-code the value.
277         var thumbWidth = 16;
278         var endX = thumbWidth / 2 + (width - thumbWidth) * this.video.currentTime / this.video.duration;
279
280         var context = document.getCSSCanvasContext('2d', 'timeline-' + this.timelineID, width, height);
281         context.fillStyle = 'white';
282         context.fillRect(0, 0, endX, height);
283     },
284
285     formatTime: function(time) {
286         if (isNaN(time))
287             time = 0;
288         var absTime = Math.abs(time);
289         var intSeconds = Math.floor(absTime % 60).toFixed(0);
290         var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
291         var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
292         var sign = time < 0 ? '-' : String();
293
294         if (intHours > 0)
295             return sign + intHours + ':' + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2);
296
297         return sign + String('00' + intMinutes).slice(intMinutes >= 10 ? -2 : -1) + ":" + String('00' + intSeconds).slice(-2);
298     },
299
300     handleTimelineChange: function(event) {
301         Controller.prototype.handleTimelineChange.call(this);
302         this.updateProgress();
303     },
304
305     handlePlayButtonTouchStart: function() {
306         this.controls.playButton.classList.add('active');
307     },
308
309     handlePlayButtonTouchEnd: function(event) {
310         this.controls.playButton.classList.remove('active');
311
312         if (this.canPlay())
313             this.video.play();
314         else
315             this.video.pause();
316
317         return true;
318     },
319
320     handlePlayButtonTouchCancel: function(event) {
321         this.controls.playButton.classList.remove('active');
322         return true;
323     },
324
325     handleBaseGestureStart: function(event) {
326         this.gestureStartTime = new Date();
327         // If this gesture started with two fingers inside the video, then
328         // don't treat it as a potential zoom, unless we're still waiting
329         // to play.
330         if (this.mostRecentNumberOfTargettedTouches == 2 && this.controlsType != ControllerIOS.StartPlaybackControls)
331             event.preventDefault();
332     },
333
334     handleBaseGestureChange: function(event) {
335         if (!this.video.controls || this.isAudio() || this.isFullScreen() || this.gestureStartTime === undefined || this.controlsType == ControllerIOS.StartPlaybackControls)
336             return;
337         
338         var scaleDetectionThreshold = 0.2;
339         if (event.scale > 1 + scaleDetectionThreshold || event.scale < 1 - scaleDetectionThreshold)
340             delete this.lastDoubleTouchTime;
341
342         if (this.mostRecentNumberOfTargettedTouches == 2 && event.scale >= 1.0)
343             event.preventDefault();
344
345         var currentGestureTime = new Date();
346         var duration = (currentGestureTime - this.gestureStartTime) / 1000;
347         if (!duration)
348             return;
349
350         var velocity = Math.abs(event.scale - 1) / duration;
351         
352         var pinchOutVelocityThreshold = 2;
353         var pinchOutGestureScaleThreshold = 1.25;
354         if (velocity < pinchOutVelocityThreshold || event.scale < pinchOutGestureScaleThreshold)
355             return;
356
357         delete this.gestureStartTime;
358         this.video.webkitEnterFullscreen();
359     },
360
361     handleBaseGestureEnd: function(event) {
362         delete this.gestureStartTime;
363     },
364
365     handleWrapperTouchStart: function(event) {
366         if (event.target != this.base && event.target != this.controls.inlinePlaybackPlaceholder)
367             return;
368
369         this.mostRecentNumberOfTargettedTouches = event.targetTouches.length;
370         
371         if (this.controlsAreHidden()) {
372             this.showControls();
373             if (this.hideTimer)
374                 clearTimeout(this.hideTimer);
375             this.hideTimer = setTimeout(this.hideControls.bind(this), this.HideControlsDelay);
376         } else if (!this.canPlay())
377             this.hideControls();
378     },
379
380     handlePanelTouchStart: function(event) {
381         this.video.style.webkitUserSelect = 'none';
382     },
383
384     handlePanelTouchEnd: function(event) {
385         this.video.style.removeProperty('-webkit-user-select');
386     },
387
388     handlePanelTouchCancel: function(event) {
389         this.video.style.removeProperty('-webkit-user-select');
390     },
391
392     handleVisibilityChange: function(event) {
393         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
394     },
395
396     presentationMode: function() {
397         if ('webkitPresentationMode' in this.video)
398             return this.video.webkitPresentationMode;
399
400         if (this.isFullScreen())
401             return 'fullscreen';
402
403         return 'inline';
404     },
405
406     isFullScreen: function()
407     {
408         return this.video.webkitDisplayingFullscreen;
409     },
410
411     handleFullscreenButtonClicked: function(event) {
412         if ('webkitSetPresentationMode' in this.video) {
413             if (this.presentationMode() === 'fullscreen')
414                 this.video.webkitSetPresentationMode('inline');
415             else
416                 this.video.webkitSetPresentationMode('fullscreen');
417
418             return;
419         }
420
421         if (this.isFullScreen())
422             this.video.webkitExitFullscreen();
423         else
424             this.video.webkitEnterFullscreen();
425     },
426
427     handleFullscreenTouchStart: function() {
428         this.controls.fullscreenButton.classList.add('active');
429     },
430
431     handleFullscreenTouchEnd: function(event) {
432         this.controls.fullscreenButton.classList.remove('active');
433
434         this.handleFullscreenButtonClicked();
435
436         return true;
437     },
438
439     handleFullscreenTouchCancel: function(event) {
440         this.controls.fullscreenButton.classList.remove('active');
441         return true;
442     },
443
444     handleOptimizedFullscreenButtonClicked: function(event) {
445         if (!('webkitSetPresentationMode' in this.video))
446             return;
447
448         if (this.presentationMode() === 'optimized')
449             this.video.webkitSetPresentationMode('inline');
450         else
451             this.video.webkitSetPresentationMode('optimized');
452     },
453         
454     handleOptimizedFullscreenTouchStart: function() {
455         this.controls.optimizedFullscreenButton.classList.add('active');
456     },
457         
458     handleOptimizedFullscreenTouchEnd: function(event) {
459         this.controls.optimizedFullscreenButton.classList.remove('active');
460         
461         this.handleOptimizedFullscreenButtonClicked();
462         
463         return true;
464     },
465         
466     handleOptimizedFullscreenTouchCancel: function(event) {
467         this.controls.optimizedFullscreenButton.classList.remove('active');
468         return true;
469     },
470
471     handleStartPlaybackButtonTouchStart: function(event) {
472         this.controls.startPlaybackButton.classList.add('active');
473     },
474
475     handleStartPlaybackButtonTouchEnd: function(event) {
476         this.controls.startPlaybackButton.classList.remove('active');
477         if (this.video.error)
478             return true;
479
480         this.video.play();
481         this.updateControls();
482
483         return true;
484     },
485
486     handleStartPlaybackButtonTouchCancel: function(event) {
487         this.controls.startPlaybackButton.classList.remove('active');
488         return true;
489     },
490
491     handleReadyStateChange: function(event) {
492         Controller.prototype.handleReadyStateChange.call(this, event);
493         this.updateControls();
494     },
495
496     handleWirelessPlaybackChange: function(event) {
497         this.updateWirelessPlaybackStatus();
498         this.setNeedsTimelineMetricsUpdate();
499     },
500
501     handleWirelessTargetAvailableChange: function(event) {
502         var wirelessPlaybackTargetsAvailable = event.availability == "available";
503         if (this.hasWirelessPlaybackTargets === wirelessPlaybackTargetsAvailable)
504             return;
505
506         this.hasWirelessPlaybackTargets = wirelessPlaybackTargetsAvailable;
507         this.updateWirelessTargetAvailable();
508         this.setNeedsTimelineMetricsUpdate();
509     },
510
511     handleWirelessPickerButtonTouchStart: function() {
512         if (!this.video.error)
513             this.controls.wirelessTargetPicker.classList.add('active');
514     },
515
516     handleWirelessPickerButtonTouchEnd: function(event) {
517         this.controls.wirelessTargetPicker.classList.remove('active');
518         this.video.webkitShowPlaybackTargetPicker();
519         return true;
520     },
521
522     handleWirelessPickerButtonTouchCancel: function(event) {
523         this.controls.wirelessTargetPicker.classList.remove('active');
524         return true;
525     },
526
527     updateShouldListenForPlaybackTargetAvailabilityEvent: function() {
528         var shouldListen = true;
529         if (this.video.error)
530             shouldListen = false;
531         if (this.controlsType === ControllerIOS.StartPlaybackControls)
532             shouldListen = false;
533         if (!this.isAudio() && !this.video.paused && this.controlsAreHidden())
534             shouldListen = false;
535         if (document.hidden)
536             shouldListen = false;
537
538         this.setShouldListenForPlaybackTargetAvailabilityEvent(shouldListen);
539     },
540
541     updateStatusDisplay: function(event)
542     {
543         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
544         this.controls.startPlaybackButton.classList.toggle(this.ClassNames.failed, this.video.error !== null);
545         Controller.prototype.updateStatusDisplay.call(this, event);
546     },
547
548     setPlaying: function(isPlaying)
549     {
550         Controller.prototype.setPlaying.call(this, isPlaying);
551         this.updateControls();
552     },
553
554     setShouldListenForPlaybackTargetAvailabilityEvent: function(shouldListen)
555     {
556         if (!window.WebKitPlaybackTargetAvailabilityEvent || this.isListeningForPlaybackTargetAvailabilityEvent == shouldListen)
557             return;
558
559         if (shouldListen && (this.shouldHaveStartPlaybackButton() || this.video.error))
560             return;
561
562         this.isListeningForPlaybackTargetAvailabilityEvent = shouldListen;
563         if (shouldListen)
564             this.listenFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
565         else
566             this.stopListeningFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
567     },
568
569     get pageScaleFactor()
570     {
571         return this._pageScaleFactor;
572     },
573
574     set pageScaleFactor(newScaleFactor)
575     {
576         if (this._pageScaleFactor === newScaleFactor)
577             return;
578
579         this._pageScaleFactor = newScaleFactor;
580
581         if (newScaleFactor) {
582             var scaleValue = 1 / newScaleFactor;
583             var scaleTransform = "scale(" + scaleValue + ")";
584             if (this.controls.startPlaybackButton)
585                 this.controls.startPlaybackButton.style.webkitTransform = scaleTransform;
586             if (this.controls.panel) {
587                 var bottomAligment = -2 * scaleValue;
588                 this.controls.panel.style.bottom = bottomAligment + "px";
589                 this.controls.panel.style.paddingBottom = -(newScaleFactor * bottomAligment) + "px";
590                 this.controls.panel.style.width = Math.ceil(newScaleFactor * 100) + "%";
591                 this.controls.panel.style.webkitTransform = scaleTransform;
592                 this.setNeedsTimelineMetricsUpdate();
593                 this.updateProgress();
594             }
595         }
596     },
597
598     handlePresentationModeChange: function(event)
599     {
600         var presentationMode = this.presentationMode();
601
602         switch (presentationMode) {
603             case 'inline':
604                 this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
605                 break;
606             case 'optimized':
607                 var backgroundImageSVG = "url('" + this.host.mediaUIImageData("optimized-fullscreen-placeholder") + "')";
608                 this.controls.inlinePlaybackPlaceholder.style.backgroundImage = backgroundImageSVG;
609                 this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', "video playback placeholder");
610                 this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
611                 break;
612         }
613
614         this.updateControls();
615         this.updateCaptionContainer();
616         if (presentationMode != 'fullscreen' && this.video.paused && this.controlsAreHidden())
617             this.showControls();
618     },
619
620     handleFullscreenChange: function(event)
621     {
622         Controller.prototype.handleFullscreenChange.call(this, event);
623         this.handlePresentationModeChange(event);
624     },
625
626 };
627
628 Object.create(Controller.prototype).extend(ControllerIOS.prototype);
629 Object.defineProperty(ControllerIOS.prototype, 'constructor', { enumerable: false, value: ControllerIOS });