[Media] Fullscreen button should always come last in inline controls (141245)
[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     reconnectControls: function()
208     {
209         Controller.prototype.reconnectControls.call(this);
210
211         if (this.controlsType === ControllerIOS.StartPlaybackControls)
212             this.addStartPlaybackControls();
213     },
214
215     configureInlineControls: function() {
216         this.controls.panel.appendChild(this.controls.playButton);
217         this.controls.panel.appendChild(this.controls.statusDisplay);
218         this.controls.panel.appendChild(this.controls.timelineBox);
219         this.controls.panel.appendChild(this.controls.wirelessTargetPicker);
220         if (!this.isLive) {
221             this.controls.timelineBox.appendChild(this.controls.currentTime);
222             this.controls.timelineBox.appendChild(this.controls.timeline);
223             this.controls.timelineBox.appendChild(this.controls.remainingTime);
224         }
225         if (!this.isAudio()) {
226             if ('webkitSupportsPresentationMode' in this.video && this.video.webkitSupportsPresentationMode('optimized'))
227                 this.controls.panel.appendChild(this.controls.optimizedFullscreenButton);
228             this.controls.panel.appendChild(this.controls.fullscreenButton);
229         }
230     },
231
232     configureFullScreenControls: function() {
233         // Do nothing
234     },
235
236     hideControls: function() {
237         Controller.prototype.hideControls.call(this);
238         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
239     },
240
241     showControls: function() {
242         Controller.prototype.showControls.call(this);
243         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
244     },
245
246     addControls: function() {
247         this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
248         this.base.appendChild(this.controls.panelCompositedParent);
249         this.controls.panelCompositedParent.appendChild(this.controls.panel);
250         this.setNeedsTimelineMetricsUpdate();
251     },
252
253     updateControls: function() {
254         if (this.shouldHaveStartPlaybackButton())
255             this.setControlsType(ControllerIOS.StartPlaybackControls);
256         else if (this.presentationMode() === "fullscreen")
257             this.setControlsType(Controller.FullScreenControls);
258         else
259             this.setControlsType(Controller.InlineControls);
260
261         this.setNeedsTimelineMetricsUpdate();
262     },
263
264     updateTime: function() {
265         Controller.prototype.updateTime.call(this);
266         this.updateProgress();
267     },
268
269     progressFillStyle: function() {
270         return 'rgba(0, 0, 0, 0.5)';
271     },
272
273     updateProgress: function(forceUpdate) {
274         Controller.prototype.updateProgress.call(this, forceUpdate);
275
276         if (!forceUpdate && this.controlsAreHidden())
277             return;
278
279         var width = this.timelineWidth;
280         var height = this.timelineHeight;
281
282         // Magic number, matching the value for ::-webkit-media-controls-timeline::-webkit-slider-thumb
283         // in mediaControlsiOS.css. Since we cannot ask the thumb for its offsetWidth as it's in its own
284         // shadow dom, just hard-code the value.
285         var thumbWidth = 16;
286         var endX = thumbWidth / 2 + (width - thumbWidth) * this.video.currentTime / this.video.duration;
287
288         var context = document.getCSSCanvasContext('2d', 'timeline-' + this.timelineID, width, height);
289         context.fillStyle = 'white';
290         context.fillRect(0, 0, endX, height);
291     },
292
293     formatTime: function(time) {
294         if (isNaN(time))
295             time = 0;
296         var absTime = Math.abs(time);
297         var intSeconds = Math.floor(absTime % 60).toFixed(0);
298         var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
299         var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
300         var sign = time < 0 ? '-' : String();
301
302         if (intHours > 0)
303             return sign + intHours + ':' + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2);
304
305         return sign + String('00' + intMinutes).slice(intMinutes >= 10 ? -2 : -1) + ":" + String('00' + intSeconds).slice(-2);
306     },
307
308     handleTimelineChange: function(event) {
309         Controller.prototype.handleTimelineChange.call(this);
310         this.updateProgress();
311     },
312
313     handlePlayButtonTouchStart: function() {
314         this.controls.playButton.classList.add('active');
315     },
316
317     handlePlayButtonTouchEnd: function(event) {
318         this.controls.playButton.classList.remove('active');
319
320         if (this.canPlay())
321             this.video.play();
322         else
323             this.video.pause();
324
325         return true;
326     },
327
328     handlePlayButtonTouchCancel: function(event) {
329         this.controls.playButton.classList.remove('active');
330         return true;
331     },
332
333     handleBaseGestureStart: function(event) {
334         this.gestureStartTime = new Date();
335         // If this gesture started with two fingers inside the video, then
336         // don't treat it as a potential zoom, unless we're still waiting
337         // to play.
338         if (this.mostRecentNumberOfTargettedTouches == 2 && this.controlsType != ControllerIOS.StartPlaybackControls)
339             event.preventDefault();
340     },
341
342     handleBaseGestureChange: function(event) {
343         if (!this.video.controls || this.isAudio() || this.isFullScreen() || this.gestureStartTime === undefined || this.controlsType == ControllerIOS.StartPlaybackControls)
344             return;
345         
346         var scaleDetectionThreshold = 0.2;
347         if (event.scale > 1 + scaleDetectionThreshold || event.scale < 1 - scaleDetectionThreshold)
348             delete this.lastDoubleTouchTime;
349
350         if (this.mostRecentNumberOfTargettedTouches == 2 && event.scale >= 1.0)
351             event.preventDefault();
352
353         var currentGestureTime = new Date();
354         var duration = (currentGestureTime - this.gestureStartTime) / 1000;
355         if (!duration)
356             return;
357
358         var velocity = Math.abs(event.scale - 1) / duration;
359         
360         var pinchOutVelocityThreshold = 2;
361         var pinchOutGestureScaleThreshold = 1.25;
362         if (velocity < pinchOutVelocityThreshold || event.scale < pinchOutGestureScaleThreshold)
363             return;
364
365         delete this.gestureStartTime;
366         this.video.webkitEnterFullscreen();
367     },
368
369     handleBaseGestureEnd: function(event) {
370         delete this.gestureStartTime;
371     },
372
373     handleWrapperTouchStart: function(event) {
374         if (event.target != this.base && event.target != this.controls.inlinePlaybackPlaceholder)
375             return;
376
377         this.mostRecentNumberOfTargettedTouches = event.targetTouches.length;
378         
379         if (this.controlsAreHidden()) {
380             this.showControls();
381             if (this.hideTimer)
382                 clearTimeout(this.hideTimer);
383             this.hideTimer = setTimeout(this.hideControls.bind(this), this.HideControlsDelay);
384         } else if (!this.canPlay())
385             this.hideControls();
386     },
387
388     handlePanelTouchStart: function(event) {
389         this.video.style.webkitUserSelect = 'none';
390     },
391
392     handlePanelTouchEnd: function(event) {
393         this.video.style.removeProperty('-webkit-user-select');
394     },
395
396     handlePanelTouchCancel: function(event) {
397         this.video.style.removeProperty('-webkit-user-select');
398     },
399
400     handleVisibilityChange: function(event) {
401         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
402     },
403
404     presentationMode: function() {
405         if ('webkitPresentationMode' in this.video)
406             return this.video.webkitPresentationMode;
407
408         if (this.isFullScreen())
409             return 'fullscreen';
410
411         return 'inline';
412     },
413
414     isFullScreen: function()
415     {
416         return this.video.webkitDisplayingFullscreen;
417     },
418
419     handleFullscreenButtonClicked: function(event) {
420         if ('webkitSetPresentationMode' in this.video) {
421             if (this.presentationMode() === 'fullscreen')
422                 this.video.webkitSetPresentationMode('inline');
423             else
424                 this.video.webkitSetPresentationMode('fullscreen');
425
426             return;
427         }
428
429         if (this.isFullScreen())
430             this.video.webkitExitFullscreen();
431         else
432             this.video.webkitEnterFullscreen();
433     },
434
435     handleFullscreenTouchStart: function() {
436         this.controls.fullscreenButton.classList.add('active');
437     },
438
439     handleFullscreenTouchEnd: function(event) {
440         this.controls.fullscreenButton.classList.remove('active');
441
442         this.handleFullscreenButtonClicked();
443
444         return true;
445     },
446
447     handleFullscreenTouchCancel: function(event) {
448         this.controls.fullscreenButton.classList.remove('active');
449         return true;
450     },
451
452     handleOptimizedFullscreenButtonClicked: function(event) {
453         if (!('webkitSetPresentationMode' in this.video))
454             return;
455
456         if (this.presentationMode() === 'optimized')
457             this.video.webkitSetPresentationMode('inline');
458         else
459             this.video.webkitSetPresentationMode('optimized');
460     },
461         
462     handleOptimizedFullscreenTouchStart: function() {
463         this.controls.optimizedFullscreenButton.classList.add('active');
464     },
465         
466     handleOptimizedFullscreenTouchEnd: function(event) {
467         this.controls.optimizedFullscreenButton.classList.remove('active');
468         
469         this.handleOptimizedFullscreenButtonClicked();
470         
471         return true;
472     },
473         
474     handleOptimizedFullscreenTouchCancel: function(event) {
475         this.controls.optimizedFullscreenButton.classList.remove('active');
476         return true;
477     },
478
479     handleStartPlaybackButtonTouchStart: function(event) {
480         this.controls.startPlaybackButton.classList.add('active');
481     },
482
483     handleStartPlaybackButtonTouchEnd: function(event) {
484         this.controls.startPlaybackButton.classList.remove('active');
485         if (this.video.error)
486             return true;
487
488         this.video.play();
489         this.updateControls();
490
491         return true;
492     },
493
494     handleStartPlaybackButtonTouchCancel: function(event) {
495         this.controls.startPlaybackButton.classList.remove('active');
496         return true;
497     },
498
499     handleReadyStateChange: function(event) {
500         Controller.prototype.handleReadyStateChange.call(this, event);
501         this.updateControls();
502     },
503
504     handleWirelessPlaybackChange: function(event) {
505         this.updateWirelessPlaybackStatus();
506         this.setNeedsTimelineMetricsUpdate();
507     },
508
509     handleWirelessTargetAvailableChange: function(event) {
510         var wirelessPlaybackTargetsAvailable = event.availability == "available";
511         if (this.hasWirelessPlaybackTargets === wirelessPlaybackTargetsAvailable)
512             return;
513
514         this.hasWirelessPlaybackTargets = wirelessPlaybackTargetsAvailable;
515         this.updateWirelessTargetAvailable();
516         this.setNeedsTimelineMetricsUpdate();
517     },
518
519     handleWirelessPickerButtonTouchStart: function() {
520         if (!this.video.error)
521             this.controls.wirelessTargetPicker.classList.add('active');
522     },
523
524     handleWirelessPickerButtonTouchEnd: function(event) {
525         this.controls.wirelessTargetPicker.classList.remove('active');
526         this.video.webkitShowPlaybackTargetPicker();
527         return true;
528     },
529
530     handleWirelessPickerButtonTouchCancel: function(event) {
531         this.controls.wirelessTargetPicker.classList.remove('active');
532         return true;
533     },
534
535     updateShouldListenForPlaybackTargetAvailabilityEvent: function() {
536         var shouldListen = true;
537         if (this.video.error)
538             shouldListen = false;
539         if (this.controlsType === ControllerIOS.StartPlaybackControls)
540             shouldListen = false;
541         if (!this.isAudio() && !this.video.paused && this.controlsAreHidden())
542             shouldListen = false;
543         if (document.hidden)
544             shouldListen = false;
545
546         this.setShouldListenForPlaybackTargetAvailabilityEvent(shouldListen);
547     },
548
549     updateStatusDisplay: function(event)
550     {
551         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
552         this.controls.startPlaybackButton.classList.toggle(this.ClassNames.failed, this.video.error !== null);
553         Controller.prototype.updateStatusDisplay.call(this, event);
554     },
555
556     setPlaying: function(isPlaying)
557     {
558         Controller.prototype.setPlaying.call(this, isPlaying);
559         this.updateControls();
560     },
561
562     setShouldListenForPlaybackTargetAvailabilityEvent: function(shouldListen)
563     {
564         if (!window.WebKitPlaybackTargetAvailabilityEvent || this.isListeningForPlaybackTargetAvailabilityEvent == shouldListen)
565             return;
566
567         if (shouldListen && (this.shouldHaveStartPlaybackButton() || this.video.error))
568             return;
569
570         this.isListeningForPlaybackTargetAvailabilityEvent = shouldListen;
571         if (shouldListen)
572             this.listenFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
573         else
574             this.stopListeningFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
575     },
576
577     get pageScaleFactor()
578     {
579         return this._pageScaleFactor;
580     },
581
582     set pageScaleFactor(newScaleFactor)
583     {
584         if (this._pageScaleFactor === newScaleFactor)
585             return;
586
587         this._pageScaleFactor = newScaleFactor;
588
589         if (newScaleFactor) {
590             var scaleValue = 1 / newScaleFactor;
591             var scaleTransform = "scale(" + scaleValue + ")";
592             if (this.controls.startPlaybackButton)
593                 this.controls.startPlaybackButton.style.webkitTransform = scaleTransform;
594             if (this.controls.panel) {
595                 var bottomAligment = -2 * scaleValue;
596                 this.controls.panel.style.bottom = bottomAligment + "px";
597                 this.controls.panel.style.paddingBottom = -(newScaleFactor * bottomAligment) + "px";
598                 this.controls.panel.style.width = Math.ceil(newScaleFactor * 100) + "%";
599                 this.controls.panel.style.webkitTransform = scaleTransform;
600                 this.setNeedsTimelineMetricsUpdate();
601                 this.updateProgress();
602             }
603         }
604     },
605
606     handlePresentationModeChange: function(event)
607     {
608         var presentationMode = this.presentationMode();
609
610         switch (presentationMode) {
611             case 'inline':
612                 this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
613                 break;
614             case 'optimized':
615                 var backgroundImageSVG = "url('" + this.host.mediaUIImageData("optimized-fullscreen-placeholder") + "')";
616                 this.controls.inlinePlaybackPlaceholder.style.backgroundImage = backgroundImageSVG;
617                 this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', "video playback placeholder");
618                 this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
619                 break;
620         }
621
622         this.updateControls();
623         this.updateCaptionContainer();
624         if (presentationMode != 'fullscreen' && this.video.paused && this.controlsAreHidden())
625             this.showControls();
626     },
627
628     handleFullscreenChange: function(event)
629     {
630         Controller.prototype.handleFullscreenChange.call(this, event);
631         this.handlePresentationModeChange(event);
632     },
633
634 };
635
636 Object.create(Controller.prototype).extend(ControllerIOS.prototype);
637 Object.defineProperty(ControllerIOS.prototype, 'constructor', { enumerable: false, value: ControllerIOS });