1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2009 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package spark.components
13{
14
15import flash.display.DisplayObject;
16import flash.display.StageDisplayState;
17import flash.events.Event;
18import flash.events.FullScreenEvent;
19import flash.events.KeyboardEvent;
20import flash.events.MouseEvent;
21import flash.events.TimerEvent;
22import flash.geom.Rectangle;
23import flash.media.Video;
24import flash.system.ApplicationDomain;
25import flash.utils.Timer;
26
27import mx.core.FlexGlobals;
28import mx.core.IVisualElementContainer;
29import mx.core.mx_internal;
30import mx.events.FlexEvent;
31import mx.managers.PopUpManager;
32import mx.utils.BitFlagUtil;
33
34import org.osmf.events.LoadEvent;
35import org.osmf.events.MediaPlayerStateChangeEvent;
36import org.osmf.events.TimeEvent;
37import org.osmf.media.MediaPlayerState;
38
39import spark.components.mediaClasses.MuteButton;
40import spark.components.mediaClasses.ScrubBar;
41import spark.components.mediaClasses.VolumeBar;
42import spark.components.supportClasses.ButtonBase;
43import spark.components.supportClasses.SkinnableComponent;
44import spark.components.supportClasses.ToggleButtonBase;
45import spark.core.IDisplayText;
46import spark.events.TrackBaseEvent;
47
48use namespace mx_internal;
49
50//--------------------------------------
51//  Events
52//--------------------------------------
53
54/**
55 *  Dispatched when the data is received as a download operation progresses.
56 *  This event is only dispatched when playing a video by downloading it
57 *  directly from a server, typically by issuing an HTTP request.
58 *  It is not displatched when playing a video from a special media server,
59 *  such as Flash Media Server.
60 *
61 *  <p>This event may not be dispatched when the source is set to null or a playback
62 *  error occurs.</p>
63 *
64 *  @eventType org.osmf.events.LoadEvent.BYTES_LOADED_CHANGE
65 *
66 *  @langversion 3.0
67 *  @playerversion Flash 10
68 *  @playerversion AIR 1.0
69 *  @productversion Flex 4
70 */
71[Event(name="bytesLoadedChange",type="org.osmf.events.LoadEvent")]
72
73/**
74 *  Dispatched when the playhead reaches the duration for playable media.
75 *
76 *  @eventType org.osmf.events.TimeEvent.COMPLETE
77 *
78 *  @langversion 3.0
79 *  @playerversion Flash 10
80 *  @playerversion AIR 1.0
81 *  @productversion Flex 4
82 */
83[Event(name="complete", type="org.osmf.events.TimeEvent")]
84
85/**
86 *  Dispatched when the <code>currentTime</code> property of the MediaPlayer has changed.
87 *
88 *  <p>This event may not be dispatched when the source is set to null or a playback
89 *  error occurs.</p>
90 *
91 *  @eventType org.osmf.events.TimeEvent.CURRENT_TIME_CHANGE
92 *
93 *  @langversion 3.0
94 *  @playerversion Flash 10
95 *  @playerversion AIR 1.0
96 *  @productversion Flex 4
97 */
98[Event(name="currentTimeChange",type="org.osmf.events.TimeEvent")]
99
100/**
101 *  Dispatched when the <code>duration</code> property of the media has changed.
102 *
103 *  <p>This event may not be dispatched when the source is set to null or a playback
104 *  error occurs.</p>
105 *
106 *  @eventType org.osmf.events.TimeEvent.DURATION_CHANGE
107 *
108 *  @langversion 3.0
109 *  @playerversion Flash 10
110 *  @playerversion AIR 1.0
111 *  @productversion Flex 4
112 */
113[Event(name="durationChange", type="org.osmf.events.TimeEvent")]
114
115/**
116 *  Dispatched when the MediaPlayer's state has changed.
117 *
118 *  @eventType org.osmf.events.MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE
119 *
120 *  @langversion 3.0
121 *  @playerversion Flash 10
122 *  @playerversion AIR 1.0
123 *  @productversion Flex 4
124 */
125[Event(name="mediaPlayerStateChange", type="org.osmf.events.MediaPlayerStateChangeEvent")]
126
127//--------------------------------------
128//  Styles
129//--------------------------------------
130
131include "../styles/metadata/BasicInheritingTextStyles.as";
132
133/**
134 *  Controls the visibility of the drop shadow for this component.
135 *
136 *  @default true
137 *
138 *  @langversion 3.0
139 *  @playerversion Flash 10
140 *  @playerversion AIR 1.5
141 *  @productversion Flex 4
142 */
143[Style(name="dropShadowVisible", type="Boolean", inherit="no", theme="spark")]
144
145/**
146 *  The time, in milli-seconds, to wait in fullscreen mode with no user-interaction
147 *  before hiding the video playback controls.
148 *
149 *  <p>If set to <code>Infinity</code>, then the playback controls will not
150 *  be hidden in fullscreen mode.  Changing this value while already in
151 *  fullscreen mode has no effect.</p>
152 *
153 *  @default 3000
154 *
155 *  @langversion 3.0
156 *  @playerversion Flash 10
157 *  @playerversion AIR 1.5
158 *  @productversion Flex 4
159 */
160[Style(name="fullScreenHideControlsDelay", type="Number", format="Time", inherit="no")]
161
162/**
163 *  @copy spark.components.supportClasses.GroupBase#style:symbolColor
164 *
165 *  @langversion 3.0
166 *  @playerversion Flash 10
167 *  @playerversion AIR 1.5
168 *  @productversion Flex 4
169 */
170[Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark")]
171
172//--------------------------------------
173//  SkinStates
174//--------------------------------------
175
176/**
177 *  Uninitialized state of the VideoPlayer.
178 *  The Video Player has been constructed at this point,
179 *  but the source has not been set and no connection
180 *  attempt is in progress.
181 *
182 *  @langversion 3.0
183 *  @playerversion Flash 10
184 *  @playerversion AIR 1.5
185 *  @productversion Flex 4
186 */
187[SkinState("uninitialized")]
188
189/**
190 *  Loading state of the VideoPlayer.
191 *  The VideoPlayer is loading or connecting to the source.
192 *
193 *  @langversion 3.0
194 *  @playerversion Flash 10
195 *  @playerversion AIR 1.5
196 *  @productversion Flex 4
197 */
198[SkinState("loading")]
199
200/**
201 *  Ready state of the VideoPlayer.
202 *  The video is ready to be played.
203 *
204 *  @langversion 3.0
205 *  @playerversion Flash 10
206 *  @playerversion AIR 1.5
207 *  @productversion Flex 4
208 */
209[SkinState("ready")]
210
211/**
212 *  Playing state of the VideoPlayer
213 *
214 *  @langversion 3.0
215 *  @playerversion Flash 10
216 *  @playerversion AIR 1.5
217 *  @productversion Flex 4
218 */
219[SkinState("playing")]
220
221/**
222 *  Paused state of the VideoPlayer
223 *
224 *  @langversion 3.0
225 *  @playerversion Flash 10
226 *  @playerversion AIR 1.5
227 *  @productversion Flex 4
228 */
229[SkinState("paused")]
230
231/**
232 *  Buffering state of the VideoPlayer
233 *
234 *  @langversion 3.0
235 *  @playerversion Flash 10
236 *  @playerversion AIR 1.5
237 *  @productversion Flex 4
238 */
239[SkinState("buffering")]
240
241/**
242 *  Playback Error state of the VideoPlayer.
243 *  An error was encountered while trying to play the video.
244 *
245 *  @langversion 3.0
246 *  @playerversion Flash 10
247 *  @playerversion AIR 1.5
248 *  @productversion Flex 4
249 */
250[SkinState("playbackError")]
251
252/**
253 *  Disabled state of the VideoPlayer
254 *
255 *  @langversion 3.0
256 *  @playerversion Flash 10
257 *  @playerversion AIR 1.5
258 *  @productversion Flex 4
259 */
260[SkinState("disabled")]
261
262/**
263 *  Uninitialized state of the VideoPlayer when
264 *  in full screen mode.
265 *  The Video Player has been constructed at this point,
266 *  but the source has not been set and no connection
267 *  attempt is in progress.
268 *
269 *  @langversion 3.0
270 *  @playerversion Flash 10
271 *  @playerversion AIR 1.5
272 *  @productversion Flex 4
273 */
274[SkinState("uninitializedAndFullScreen")]
275
276/**
277 *  Loading state of the VideoPlayer when
278 *  in full screen mode.
279 *  The VideoPlayer is loading or connecting to the source.
280 *
281 *  @langversion 3.0
282 *  @playerversion Flash 10
283 *  @playerversion AIR 1.5
284 *  @productversion Flex 4
285 */
286[SkinState("loadingAndFullScreen")]
287
288/**
289 *  Ready state of the VideoPlayer when
290 *  in full screen mode.  The video is ready to be played.
291 *
292 *  @langversion 3.0
293 *  @playerversion Flash 10
294 *  @playerversion AIR 1.5
295 *  @productversion Flex 4
296 */
297[SkinState("readyAndFullScreen")]
298
299/**
300 *  Playing state of the VideoPlayer when
301 *  in full screen mode.
302 *
303 *  @langversion 3.0
304 *  @playerversion Flash 10
305 *  @playerversion AIR 1.5
306 *  @productversion Flex 4
307 */
308[SkinState("playingAndFullScreen")]
309
310/**
311 *  Paused state of the VideoPlayer when
312 *  in full screen mode.
313 *
314 *  @langversion 3.0
315 *  @playerversion Flash 10
316 *  @playerversion AIR 1.5
317 *  @productversion Flex 4
318 */
319[SkinState("pausedAndFullScreen")]
320
321/**
322 *  Buffering state of the VideoPlayer when
323 *  in full screen mode.
324 *
325 *  @langversion 3.0
326 *  @playerversion Flash 10
327 *  @playerversion AIR 1.5
328 *  @productversion Flex 4
329 */
330[SkinState("bufferingAndFullScreen")]
331
332/**
333 *  Playback Error state of the VideoPlayer when
334 *  in full screen mode.
335 *  An error was encountered while trying to play the video.
336 *
337 *  @langversion 3.0
338 *  @playerversion Flash 10
339 *  @playerversion AIR 1.5
340 *  @productversion Flex 4
341 */
342[SkinState("playbackErrorAndFullScreen")]
343
344/**
345 *  Disabled state of the VideoPlayer when
346 *  in full screen mode.
347 *
348 *  @langversion 3.0
349 *  @playerversion Flash 10
350 *  @playerversion AIR 1.5
351 *  @productversion Flex 4
352 */
353[SkinState("disabledAndFullScreen")]
354
355//--------------------------------------
356//  Excluded APIs
357//--------------------------------------
358
359[Exclude(name="focusBlendMode", kind="style")]
360[Exclude(name="focusThickness", kind="style")]
361
362//--------------------------------------
363//  Other metadata
364//--------------------------------------
365
366[AccessibilityClass(implementation="spark.accessibility.VideoPlayerAccImpl")]
367
368[DefaultProperty("source")]
369
370[IconFile("VideoPlayer.png")]
371
372/**
373 * Because this component does not define a skin for the mobile theme, Adobe
374 * recommends that you not use it in a mobile application. Alternatively, you
375 * can define your own mobile skin for the component. For more information,
376 * see <a href="http://help.adobe.com/en_US/flex/mobileapps/WS19f279b149e7481c698e85712b3011fe73-8000.html">Basics of mobile skinning</a>.
377 */
378[DiscouragedForProfile("mobileDevice")]
379
380/**
381 *  The VideoPlayer control is a skinnable video player that supports
382 *  progressive download, multi-bitrate streaming, and streaming video.
383 *  It supports playback of FLV and F4v files. The VideoPlayer control
384 *  contains a full-featured UI for controlling video playback.
385 *
386 *  <p><code>VideoDisplay</code> is the chromeless version that does not support skinning.
387 *  It is useful when you do not want the user to interact with the control.</p>
388 *
389 *  <p>The VideoPlayer control has the following default characteristics:</p>
390 *     <table class="innertable">
391 *        <tr>
392 *           <th>Characteristic</th>
393 *           <th>Description</th>
394 *        </tr>
395 *        <tr>
396 *           <td>Default size</td>
397 *           <td>263 pixels wide by 184 pixels high</td>
398 *        </tr>
399 *        <tr>
400 *           <td>Minimum size</td>
401 *           <td>0</td>
402 *        </tr>
403 *        <tr>
404 *           <td>Maximum size</td>
405 *           <td>10000 pixels wide and 10000 pixels high</td>
406 *        </tr>
407 *        <tr>
408 *           <td>Default skin class</td>
409 *           <td>spark.skins.spark.VideoPlayerSkin</td>
410 *        </tr>
411 *     </table>
412 *
413 *  @see spark.components.VideoDisplay
414 *  @see spark.skins.spark.VideoPlayerSkin
415 *  @see spark.skins.spark.mediaClasses.fullScreen.FullScreenButtonSkin
416 *  @see spark.skins.spark.mediaClasses.fullScreen.MuteButtonSkin
417 *  @see spark.skins.spark.mediaClasses.fullScreen.PlayPauseButtonSkin
418 *  @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarSkin
419 *  @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarThumbSkin
420 *  @see spark.skins.spark.mediaClasses.fullScreen.ScrubBarTrackSkin
421 *  @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarSkin
422 *  @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarThumbSkin
423 *  @see spark.skins.spark.mediaClasses.fullScreen.VolumeBarTrackSkin
424 *  @see spark.skins.spark.mediaClasses.normal.FullScreenButtonSkin
425 *  @see spark.skins.spark.mediaClasses.normal.MuteButtonSkin
426 *  @see spark.skins.spark.mediaClasses.normal.PlayPauseButtonSkin
427 *  @see spark.skins.spark.mediaClasses.normal.ScrubBarSkin
428 *  @see spark.skins.spark.mediaClasses.normal.ScrubBarThumbSkin
429 *  @see spark.skins.spark.mediaClasses.normal.ScrubBarTrackSkin
430 *  @see spark.skins.spark.mediaClasses.normal.VolumeBarSkin
431 *  @see spark.skins.spark.mediaClasses.normal.VolumeBarThumbSkin
432 *  @see spark.skins.spark.mediaClasses.normal.VolumeBarTrackSkin
433 *
434 *  @mxml
435 *
436 *  <p>The <code>&lt;s:VideoPlayer&gt;</code> tag inherits all of the tag
437 *  attributes of its superclass and adds the following tag attributes:</p>
438 *
439 *  <pre>
440 *  &lt;s:VideoPlayer
441
442 *    <strong>Properties</strong>
443 *    autoDisplayFirstFrame="true"
444 *    autoPlay="true"
445 *    autoRewind="true"
446 *    loop="false"
447 *    muted="false"
448 *    pauseWhenHidden="true"
449 *    scaleMode="letterbox"
450 *    source=""
451 *    volume="1"
452 *
453 *    <strong>Events</strong>
454 *    bytesLoadedChange="<i>No default</i>"
455 *    complete="<i>No default</i>"
456 *    currentTimeChange="<i>No default</i>"
457 *    durationChange="<i>No default</i>"
458 *    mediaPlayerStateChange="<i>No default</i>"
459 *
460 *
461 *    <strong>Styles</strong>
462 *    alignmentBaseline="baseline"
463 *    baselineShift="0"
464 *    cffHinting="0.0"
465 *    color="0x000000"
466 *    digitCase="default"
467 *    digitWidth="default"
468 *    direction="ltr"
469 *    dominantBaseline="auto"
470 *    dropShadowVisible="true"
471 *    fontFamily="Arial"
472 *    fontLookup="device"
473 *    fontSize="12"
474 *    fontStyle="normal"
475 *    fontWeight="normal"
476 *    justificationRule="auto"
477 *    justificationStyle="auto"
478 *    kerning="false"
479 *    ligatureLevel="common"
480 *    lineHeight="120%"
481 *    lineThrough="false%"
482 *    locale="en"
483 *    renderingMode="cff"
484 *    textAlign="start"
485 *    textAlignLast="start"
486 *    textAlpha="1"
487 *    textDecoration="start"
488 *    textJustify="interWord"
489 *    trackingLeft="0"
490 *    trackingRight="00"
491 *    typographicCase="default"
492 *  /&gt;
493 *  </pre>
494 *
495 *  @includeExample examples/VideoPlayerExample.mxml
496 *
497 *  @langversion 3.0
498 *  @playerversion Flash 10
499 *  @playerversion AIR 1.5
500 *  @productversion Flex 4
501 */
502public class VideoPlayer extends SkinnableComponent
503{
504    include "../core/Version.as";
505
506    //--------------------------------------------------------------------------
507    //
508    //  Class constants
509    //
510    //--------------------------------------------------------------------------
511
512    /**
513     *  @private
514     */
515    private static const AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG:uint = 1 << 0;
516
517    /**
518     *  @private
519     */
520    private static const AUTO_PLAY_PROPERTY_FLAG:uint = 1 << 1;
521
522    /**
523     *  @private
524     */
525    private static const AUTO_REWIND_PROPERTY_FLAG:uint = 1 << 2;
526
527    /**
528     *  @private
529     */
530    private static const LOOP_PROPERTY_FLAG:uint = 1 << 3;
531
532    /**
533     *  @private
534     */
535    private static const SCALE_MODE_PROPERTY_FLAG:uint = 1 << 4;
536
537    /**
538     *  @private
539     */
540    private static const MUTED_PROPERTY_FLAG:uint = 1 << 5;
541
542    /**
543     *  @private
544     */
545    private static const SOURCE_PROPERTY_FLAG:uint = 1 << 6;
546
547    /**
548     *  @private
549     */
550    private static const VOLUME_PROPERTY_FLAG:uint = 1 << 7;
551
552    /**
553     *  @private
554     */
555    private static const PAUSE_WHEN_HIDDEN_PROPERTY_FLAG:uint = 1 << 8;
556
557    /**
558     *  @private
559     */
560    private static const THUMBNAIL_SOURCE_PROPERTY_FLAG:uint = 1 << 9;
561
562
563    //--------------------------------------------------------------------------
564    //
565    //  Class properties
566    //
567    //--------------------------------------------------------------------------
568
569    /**
570     * @private
571     */
572    private static var _screenClass:Class;
573
574    /**
575     * @private
576     */
577    private static var checkedForScreenClass:Boolean;
578
579    /**
580     *  @private
581     */
582    private static function get screenClass():Class
583    {
584        if (!checkedForScreenClass)
585        {
586            checkedForScreenClass = true;
587
588            if (ApplicationDomain.currentDomain.
589                hasDefinition("flash.display::Screen"))
590            {
591                _screenClass = Class(ApplicationDomain.currentDomain.
592                    getDefinition("flash.display::Screen"));
593            }
594        }
595
596        return _screenClass;
597    }
598
599    //--------------------------------------------------------------------------
600    //
601    //  Class mixins
602    //
603    //--------------------------------------------------------------------------
604
605    /**
606     *  @private
607     *  Placeholder for mixin by VideoPlayerAccImpl.
608     */
609    mx_internal static var createAccessibilityImplementation:Function;
610
611    //--------------------------------------------------------------------------
612    //
613    //  Constructor
614    //
615    //--------------------------------------------------------------------------
616
617    /**
618     *  Constructor.
619     *
620     *  @langversion 3.0
621     *  @playerversion Flash 10
622     *  @playerversion AIR 1.5
623     *  @productversion Flex 4
624     */
625    public function VideoPlayer()
626    {
627        super();
628    }
629
630    //--------------------------------------------------------------------------
631    //
632    //  Skin Parts
633    //
634    //--------------------------------------------------------------------------
635
636    [SkinPart(required="true")]
637
638    /**
639     *  A required skin part that defines the VideoDisplay.
640     *
641     *  @langversion 3.0
642     *  @playerversion Flash 10
643     *  @playerversion AIR 1.5
644     *  @productversion Flex 4
645     */
646    public var videoDisplay:VideoDisplay;
647
648    [SkinPart(required="false")]
649
650    /**
651     *  An optional skin part to display the current value of <code>codecurrentTime</code>.
652     *
653     *  @langversion 3.0
654     *  @playerversion Flash 10
655     *  @playerversion AIR 1.5
656     *  @productversion Flex 4
657     */
658    public var currentTimeDisplay:IDisplayText;
659
660    [SkinPart(required="false")]
661
662    /**
663     *  An optional skin part for a button to toggle fullscreen mode.
664     *
665     *  @langversion 3.0
666     *  @playerversion Flash 10
667     *  @playerversion AIR 1.5
668     *  @productversion Flex 4
669     */
670    public var fullScreenButton:ButtonBase;
671
672    [SkinPart(required="false")]
673
674    /**
675     *  An optional skin part for the mute button.  The mute
676     *  button has both a <code>muted</code> property and a
677     *  <code>volume</code> property.
678     *
679     *  @langversion 3.0
680     *  @playerversion Flash 10
681     *  @playerversion AIR 1.5
682     *  @productversion Flex 4
683     */
684    public var muteButton:MuteButton;
685
686    [SkinPart(required="false")]
687
688    /**
689     *  An optional skin part for the pause button.
690     *
691     *  @langversion 3.0
692     *  @playerversion Flash 10
693     *  @playerversion AIR 1.5
694     *  @productversion Flex 4
695     */
696    public var pauseButton:ButtonBase;
697
698    [SkinPart(required="false")]
699
700    /**
701     *  An optional skin part for the play button.
702     *
703     *  @langversion 3.0
704     *  @playerversion Flash 10
705     *  @playerversion AIR 1.5
706     *  @productversion Flex 4
707     */
708    public var playButton:ButtonBase;
709
710    [SkinPart(required="false")]
711
712    /**
713     *  An optional skin part for all of the player controls.
714     *  This skin is used to determine what to hide when the player is in full screen
715     *  mode and there has been no user interaction.
716     *
717     *  @langversion 3.0
718     *  @playerversion Flash 10
719     *  @playerversion AIR 1.5
720     *  @productversion Flex 4
721     */
722    public var playerControls:DisplayObject;
723
724    [SkinPart(required="false")]
725
726    /**
727     *  An optional skin part for a play/pause button.  When the
728     *  video is playing, the <code>selected</code> property is set to
729     *  <code>true</code>.  When the video is paused or stopped,
730     *  the <code>selected</code> property is set to <code>false</code>.
731     *
732     *  @langversion 3.0
733     *  @playerversion Flash 10
734     *  @playerversion AIR 1.5
735     *  @productversion Flex 4
736     */
737    public var playPauseButton:ToggleButtonBase;
738
739    [SkinPart(required="false")]
740
741    /**
742     *  An optional skin part for the scrub bar (the
743     *  timeline).
744     *
745     *  @langversion 3.0
746     *  @playerversion Flash 10
747     *  @playerversion AIR 1.5
748     *  @productversion Flex 4
749     */
750    public var scrubBar:ScrubBar;
751
752    [SkinPart(required="false")]
753
754    /**
755     *  An optional skin part for the stop button.
756     *
757     *  @langversion 3.0
758     *  @playerversion Flash 10
759     *  @playerversion AIR 1.5
760     *  @productversion Flex 4
761     */
762    public var stopButton:ButtonBase;
763
764    [SkinPart(required="false")]
765
766    /**
767     *  An optional skin part to display the duration.
768     *
769     *  @langversion 3.0
770     *  @playerversion Flash 10
771     *  @playerversion AIR 1.5
772     *  @productversion Flex 4
773     */
774    public var durationDisplay:IDisplayText;
775
776    [SkinPart(required="false")]
777
778    /**
779     *  An optional skin part for the volume control.
780     *
781     *  @langversion 3.0
782     *  @playerversion Flash 10
783     *  @playerversion AIR 1.5
784     *  @productversion Flex 4
785     */
786    public var volumeBar:VolumeBar;
787
788    //--------------------------------------------------------------------------
789    //
790    //  Variables
791    //
792    //--------------------------------------------------------------------------
793
794    /**
795     *  @private
796     *  Several properties are proxied to videoDisplay.  However, when videoDisplay
797     *  is not around, we need to store values set on VideoPlayer.  This object
798     *  stores those values.  If videoDisplay is around, the values are stored
799     *  on the videoDisplay directly.  However, we need to know what values
800     *  have been set by the developer on the VideoPlayer (versus set on
801     *  the videoDisplay or defaults of the videoDisplay) as those are values
802     *  we want to carry around if the videoDisplay changes (via a new skin).
803     *  In order to store this info effeciently, videoDisplayProperties becomes
804     *  a uint to store a series of BitFlags.  These bits represent whether a
805     *  property has been explicitely set on this VideoPlayer.  When the
806     *  contentGroup is not around, videoDisplayProperties is a typeless
807     *  object to store these proxied properties.  When videoDisplay is around,
808     *  videoDisplayProperties stores booleans as to whether these properties
809     *  have been explicitely set or not.
810     */
811    private var videoDisplayProperties:Object = {};
812
813    /**
814     *  @private
815     *  The value of the pauseWhenHidden property before exiting
816     *  fullScreen.  We need to store it away here so we can
817     *  restore it at commitProperties() time because of an AIR
818     *  Mac bug.
819     */
820    private var exitingFullScreenPauseWhenHidden:Boolean;
821
822    /**
823     *  @private
824     *  Whether the pauseWhenHidden property needs to be updated.
825     */
826    private var needsToUpdatePauseWhenHidden:Boolean = false;
827
828    //--------------------------------------------------------------------------
829    //
830    //  Properties
831    //
832    //--------------------------------------------------------------------------
833
834    //----------------------------------
835    //  autoDisplayFirstFrame
836    //----------------------------------
837
838    [Inspectable(category="General", defaultValue="true")]
839
840    /**
841     *  @copy spark.components.VideoDisplay#autoDisplayFirstFrame
842     *
843     *  @default true
844     *
845     *  @langversion 3.0
846     *  @playerversion Flash 10
847     *  @playerversion AIR 1.5
848     *  @productversion Flex 4
849     */
850    public function get autoDisplayFirstFrame():Boolean
851    {
852        if (videoDisplay)
853        {
854            return videoDisplay.autoDisplayFirstFrame;
855        }
856        else
857        {
858            var v:* = videoDisplayProperties.autoDisplayFirstFrame;
859            return (v === undefined) ? true : v;
860        }
861    }
862
863    /**
864     * @private
865     */
866    public function set autoDisplayFirstFrame(value:Boolean):void
867    {
868        if (videoDisplay)
869        {
870            videoDisplay.autoDisplayFirstFrame = value;
871            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
872                AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG, true);
873        }
874        else
875        {
876            videoDisplayProperties.autoDisplayFirstFrame = value;
877        }
878    }
879
880    //----------------------------------
881    //  autoPlay
882    //----------------------------------
883
884    [Inspectable(category="General", defaultValue="true")]
885
886    /**
887     *  @copy spark.components.VideoDisplay#autoPlay
888     *
889     *  @default true
890     *
891     *  @langversion 3.0
892     *  @playerversion Flash 10
893     *  @playerversion AIR 1.5
894     *  @productversion Flex 4
895     */
896    public function get autoPlay():Boolean
897    {
898        if (videoDisplay)
899        {
900            return videoDisplay.autoPlay;
901        }
902        else
903        {
904            var v:* = videoDisplayProperties.autoPlay;
905            return (v === undefined) ? true : v;
906        }
907    }
908
909    /**
910     * @private
911     */
912    public function set autoPlay(value:Boolean):void
913    {
914        if (videoDisplay)
915        {
916            videoDisplay.autoPlay = value;
917            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
918                AUTO_PLAY_PROPERTY_FLAG, true);
919        }
920        else
921        {
922            videoDisplayProperties.autoPlay = value;
923        }
924    }
925
926    //----------------------------------
927    //  autoRewind
928    //----------------------------------
929
930    [Inspectable(category="General", defaultValue="true")]
931
932    /**
933     *  @copy spark.components.VideoDisplay#autoRewind
934     *
935     *  @default true
936     *
937     *  @langversion 3.0
938     *  @playerversion Flash 10
939     *  @playerversion AIR 1.5
940     *  @productversion Flex 4
941     */
942    public function get autoRewind():Boolean
943    {
944        if (videoDisplay)
945        {
946            return videoDisplay.autoRewind;
947        }
948        else
949        {
950            var v:* = videoDisplayProperties.autoRewind;
951            return (v === undefined) ? true : v;
952        }
953    }
954
955    /**
956     * @private
957     */
958    public function set autoRewind(value:Boolean):void
959    {
960        if (videoDisplay)
961        {
962            videoDisplay.autoRewind = value;
963            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
964                AUTO_REWIND_PROPERTY_FLAG, true);
965        }
966        else
967        {
968            videoDisplayProperties.autoRewind = value;
969        }
970    }
971
972    //----------------------------------
973    //  bytesLoaded
974    //----------------------------------
975
976    [Inspectable(Category="General", defaultValue="0")]
977    [Bindable("bytesLoadedChange")]
978    [Bindable("mediaPlayerStateChange")]
979
980    /**
981     *  @copy spark.components.VideoDisplay#bytesLoaded
982     *
983     *  @default 0
984     *
985     *  @langversion 3.0
986     *  @playerversion Flash 10
987     *  @playerversion AIR 1.5
988     *  @productversion Flex 4
989     */
990    public function get bytesLoaded():Number
991    {
992        if (videoDisplay)
993            return videoDisplay.bytesLoaded;
994        else
995            return 0;
996    }
997
998    //----------------------------------
999    //  bytesTotal
1000    //----------------------------------
1001
1002    [Inspectable(Category="General", defaultValue="0")]
1003    [Bindable("mediaPlayerStateChange")]
1004
1005    /**
1006     *  @copy spark.components.VideoDisplay#bytesTotal
1007     *
1008     *  @default 0
1009     *
1010     *  @langversion 3.0
1011     *  @playerversion Flash 10
1012     *  @playerversion AIR 1.5
1013     *  @productversion Flex 4
1014     */
1015    public function get bytesTotal():Number
1016    {
1017        if (videoDisplay)
1018            return videoDisplay.bytesTotal;
1019        else
1020            return 0;
1021    }
1022
1023    //----------------------------------
1024    //  currentTime
1025    //----------------------------------
1026
1027    [Inspectable(Category="General", defaultValue="0")]
1028    [Bindable("currentTimeChange")]
1029    [Bindable("mediaPlayerStateChange")]
1030
1031    /**
1032     *  @copy spark.components.VideoDisplay#currentTime
1033     *
1034     *  @default 0
1035     *
1036     *  @langversion 3.0
1037     *  @playerversion Flash 10
1038     *  @playerversion AIR 1.5
1039     *  @productversion Flex 4
1040     */
1041    public function get currentTime():Number
1042    {
1043        if (videoDisplay)
1044            return videoDisplay.currentTime;
1045        else
1046            return 0;
1047    }
1048
1049    //----------------------------------
1050    //  duration
1051    //----------------------------------
1052
1053    [Inspectable(Category="General", defaultValue="0")]
1054    [Bindable("durationChange")]
1055    [Bindable("mediaPlayerStateChange")]
1056
1057    /**
1058     *  @copy spark.components.VideoDisplay#duration
1059     *
1060     *  @default 0
1061     *
1062     *  @langversion 3.0
1063     *  @playerversion Flash 10
1064     *  @playerversion AIR 1.5
1065     *  @productversion Flex 4
1066     */
1067    public function get duration():Number
1068    {
1069        if (videoDisplay)
1070            return videoDisplay.duration;
1071        else
1072            return 0;
1073    }
1074
1075    //----------------------------------
1076    //  loop
1077    //----------------------------------
1078
1079    [Inspectable(Category="General", defaultValue="false")]
1080
1081    /**
1082     *  @copy spark.components.VideoDisplay#loop
1083     *
1084     *  @default false
1085     *
1086     *  @langversion 3.0
1087     *  @playerversion Flash 10
1088     *  @playerversion AIR 1.5
1089     *  @productversion Flex 4
1090     */
1091    public function get loop():Boolean
1092    {
1093        if (videoDisplay)
1094        {
1095            return videoDisplay.loop;
1096        }
1097        else
1098        {
1099            var v:* = videoDisplayProperties.loop;
1100            return (v === undefined) ? false : v;
1101        }
1102    }
1103
1104    /**
1105     *  @private
1106     */
1107    public function set loop(value:Boolean):void
1108    {
1109        if (videoDisplay)
1110        {
1111            videoDisplay.loop = value;
1112            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1113                LOOP_PROPERTY_FLAG, true);
1114        }
1115        else
1116        {
1117            videoDisplayProperties.loop = value;
1118        }
1119    }
1120
1121    //----------------------------------
1122    //  mediaPlayerState
1123    //----------------------------------
1124
1125    [Inspectable(category="General", defaultValue="uninitialized")]
1126    [Bindable("mediaPlayerStateChange")]
1127
1128    /**
1129     *  @copy spark.components.VideoDisplay#mediaPlayerState
1130     *
1131     *  @default uninitialized
1132     *
1133     *  @see org.osmf.media.MediaPlayerState
1134     *
1135     *  @langversion 3.0
1136     *  @playerversion Flash 10
1137     *  @playerversion AIR 1.5
1138     *  @productversion Flex 4
1139     */
1140    public function get mediaPlayerState():String
1141    {
1142        if (videoDisplay)
1143            return videoDisplay.mediaPlayerState;
1144        else
1145            return MediaPlayerState.UNINITIALIZED;
1146    }
1147
1148    //----------------------------------
1149    //  muted
1150    //----------------------------------
1151
1152    [Inspectable(category="General", defaultValue="false")]
1153    [Bindable("volumeChanged")]
1154
1155    /**
1156     *  @copy spark.components.VideoDisplay#muted
1157     *
1158     *  @default false
1159     *
1160     *  @langversion 3.0
1161     *  @playerversion Flash 10
1162     *  @playerversion AIR 1.5
1163     *  @productversion Flex 4
1164     */
1165    public function get muted():Boolean
1166    {
1167        if (videoDisplay)
1168        {
1169            return videoDisplay.muted;
1170        }
1171        else
1172        {
1173            var v:* = videoDisplayProperties.muted;
1174            return (v === undefined) ? false : v;
1175        }
1176    }
1177
1178    /**
1179     *  @private
1180     */
1181    public function set muted(value:Boolean):void
1182    {
1183        if (videoDisplay)
1184        {
1185            videoDisplay.muted = value;
1186            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1187                MUTED_PROPERTY_FLAG, true);
1188        }
1189        else
1190        {
1191            videoDisplayProperties.muted = value;
1192        }
1193
1194        if (volumeBar)
1195            volumeBar.muted = value;
1196        if (muteButton)
1197            muteButton.muted = value;
1198    }
1199
1200    //----------------------------------
1201    //  pauseWhenHidden
1202    //----------------------------------
1203
1204    [Inspectable(category="General", defaultValue="true")]
1205
1206    /**
1207     *  @copy spark.components.VideoDisplay#pauseWhenHidden
1208     *
1209     *  @default true
1210     *
1211     *  @langversion 3.0
1212     *  @playerversion Flash 10
1213     *  @playerversion AIR 1.5
1214     *  @productversion Flex 4
1215     */
1216    public function get pauseWhenHidden():Boolean
1217    {
1218        if (needsToUpdatePauseWhenHidden)
1219        {
1220            return exitingFullScreenPauseWhenHidden;
1221        }
1222        else if (videoDisplay)
1223        {
1224            return videoDisplay.pauseWhenHidden;
1225        }
1226        else
1227        {
1228            var v:* = videoDisplayProperties.pauseWhenHidden;
1229            return (v === undefined) ? false : v;
1230        }
1231    }
1232
1233    /**
1234     *  @private
1235     */
1236    public function set pauseWhenHidden(value:Boolean):void
1237    {
1238        if (needsToUpdatePauseWhenHidden)
1239        {
1240            exitingFullScreenPauseWhenHidden = value;
1241        }
1242        else if (videoDisplay)
1243        {
1244            videoDisplay.pauseWhenHidden = value;
1245            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1246                PAUSE_WHEN_HIDDEN_PROPERTY_FLAG, true);
1247        }
1248        else
1249        {
1250            videoDisplayProperties.pauseWhenHidden = value;
1251        }
1252    }
1253
1254    //----------------------------------
1255    //  playing
1256    //----------------------------------
1257
1258    [Inspectable(category="General")]
1259    [Bindable("mediaPlayerStateChange")]
1260
1261    /**
1262     *  @copy spark.components.VideoDisplay#playing
1263     *
1264     *  @langversion 3.0
1265     *  @playerversion Flash 10
1266     *  @playerversion AIR 1.5
1267     *  @productversion Flex 4
1268     */
1269    public function get playing():Boolean
1270    {
1271        if (videoDisplay)
1272            return videoDisplay.playing;
1273        else
1274            return false;
1275    }
1276
1277    //----------------------------------
1278    //  scaleMode
1279    //----------------------------------
1280
1281    [Inspectable(Category="General", enumeration="none,stretch,letterbox,zoom", defaultValue="letterbox")]
1282
1283    /**
1284     *  @copy spark.components.VideoDisplay#scaleMode
1285     *
1286     *  @default "letterbox"
1287     *
1288     *  @see org.osmf.display.ScaleMode
1289     *
1290     *  @langversion 3.0
1291     *  @playerversion Flash 10
1292     *  @playerversion AIR 1.5
1293     *  @productversion Flex 4
1294     */
1295    public function get scaleMode():String
1296    {
1297        if (videoDisplay)
1298        {
1299            return videoDisplay.scaleMode;
1300        }
1301        else
1302        {
1303            var v:* = videoDisplayProperties.scaleMode;
1304            return (v === undefined) ? "letterbox" : v;
1305        }
1306    }
1307
1308    /**
1309     *  @private
1310     */
1311    public function set scaleMode(value:String):void
1312    {
1313        if (videoDisplay)
1314        {
1315            videoDisplay.scaleMode = value;
1316            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1317                SCALE_MODE_PROPERTY_FLAG, true);
1318        }
1319        else
1320        {
1321            videoDisplayProperties.scaleMode = value;
1322        }
1323    }
1324
1325    //----------------------------------
1326    //  source
1327    //----------------------------------
1328
1329    [Inspectable(category="General", defaultValue="null")]
1330    [Bindable("sourceChanged")]
1331
1332    /**
1333     *  @copy spark.components.VideoDisplay#source
1334     *
1335     *  @default null
1336     *
1337     *  @langversion 3.0
1338     *  @playerversion Flash 10
1339     *  @playerversion AIR 1.5
1340     *  @productversion Flex 4
1341     */
1342    public function get source():Object
1343    {
1344        if (videoDisplay)
1345        {
1346            return videoDisplay.source;
1347        }
1348        else
1349        {
1350            var v:* = videoDisplayProperties.source;
1351            return (v === undefined) ? null : v;
1352        }
1353    }
1354
1355    /**
1356     * @private
1357     */
1358    public function set source(value:Object):void
1359    {
1360        if (videoDisplay)
1361        {
1362            videoDisplay.source = value;
1363            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1364                SOURCE_PROPERTY_FLAG, true);
1365        }
1366        else
1367        {
1368            videoDisplayProperties.source = value;
1369        }
1370    }
1371
1372    //----------------------------------
1373    //  thumbnailSource
1374    //----------------------------------
1375
1376    [Inspectable(category="General")]
1377
1378    /**
1379     *  @private
1380     *  @copy spark.components.VideoDisplay#thumbnailSource
1381     *
1382     *  @langversion 3.0
1383     *  @playerversion Flash 10
1384     *  @playerversion AIR 1.5
1385     *  @productversion Flex 4
1386     */
1387    mx_internal function get thumbnailSource():Object
1388    {
1389        if (videoDisplay)
1390        {
1391            return videoDisplay.thumbnailSource;
1392        }
1393        else
1394        {
1395            var v:* = videoDisplayProperties.thumbnailSource;
1396            return (v === undefined) ? null : v;
1397        }
1398    }
1399
1400    /**
1401     * @private
1402     */
1403    mx_internal function set thumbnailSource(value:Object):void
1404    {
1405        if (videoDisplay)
1406        {
1407            videoDisplay.thumbnailSource = value;
1408            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1409                THUMBNAIL_SOURCE_PROPERTY_FLAG, true);
1410        }
1411        else
1412        {
1413            videoDisplayProperties.thumbnailSource = value;
1414        }
1415    }
1416
1417    //----------------------------------
1418    //  videoObject
1419    //----------------------------------
1420
1421    [Inspectable(category="General", defaultValue="null")]
1422
1423    /**
1424     *  @copy spark.components.VideoDisplay#videoObject
1425     *
1426     *  @default null
1427     *
1428     *  @langversion 3.0
1429     *  @playerversion Flash 10
1430     *  @playerversion AIR 1.5
1431     *  @productversion Flex 4
1432     */
1433    public function get videoObject():Video
1434    {
1435        if (videoDisplay)
1436            return videoDisplay.videoObject;
1437        else
1438            return null;
1439    }
1440
1441    //----------------------------------
1442    //  volume
1443    //----------------------------------
1444
1445    [Inspectable(category="General", defaultValue="1.0", minValue="0.0", maxValue="1.0")]
1446    [Bindable("volumeChanged")]
1447
1448    /**
1449     *  @copy spark.components.VideoDisplay#volume
1450     *
1451     *  @default 1
1452     *
1453     *  @langversion 3.0
1454     *  @playerversion Flash 10
1455     *  @playerversion AIR 1.5
1456     *  @productversion Flex 4
1457     */
1458    public function get volume():Number
1459    {
1460        if (videoDisplay)
1461        {
1462            return videoDisplay.volume;
1463        }
1464        else
1465        {
1466            var v:* = videoDisplayProperties.volume;
1467            return (v === undefined) ? 1 : v;
1468        }
1469    }
1470
1471    /**
1472     * @private
1473     */
1474    public function set volume(value:Number):void
1475    {
1476        if (videoDisplay)
1477        {
1478            videoDisplay.volume = value;
1479            videoDisplayProperties = BitFlagUtil.update(videoDisplayProperties as uint,
1480                VOLUME_PROPERTY_FLAG, true);
1481        }
1482        else
1483        {
1484            videoDisplayProperties.volume = value;
1485        }
1486
1487        if (volumeBar)
1488            volumeBar.value = value;
1489    }
1490
1491    //--------------------------------------------------------------------------
1492    //
1493    //  Overridden methods
1494    //
1495    //--------------------------------------------------------------------------
1496
1497    /**
1498     *  @private
1499     */
1500    override protected function initializeAccessibility():void
1501    {
1502        if (VideoPlayer.createAccessibilityImplementation != null)
1503            VideoPlayer.createAccessibilityImplementation(this);
1504    }
1505
1506    /**
1507     *  @private
1508     */
1509    override protected function getCurrentSkinState():String
1510    {
1511        if (!videoDisplay || !videoDisplay.videoPlayer)
1512            return null;
1513
1514        var state:String = videoDisplay.videoPlayer.state;
1515
1516        // now that we have our video player's current state (atleast the one we care about)
1517        // and that we've set the previous state to something we care about, let's figure
1518        // out our skin's state
1519
1520        if (!enabled)
1521            state="disabled"
1522
1523        if (fullScreen)
1524            return state + "AndFullScreen";
1525
1526        return state;
1527    }
1528
1529    /**
1530     *  @private
1531     */
1532    override protected function partAdded(partName:String, instance:Object):void
1533    {
1534        super.partAdded(partName, instance);
1535
1536        if (instance == videoDisplay)
1537        {
1538            videoDisplay.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoDisplay_currentTimeChangeHandler);
1539            videoDisplay.addEventListener(LoadEvent.BYTES_LOADED_CHANGE, videoDisplay_bytesLoadedChangeHandler);
1540            videoDisplay.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoDisplay_mediaPlayerStateChangeHandler);
1541            videoDisplay.addEventListener(TimeEvent.DURATION_CHANGE, videoDisplay_durationChangeHandler);
1542            videoDisplay.addEventListener(TimeEvent.COMPLETE, dispatchEvent);
1543
1544            // just strictly for binding purposes
1545            videoDisplay.addEventListener("sourceChanged", dispatchEvent);
1546            videoDisplay.addEventListener("volumeChanged", videoDisplay_volumeChangedHandler);
1547
1548            // copy proxied values from videoProperties (if set) to video
1549
1550            var newVideoProperties:uint = 0;
1551
1552            if (videoDisplayProperties.source !== undefined)
1553            {
1554                videoDisplay.source = videoDisplayProperties.source;
1555                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1556                    SOURCE_PROPERTY_FLAG, true);
1557            }
1558
1559            if (videoDisplayProperties.autoPlay !== undefined)
1560            {
1561                videoDisplay.autoPlay = videoDisplayProperties.autoPlay;
1562                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1563                    AUTO_PLAY_PROPERTY_FLAG, true);
1564            }
1565
1566            if (videoDisplayProperties.volume !== undefined)
1567            {
1568                videoDisplay.volume = videoDisplayProperties.volume;
1569                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1570                    VOLUME_PROPERTY_FLAG, true);
1571            }
1572
1573            if (videoDisplayProperties.autoRewind !== undefined)
1574            {
1575                videoDisplay.autoRewind = videoDisplayProperties.autoRewind;
1576                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1577                    AUTO_REWIND_PROPERTY_FLAG, true);
1578            }
1579
1580            if (videoDisplayProperties.loop !== undefined)
1581            {
1582                videoDisplay.loop = videoDisplayProperties.loop;
1583                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1584                    LOOP_PROPERTY_FLAG, true);
1585            }
1586
1587            if (videoDisplayProperties.scaleMode !== undefined)
1588            {
1589                videoDisplay.scaleMode = videoDisplayProperties.scaleMode;
1590                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1591                    SCALE_MODE_PROPERTY_FLAG, true);
1592            }
1593
1594            if (videoDisplayProperties.muted !== undefined)
1595            {
1596                videoDisplay.muted = videoDisplayProperties.muted;
1597                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1598                    MUTED_PROPERTY_FLAG, true);
1599            }
1600
1601            if (videoDisplayProperties.pauseWhenHidden !== undefined)
1602            {
1603                videoDisplay.pauseWhenHidden = videoDisplayProperties.pauseWhenHidden;
1604                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1605                    PAUSE_WHEN_HIDDEN_PROPERTY_FLAG, true);
1606            }
1607
1608            if (videoDisplayProperties.autoDisplayFirstFrame !== undefined)
1609            {
1610                videoDisplay.autoDisplayFirstFrame = videoDisplayProperties.autoDisplayFirstFrame;
1611                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1612                    AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG, true);
1613            }
1614
1615            if (videoDisplayProperties.thumbnailSource !== undefined)
1616            {
1617                videoDisplay.thumbnailSource = videoDisplayProperties.thumbnailSource;
1618                newVideoProperties = BitFlagUtil.update(newVideoProperties as uint,
1619                    THUMBNAIL_SOURCE_PROPERTY_FLAG, true);
1620            }
1621
1622            // these are state properties just carried over from an old video element
1623            if (videoDisplayProperties.currentTime !== undefined ||
1624                videoDisplayProperties.playing !== undefined)
1625            {
1626                videoDisplay_updateCompleteHandlerProperties = {
1627                    autoPlay: videoDisplay.autoPlay,
1628                        playing: videoDisplayProperties.playing,
1629                        currentTime: videoDisplayProperties.currentTime};
1630
1631                // so the videoDisplay doesn't start playing...we'll handle it instead in
1632                // videoDisplay_updateCompleteHandler
1633                videoDisplay.autoPlay = false;
1634
1635                videoDisplay.addEventListener(FlexEvent.UPDATE_COMPLETE, videoDisplay_updateCompleteHandler);
1636            }
1637
1638            videoDisplayProperties = newVideoProperties;
1639
1640            if (volumeBar)
1641            {
1642                volumeBar.value = videoDisplay.volume;
1643                volumeBar.muted = videoDisplay.muted;
1644            }
1645
1646            if (muteButton)
1647            {
1648                muteButton.volume = videoDisplay.volume;
1649                muteButton.muted = videoDisplay.muted;
1650            }
1651
1652            if (scrubBar)
1653                updateScrubBar();
1654
1655            if (currentTimeDisplay)
1656                updateCurrentTime();
1657
1658            if (durationDisplay)
1659                updateDuration();
1660        }
1661        else if (instance == playButton)
1662        {
1663            playButton.addEventListener(MouseEvent.CLICK, playButton_clickHandler);
1664        }
1665        else if (instance == pauseButton)
1666        {
1667            pauseButton.addEventListener(MouseEvent.CLICK, pauseButton_clickHandler);
1668        }
1669        else if (instance == playPauseButton)
1670        {
1671            playPauseButton.addEventListener(MouseEvent.CLICK, playPauseButton_clickHandler);
1672        }
1673        else if (instance == stopButton)
1674        {
1675            stopButton.addEventListener(MouseEvent.CLICK, stopButton_clickHandler);
1676        }
1677        else if (instance == muteButton)
1678        {
1679            if (videoDisplay)
1680            {
1681                muteButton.muted = muted;
1682                muteButton.volume = volume;
1683            }
1684
1685            muteButton.addEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
1686        }
1687        else if (instance == volumeBar)
1688        {
1689            volumeBar.minimum = 0;
1690            volumeBar.maximum = 1;
1691            if (videoDisplay)
1692            {
1693                volumeBar.value = volume;
1694                volumeBar.muted = muted;
1695            }
1696
1697            volumeBar.addEventListener(Event.CHANGE, volumeBar_changeHandler);
1698            volumeBar.addEventListener(FlexEvent.MUTED_CHANGE, volumeBar_mutedChangeHandler);
1699        }
1700        else if (instance == scrubBar)
1701        {
1702            if (videoDisplay)
1703                updateScrubBar();
1704
1705            // add thumbPress and thumbRelease so we pause the video while dragging
1706            scrubBar.addEventListener(TrackBaseEvent.THUMB_PRESS, scrubBar_thumbPressHandler);
1707            scrubBar.addEventListener(TrackBaseEvent.THUMB_RELEASE, scrubBar_thumbReleaseHandler);
1708
1709            // add change to actually seek() when the change is complete
1710            scrubBar.addEventListener(Event.CHANGE, scrubBar_changeHandler);
1711
1712            // add changeEnd and changeStart so we don't update the scrubbar's value
1713            // while the scrubbar is moving around due to an animation
1714            scrubBar.addEventListener(FlexEvent.CHANGE_END, scrubBar_changeEndHandler);
1715            scrubBar.addEventListener(FlexEvent.CHANGE_START, scrubBar_changeStartHandler);
1716        }
1717        else if (instance == fullScreenButton)
1718        {
1719            fullScreenButton.addEventListener(MouseEvent.CLICK, fullScreenButton_clickHandler);
1720        }
1721        else if (instance == currentTimeDisplay)
1722        {
1723            if (videoDisplay)
1724                updateCurrentTime();
1725        }
1726        else if (instance == durationDisplay)
1727        {
1728            if (videoDisplay)
1729                updateDuration();
1730        }
1731    }
1732
1733    /**
1734     *  @private
1735     *  Holds the state of the video element when the skin is being swapped out.
1736     *  This is so the new videoDisplay can load up and start playing
1737     *  where it left off.
1738     */
1739    private var videoDisplay_updateCompleteHandlerProperties:Object;
1740
1741    /**
1742     *  @private
1743     *  We only listen for the updateComplete event on the videoDisplay when
1744     *  a skin has been swapped.  This is so we can push the old videoDisplay's
1745     *  state in to the new object, and we can start playing the video
1746     *  where it left off.
1747     */
1748    private function videoDisplay_updateCompleteHandler(event:FlexEvent):void
1749    {
1750        if (videoDisplay_updateCompleteHandlerProperties.autoPlay)
1751            videoDisplay.autoPlay = true;
1752
1753        if (videoDisplay_updateCompleteHandlerProperties.currentTime !== undefined)
1754            videoDisplay.seek(videoDisplay_updateCompleteHandlerProperties.currentTime);
1755
1756        if (videoDisplay_updateCompleteHandlerProperties.playing)
1757            videoDisplay.play();
1758
1759        videoDisplay_updateCompleteHandlerProperties = null;
1760        videoDisplay.removeEventListener(FlexEvent.UPDATE_COMPLETE, videoDisplay_updateCompleteHandler);
1761    }
1762
1763    /**
1764     *  @private
1765     */
1766    override protected function partRemoved(partName:String, instance:Object):void
1767    {
1768        super.partRemoved(partName, instance);
1769
1770        if (instance == videoDisplay)
1771        {
1772            // validate before doing anything with the videoDisplay.
1773            // This is so if the video element hasn't been validated, it won't start playing.
1774            // plus this way we'll get a valid currentTime and all those other properties
1775            // we are interested in.
1776            videoDisplay.validateNow();
1777
1778            // copy proxied values from video (if explicitely set) to videoProperties
1779
1780            var newVideoProperties:Object = {};
1781
1782            if (BitFlagUtil.isSet(videoDisplayProperties as uint, SOURCE_PROPERTY_FLAG))
1783                newVideoProperties.source = videoDisplay.source;
1784
1785            if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_PLAY_PROPERTY_FLAG))
1786                newVideoProperties.autoPlay = videoDisplay.autoPlay;
1787
1788            if (BitFlagUtil.isSet(videoDisplayProperties as uint, VOLUME_PROPERTY_FLAG))
1789                newVideoProperties.volume = videoDisplay.volume;
1790
1791            if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_REWIND_PROPERTY_FLAG))
1792                newVideoProperties.autoRewind = videoDisplay.autoRewind;
1793
1794            if (BitFlagUtil.isSet(videoDisplayProperties as uint, LOOP_PROPERTY_FLAG))
1795                newVideoProperties.loop = videoDisplay.loop;
1796
1797            if (BitFlagUtil.isSet(videoDisplayProperties as uint, SCALE_MODE_PROPERTY_FLAG))
1798                newVideoProperties.scaleMode = videoDisplay.scaleMode;
1799
1800            if (BitFlagUtil.isSet(videoDisplayProperties as uint, MUTED_PROPERTY_FLAG))
1801                newVideoProperties.muted = videoDisplay.muted;
1802
1803            if (BitFlagUtil.isSet(videoDisplayProperties as uint, PAUSE_WHEN_HIDDEN_PROPERTY_FLAG))
1804                newVideoProperties.pauseWhenHidden = videoDisplay.pauseWhenHidden;
1805
1806            if (BitFlagUtil.isSet(videoDisplayProperties as uint, AUTO_DISPLAY_FIRST_FRAME_PROPERTY_FLAG))
1807                newVideoProperties.autoDisplayFirstFrame = videoDisplay.autoDisplayFirstFrame;
1808
1809            if (BitFlagUtil.isSet(videoDisplayProperties as uint, THUMBNAIL_SOURCE_PROPERTY_FLAG))
1810                newVideoProperties.thumbnailSource = videoDisplay.thumbnailSource;
1811
1812            // push our current state (where we were in the video and whether we were playing)
1813            // so that the new skin gets these as well
1814            newVideoProperties.currentTime = videoDisplay.currentTime;
1815            newVideoProperties.playing = videoDisplay.playing;
1816
1817            videoDisplay.stop();
1818
1819            videoDisplayProperties = newVideoProperties;
1820
1821            videoDisplay.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoDisplay_currentTimeChangeHandler);
1822            videoDisplay.removeEventListener(LoadEvent.BYTES_LOADED_CHANGE, videoDisplay_bytesLoadedChangeHandler);
1823            videoDisplay.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoDisplay_mediaPlayerStateChangeHandler);
1824            videoDisplay.removeEventListener(TimeEvent.DURATION_CHANGE, videoDisplay_durationChangeHandler);
1825            videoDisplay.removeEventListener(TimeEvent.COMPLETE, dispatchEvent);
1826
1827            // just strictly for binding purposes
1828            videoDisplay.removeEventListener("sourceChanged", dispatchEvent);
1829            videoDisplay.removeEventListener("volumeChanged", videoDisplay_volumeChangedHandler);
1830        }
1831        else if (instance == playButton)
1832        {
1833            playButton.removeEventListener(MouseEvent.CLICK, playButton_clickHandler);
1834        }
1835        else if (instance == pauseButton)
1836        {
1837            pauseButton.removeEventListener(MouseEvent.CLICK, pauseButton_clickHandler);
1838        }
1839        else if (instance == playPauseButton)
1840        {
1841            playPauseButton.removeEventListener(MouseEvent.CLICK, playPauseButton_clickHandler);
1842        }
1843        else if (instance == stopButton)
1844        {
1845            stopButton.removeEventListener(MouseEvent.CLICK, stopButton_clickHandler);
1846        }
1847        else if (instance == muteButton)
1848        {
1849            playButton.removeEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
1850        }
1851        else if (instance == volumeBar)
1852        {
1853            volumeBar.removeEventListener(Event.CHANGE, volumeBar_changeHandler);
1854            volumeBar.removeEventListener(FlexEvent.MUTED_CHANGE, volumeBar_mutedChangeHandler);
1855        }
1856        else if (instance == scrubBar)
1857        {
1858            scrubBar.removeEventListener(TrackBaseEvent.THUMB_PRESS, scrubBar_thumbPressHandler);
1859            scrubBar.removeEventListener(TrackBaseEvent.THUMB_RELEASE, scrubBar_thumbReleaseHandler);
1860            scrubBar.removeEventListener(Event.CHANGE, scrubBar_changeHandler);
1861            scrubBar.removeEventListener(FlexEvent.CHANGE_END, scrubBar_changeEndHandler);
1862            scrubBar.removeEventListener(FlexEvent.CHANGE_START, scrubBar_changeStartHandler);
1863        }
1864        else if (instance == fullScreenButton)
1865        {
1866            fullScreenButton.removeEventListener(MouseEvent.CLICK, fullScreenButton_clickHandler);
1867        }
1868    }
1869
1870    /**
1871     *  @private
1872     */
1873    override protected function commitProperties():void
1874    {
1875        super.commitProperties();
1876
1877        // if coming from full screen mode, we reset the
1878        // pauseWhenHidden property here because of an AIR bug
1879        // that requires us to defer it.
1880        if (needsToUpdatePauseWhenHidden)
1881        {
1882            needsToUpdatePauseWhenHidden = false;
1883            pauseWhenHidden = exitingFullScreenPauseWhenHidden;
1884        }
1885    }
1886
1887    //--------------------------------------------------------------------------
1888    //
1889    //  Methods
1890    //
1891    //--------------------------------------------------------------------------
1892
1893    /**
1894     *  @throws TypeError If the skin hasn't been loaded and there is no videoDisplay.
1895     *
1896     *  @copy spark.components.VideoDisplay#pause()
1897     *
1898     *
1899     *  @langversion 3.0
1900     *  @playerversion Flash 10
1901     *  @playerversion AIR 1.5
1902     *  @productversion Flex 4
1903     */
1904    public function pause():void
1905    {
1906        videoDisplay.pause();
1907    }
1908
1909    /**
1910     *  @copy spark.components.VideoDisplay#play()
1911     *
1912     *  @throws TypeError if the skin hasn't been loaded up yet
1913     *                    and there's no videoDisplay.
1914     *
1915     *  @langversion 3.0
1916     *  @playerversion Flash 10
1917     *  @playerversion AIR 1.5
1918     *  @productversion Flex 4
1919     */
1920    public function play():void
1921    {
1922        //trace("play");
1923        videoDisplay.play();
1924    }
1925
1926    /**
1927     *  @copy spark.components.VideoDisplay#seek()
1928     *
1929     *  @throws TypeError if the skin hasn't been loaded up yet
1930     *                    and there's no videoDisplay.
1931     *
1932     *  @langversion 3.0
1933     *  @playerversion Flash 10
1934     *  @playerversion AIR 1.5
1935     *  @productversion Flex 4
1936     */
1937    public function seek(time:Number):void
1938    {
1939        videoDisplay.seek(time);
1940    }
1941
1942    /**
1943     *  @copy spark.components.VideoDisplay#stop()
1944     *
1945     *  @throws TypeError if the skin hasn't been loaded up yet
1946     *                    and there's no videoDisplay.
1947     *
1948     *  @langversion 3.0
1949     *  @playerversion Flash 10
1950     *  @playerversion AIR 1.5
1951     *  @productversion Flex 4
1952     */
1953    public function stop():void
1954    {
1955        videoDisplay.stop();
1956    }
1957
1958    /**
1959     *  @private
1960     */
1961    private function updateScrubBar():void
1962    {
1963        if (!videoDisplay)
1964            return;
1965
1966        if (!scrubBarMouseCaptured && !scrubBarChanging)
1967        {
1968            scrubBar.minimum = 0;
1969            scrubBar.maximum = videoDisplay.duration;
1970            scrubBar.value = videoDisplay.currentTime;
1971        }
1972
1973        // if streaming, then we pretend to have everything in view
1974        // if progressive, then look at the bytesLoaded and bytesTotal
1975        if (!videoDisplay.videoPlayer.canLoad)
1976            scrubBar.loadedRangeEnd = videoDisplay.duration;
1977        else if (videoDisplay.bytesTotal == 0)
1978            scrubBar.loadedRangeEnd = 0;
1979        else
1980            scrubBar.loadedRangeEnd = (videoDisplay.bytesLoaded/videoDisplay.bytesTotal)*videoDisplay.duration;
1981    }
1982
1983    /**
1984     *  @private
1985     */
1986    private function updateDuration():void
1987    {
1988        durationDisplay.text = formatTimeValue(duration);
1989    }
1990
1991    /**
1992     *  Formats a time value, specified in seconds, into a String that
1993     *  gets used for <code>currentTime</code> and the <code>duration</code>.
1994     *
1995     *  @param value Value in seconds of the time to format.
1996     *
1997     *  @return Formatted time value.
1998     *
1999     *  @langversion 3.0
2000     *  @playerversion Flash 10
2001     *  @playerversion AIR 2.5
2002     *  @productversion Flex 4.5
2003     */
2004    protected function formatTimeValue(value:Number):String
2005    {
2006        // default format: hours:minutes:seconds
2007        value = Math.round(value);
2008
2009        var hours:uint = Math.floor(value/3600) % 24;
2010        var minutes:uint = Math.floor(value/60) % 60;
2011        var seconds:uint = value % 60;
2012
2013        var result:String = "";
2014        if (hours != 0)
2015            result = hours + ":";
2016
2017        if (result && minutes < 10)
2018            result += "0" + minutes + ":";
2019        else
2020            result += minutes + ":";
2021
2022        if (seconds < 10)
2023            result += "0" + seconds;
2024        else
2025            result += seconds;
2026
2027        return result;
2028    }
2029
2030    /**
2031     *  @private
2032     */
2033    private function updateCurrentTime():void
2034    {
2035        currentTimeDisplay.text = formatTimeValue(currentTime);
2036    }
2037
2038    /**
2039     *  @private
2040     *  Returns the screen bounds.
2041     *  If we are on the AIR Player, we need to work around AIR Player bug #2503351
2042     *  We check if the flash.display.Screen class is defined. If so, then
2043     *  we are running on the AIR Player and can access this API.
2044     */
2045    mx_internal function getScreenBounds():Rectangle
2046    {
2047        var resultRect:Rectangle = new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight);
2048
2049        if (screenClass)
2050        {
2051            // Get the screen where the application resides
2052            try
2053            {
2054                var nativeWindowBounds:Rectangle = stage["nativeWindow"]["bounds"];
2055                var currentScreen:Object = screenClass["getScreensForRectangle"](nativeWindowBounds)[0];
2056
2057                // Return the bounds of that screen
2058                resultRect = currentScreen["bounds"];
2059            }
2060            catch (e:Error)
2061            {
2062            }
2063        }
2064
2065        return resultRect;
2066    }
2067
2068    //--------------------------------------------------------------------------
2069    //
2070    //  Event handlers
2071    //
2072    //--------------------------------------------------------------------------
2073
2074    /**
2075     *  @private
2076     */
2077    private function videoDisplay_currentTimeChangeHandler(event:TimeEvent):void
2078    {
2079        if (scrubBar)
2080            updateScrubBar();
2081
2082        if (currentTimeDisplay)
2083            updateCurrentTime();
2084
2085        dispatchEvent(event);
2086    }
2087
2088    /**
2089     *  @private
2090     */
2091    private function videoDisplay_bytesLoadedChangeHandler(event:LoadEvent):void
2092    {
2093        if (scrubBar)
2094            updateScrubBar();
2095
2096        dispatchEvent(event);
2097    }
2098
2099    /**
2100     *  @private
2101     */
2102    private function videoDisplay_mediaPlayerStateChangeHandler(event:MediaPlayerStateChangeEvent):void
2103    {
2104        invalidateSkinState();
2105
2106        if (scrubBar)
2107            updateScrubBar();
2108
2109        if (durationDisplay)
2110            updateDuration();
2111
2112        if (currentTimeDisplay)
2113            updateCurrentTime();
2114
2115        if (playPauseButton)
2116            playPauseButton.selected = playing;
2117
2118        //trace("mediaPlayerStateChangeHandler " + event + " state = " + event.state + " playing = " + playing);
2119
2120        dispatchEvent(event);
2121    }
2122
2123    /**
2124     *  @private
2125     */
2126    private function videoDisplay_durationChangeHandler(event:TimeEvent):void
2127    {
2128        if (scrubBar)
2129            updateScrubBar();
2130
2131        if (durationDisplay)
2132            updateDuration();
2133
2134        dispatchEvent(event);
2135    }
2136
2137    /**
2138     *  @private
2139     */
2140    private function videoDisplay_volumeChangedHandler(event:Event):void
2141    {
2142        if (volumeBar)
2143        {
2144            volumeBar.value = volume;
2145            volumeBar.muted = muted;
2146        }
2147
2148        if (muteButton)
2149        {
2150            muteButton.muted = muted;
2151            muteButton.volume = volume;
2152        }
2153
2154        dispatchEvent(event);
2155    }
2156
2157    /**
2158     *  @private
2159     *  Indicates whether we are in the full screen state or not.
2160     *  We use this when determining our current skin state.
2161     */
2162    private var fullScreen:Boolean = false;
2163
2164    /**
2165     *  @private
2166     *  Holds a list of properties for the "video player state" that will
2167     *  be restored when going out of fullScreen mode.
2168     */
2169    private var beforeFullScreenInfo:Object;
2170
2171    /**
2172     *  @private
2173     *  Timer, which waits for 3 seconds by default to hide the
2174     *  playback controls.  If there's interaction by the user, then
2175     *  these playback controls are show again, and the timer will reset
2176     *  and start the countdown.
2177     */
2178    private var fullScreenHideControlTimer:Timer;
2179
2180    /**
2181     *  @private
2182     */
2183    private function fullScreenButton_clickHandler(event:MouseEvent):void
2184    {
2185        if (!fullScreen)
2186        {
2187            // check to make sure we can go into fullscreen mode
2188            if (!systemManager.getTopLevelRoot())
2189                return;
2190
2191            var screenBounds:Rectangle = getScreenBounds();
2192
2193            fullScreen = true;
2194
2195            // need it to go into full screen state for the skin
2196            invalidateSkinState();
2197
2198            // keep track of pauseWhenHidden b/c we will set it to false temporarily
2199            // so that the video does not pause when we reparent it to the top
2200            // level application
2201            var oldPauseWhenHidden:Boolean = pauseWhenHidden;
2202
2203            // let's get it off of our layout system so it doesn't interfere with
2204            // the sizing and positioning. Then let's resize it to be
2205            // the full size of our screen.  Then let's position it off-screen so
2206            // there are no other elements in the way.
2207            beforeFullScreenInfo = {parent: this.parent,
2208                x: this.x,
2209                y: this.y,
2210                explicitWidth: this.explicitWidth,
2211                explicitHeight: this.explicitHeight,
2212                percentWidth: this.percentWidth,
2213                percentHeight: this.percentHeight,
2214                isPopUp: this.isPopUp};
2215
2216            pauseWhenHidden = false;
2217
2218            if (!isPopUp)
2219            {
2220                // remove from old parent
2221                if (parent is IVisualElementContainer)
2222                {
2223                    var ivec:IVisualElementContainer = IVisualElementContainer(parent);
2224                    beforeFullScreenInfo.childIndex = ivec.getElementIndex(this);
2225                    ivec.removeElement(this);
2226                }
2227                else
2228                {
2229                    beforeFullScreenInfo.childIndex = parent.getChildIndex(this);
2230                    parent.removeChild(this);
2231                }
2232
2233                // add as a popup
2234                PopUpManager.addPopUp(this, FlexGlobals.topLevelApplication as DisplayObject, false, null, moduleFactory);
2235            }
2236
2237            // Resize the component to be the full screen of the stage.
2238            // Push the component at (0,0).  It should be on top of everything
2239            // at this point because it was added as a popup
2240            setLayoutBoundsSize(screenBounds.width, screenBounds.height, true);
2241            // set the explicit width/height to make sure this value sticks regardless
2242            // of any other code or layout passes.  Calling setLayoutBoundsSize() before hand
2243            // allows us to use postLayout width/height.
2244            // Setting explictWidth/Height sets percentWidth/Height to NaN.
2245            this.explicitWidth = width;
2246            this.explicitHeight = height;
2247            setLayoutBoundsPosition(0, 0, true);
2248
2249            // this is for video performance reasons, but sometimes the videoObject isn't there
2250            // if the source is null
2251            if (videoDisplay.videoObject)
2252            {
2253                beforeFullScreenInfo.smoothing = videoDisplay.videoObject.smoothing;
2254                beforeFullScreenInfo.deblocking = videoDisplay.videoObject.deblocking;
2255                videoDisplay.videoObject.smoothing = false;
2256                videoDisplay.videoObject.deblocking = 0;
2257            }
2258
2259            this.validateNow();
2260
2261            systemManager.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenEventHandler);
2262
2263            // TODO (rfrishbe): Should we make this FULL_SCREEN_INTERACTIVE if in AIR?
2264            systemManager.stage.displayState = StageDisplayState.FULL_SCREEN;
2265
2266            pauseWhenHidden = oldPauseWhenHidden;
2267
2268            var fullScreenHideControlsDelay:Number = getStyle("fullScreenHideControlsDelay");
2269
2270            if (fullScreenHideControlsDelay == 0)
2271            {
2272                playerControls.visible = false;
2273
2274                if (volumeBar)
2275                    volumeBar.closeDropDown(true);
2276            }
2277            else if (fullScreenHideControlsDelay < Infinity)
2278            {
2279                // start timer for detecting for mouse movements/clicks to hide the controls
2280                fullScreenHideControlTimer = new Timer(fullScreenHideControlsDelay, 1);
2281                fullScreenHideControlTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
2282                    fullScreenHideControlTimer_timerCompleteHandler, false, 0, true);
2283
2284                // use stage or systemManager?
2285                systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_DOWN, resetFullScreenHideControlTimer);
2286                systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_MOVE, resetFullScreenHideControlTimer);
2287                systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_WHEEL, resetFullScreenHideControlTimer);
2288
2289                // keyboard events don't happen when in fullScreen mode, but could be in fullScreen and interactive mode
2290                systemManager.getSandboxRoot().addEventListener(KeyboardEvent.KEY_DOWN, resetFullScreenHideControlTimer);
2291
2292                fullScreenHideControlTimer.start();
2293            }
2294        }
2295        else
2296        {
2297            systemManager.stage.displayState = StageDisplayState.NORMAL;
2298        }
2299    }
2300
2301    /**
2302     *  @private
2303     *  After waiting a certain time perdiod, we hide the controls if no
2304     *  user-interaction has occurred on-screen.
2305     */
2306    private function fullScreenHideControlTimer_timerCompleteHandler(event:TimerEvent):void
2307    {
2308        playerControls.visible = false;
2309
2310        if (volumeBar)
2311            volumeBar.closeDropDown(true);
2312    }
2313
2314    /**
2315     *  @private
2316     *  Handles when mouse interaction happens, and we are in the fullscreen mode.  This
2317     *  resets the fullScreenHideControlTimer.
2318     */
2319    private function resetFullScreenHideControlTimer(event:Event):void
2320    {
2321        playerControls.visible = true;
2322
2323        if (fullScreenHideControlTimer)
2324        {
2325            fullScreenHideControlTimer.reset();
2326            fullScreenHideControlTimer.start();
2327        }
2328        else
2329        {
2330            fullScreenHideControlTimer = new Timer(getStyle("fullScreenHideControlsDelay"), 1);
2331            fullScreenHideControlTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
2332                fullScreenHideControlTimer_timerCompleteHandler, false, 0, true);
2333        }
2334    }
2335
2336    /**
2337     *  @private
2338     *  Handles when coming out the full screen mode
2339     */
2340    private function fullScreenEventHandler(event:FullScreenEvent):void
2341    {
2342        // going in to full screen is handled by the
2343        // fullScreenButton_clickHandler
2344        if (event.fullScreen)
2345            return;
2346
2347        // keep track of pauseWhenHidden b/c we will set it to false temporarily
2348        // so that the video does not pause when we reparent it to the top
2349        // level application
2350        exitingFullScreenPauseWhenHidden = pauseWhenHidden;
2351        pauseWhenHidden = false;
2352
2353        // set the fullScreen variable back to false and remove this event listener
2354        fullScreen = false;
2355        systemManager.stage.removeEventListener(FullScreenEvent.FULL_SCREEN, fullScreenEventHandler);
2356
2357        // remove the event listeners to hide the controls
2358        systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_DOWN, resetFullScreenHideControlTimer);
2359        systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_MOVE, resetFullScreenHideControlTimer);
2360        systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_WHEEL, resetFullScreenHideControlTimer);
2361        systemManager.getSandboxRoot().removeEventListener(KeyboardEvent.KEY_DOWN, resetFullScreenHideControlTimer);
2362
2363        if (fullScreenHideControlTimer)
2364        {
2365            fullScreenHideControlTimer.stop();
2366            fullScreenHideControlTimer = null;
2367        }
2368
2369        // make the controls visible no matter what
2370        playerControls.visible = true;
2371
2372        // reset it so we're re-included in the layout
2373        this.x = beforeFullScreenInfo.x;
2374        this.y = beforeFullScreenInfo.y;
2375        this.explicitWidth = beforeFullScreenInfo.explicitWidth;
2376        this.explicitHeight = beforeFullScreenInfo.explicitHeight;
2377        this.percentWidth = beforeFullScreenInfo.percentWidth;
2378        this.percentHeight = beforeFullScreenInfo.percentHeight;
2379
2380        // sometimes there's no video object currently or there might not've been a
2381        // video object when we went in to fullScreen mode.  There may be no videoObject
2382        // if the source hasn't been set.
2383        if (videoDisplay.videoObject && beforeFullScreenInfo.smoothing !== undefined)
2384        {
2385            videoDisplay.videoObject.smoothing = beforeFullScreenInfo.smoothing;
2386            videoDisplay.videoObject.deblocking = beforeFullScreenInfo.deblocking;
2387        }
2388
2389        if (!beforeFullScreenInfo.isPopUp)
2390        {
2391            // remove from top level application:
2392            PopUpManager.removePopUp(this);
2393
2394            // add back to original parent
2395            if (beforeFullScreenInfo.parent is IVisualElementContainer)
2396                beforeFullScreenInfo.parent.addElementAt(this, beforeFullScreenInfo.childIndex);
2397            else
2398                beforeFullScreenInfo.parent.addChildAt(this, beforeFullScreenInfo.childIndex);
2399        }
2400
2401        // want to update pauseWhenHidden, but can't do it here
2402        // b/c the AIR window thinks it's invisible at this point
2403        // if we're on a Mac (a bug), so let's just defer this check
2404        // to commitProperties().
2405        if (exitingFullScreenPauseWhenHidden)
2406        {
2407            // if we need to set it back to true
2408            needsToUpdatePauseWhenHidden = true;
2409            invalidateProperties();
2410        }
2411
2412        beforeFullScreenInfo = null;
2413
2414        invalidateSkinState();
2415        invalidateSize();
2416        invalidateDisplayList();
2417    }
2418
2419    /**
2420     *  @private
2421     */
2422    private function playButton_clickHandler(event:MouseEvent):void
2423    {
2424        if (!playing)
2425            play();
2426    }
2427
2428    /**
2429     *  @private
2430     */
2431    private function pauseButton_clickHandler(event:MouseEvent):void
2432    {
2433        pause();
2434    }
2435
2436    /**
2437     *  @private
2438     */
2439    private function stopButton_clickHandler(event:MouseEvent):void
2440    {
2441        stop();
2442    }
2443
2444    /**
2445     *  @private
2446     */
2447    private function playPauseButton_clickHandler(event:MouseEvent):void
2448    {
2449        if (playing)
2450            pause();
2451        else
2452            play();
2453
2454        // need to synch up to what we've actually got because sometimes
2455        // the play() didn't actually play() because there's no source
2456        // or we're in an error state
2457        playPauseButton.selected = playing;
2458    }
2459
2460    /**
2461     *  @private
2462     */
2463    private function muteButton_mutedChangeHandler(event:FlexEvent):void
2464    {
2465        muted = muteButton.muted;
2466    }
2467
2468    /**
2469     *  @private
2470     */
2471    private function volumeBar_changeHandler(event:Event):void
2472    {
2473        if (volume != volumeBar.value)
2474            volume = volumeBar.value;
2475    }
2476
2477    /**
2478     *  @private
2479     */
2480    private function volumeBar_mutedChangeHandler(event:FlexEvent):void
2481    {
2482        if (muted != volumeBar.muted)
2483            muted = volumeBar.muted;
2484    }
2485
2486    /**
2487     *  @private
2488     *  When someone is holding the scrubBar, we don't want to update the
2489     *  range's value--for this time period, we'll let the user completely
2490     *  control the range.
2491     */
2492    private var scrubBarMouseCaptured:Boolean;
2493
2494    /**
2495     *  @private
2496     *  We pause the video when dragging the thumb for the scrub bar.  This
2497     *  stores whether we were paused or not.
2498     */
2499    private var wasPlayingBeforeSeeking:Boolean;
2500
2501    /**
2502     *  @private
2503     *  We are in the process of changing the timestamp
2504     */
2505    private var scrubBarChanging:Boolean;
2506
2507    /**
2508     *  @private
2509     */
2510    private function scrubBar_changeStartHandler(event:Event):void
2511    {
2512        scrubBarChanging = true;
2513    }
2514
2515    /**
2516     *  @private
2517     */
2518    private function scrubBar_thumbPressHandler(event:TrackBaseEvent):void
2519    {
2520        scrubBarMouseCaptured = true;
2521        if (playing)
2522        {
2523            pause();
2524            wasPlayingBeforeSeeking = true;
2525        }
2526    }
2527
2528    /**
2529     *  @private
2530     */
2531    private function scrubBar_thumbReleaseHandler(event:TrackBaseEvent):void
2532    {
2533        scrubBarMouseCaptured = false;
2534        if (wasPlayingBeforeSeeking)
2535        {
2536            play();
2537            wasPlayingBeforeSeeking = false;
2538        }
2539    }
2540
2541    /**
2542     *  @private
2543     */
2544    private function scrubBar_changeHandler(event:Event):void
2545    {
2546        seek(scrubBar.value);
2547    }
2548
2549    /**
2550     *  @private
2551     */
2552    private function scrubBar_changeEndHandler(event:Event):void
2553    {
2554        scrubBarChanging = false;
2555    }
2556}
2557}
2558