2 * Authors: Yonghwi Park <yonghwi0324.park@samsung.com>
3 * Wonseop Kim <wonseop.kim@samsung.com>
7 * MultiMediaView is a widget that provides an audio or a video content handling features.
8 * A multi-media content handled with this widget can be played with HTML5's <audio> or <video> tag.
9 * If a user wants to play a music file, he should use "<audio>" tag.
10 * And he should use "<video>" tag to play a video file.
13 * data-theme : Set a theme of widget.
14 * If this value is not defined, widget will use parent`s theme. (optional)
15 * data-controls : If this value is 'true', widget will use belonging controller.
16 * If this value is 'false', widget will use browser`s controller.
17 * Default value is 'true'.
18 * data-fullscreen : Set a status that fullscreen when inital start.
19 * Default value is 'false'.
23 * : Get or set a widget of widget.
25 * : Get or set a height of widget.
26 * size( number, number )
27 * : Set a size of widget and resize a widget.
28 * First argument is width and second argument is height.
29 * fullscreen( [boolean] )
30 * : Set a status that fullscreen.
34 * create : triggered when a multimediaview is created.
39 * <video data-controls="true" style="width:100%;">
40 * <source src="media/oceans-clip.mp4" type="video/mp4" />
41 * Your browser does not support the video tag.
45 * <audio data-controls="true" style="width:100%;">
46 * <source src="media/Over the horizon.mp3" type="audio/mp3" />
47 * Your browser does not support the audio tag.
52 ( function ( $, document, window, undefined ) {
53 $.widget( "tizen.multimediaview", $.mobile.widget, {
58 initSelector : "video, audio"
60 _create : function () {
63 viewElement = view[0],
64 option = self.options,
65 role = "multimediaview",
70 isControlHide : false,
78 view.addClass( "ui-multimediaview" );
79 control = self._createControl();
81 if ( view[0].nodeName === "AUDIO" ) {
82 control.addClass( "ui-multimediaview-audio" );
86 view.wrap( "<div class='ui-multimediaview-wrap'>" ).after( control );
87 if ( option.controls ) {
88 if ( view.attr("controls") ) {
89 view.removeAttr( "controls" );
95 $( document ).bind( "pagechange.multimediaview", function ( e ) {
96 var $page = $( e.target );
97 if ( $page.find( view ).length > 0 && viewElement.autoplay ) {
101 if ( option.controls ) {
105 }).bind( "pagebeforechange.multimediaview", function ( e ) {
106 if ( viewElement.played.length !== 0 ) {
111 $( window ).bind( "resize.multimediaview orientationchange.multimediaview", function ( e ) {
112 if ( !option.controls ) {
115 var $page = $( e.target ),
116 $scrollview = view.parents( ".ui-scrollview-clip" );
118 $scrollview.each( function ( i ) {
119 if ( $.data( this, "scrollview" ) ) {
120 $( this ).scrollview( "scrollTo", 0, 0 );
124 // for maintaining page layout
125 if ( !option.fullscreen ) {
126 $( ".ui-footer:visible" ).show();
128 $( ".ui-footer" ).hide();
129 self._fitContentArea( $page );
135 _resize : function () {
136 var view = this.element,
137 parent = view.parent(),
138 control = parent.find( ".ui-multimediaview-control" ),
143 this._resizeFullscreen( this.options.fullscreen );
144 viewWidth = ( ( view[0].nodeName === "VIDEO" ) ? view.width() : parent.width() );
145 viewHeight = ( ( view[0].nodeName === "VIDEO" ) ? view.height() : control.height() );
146 viewOffset = view.offset();
148 this._resizeControl( viewOffset, viewWidth, viewHeight );
150 this._updateSeekBar();
151 this._updateVolumeState();
153 _resizeControl : function ( offset, width, height ) {
156 viewElement = view[0],
157 control = view.parent().find( ".ui-multimediaview-control" ),
158 buttons = control.find( ".ui-button" ),
159 playpauseButton = control.find( ".ui-playpausebutton" ),
160 volumeControl = control.find( ".ui-volumecontrol" ),
161 seekBar = control.find( ".ui-seekbar" ),
162 durationLabel = control.find( ".ui-durationlabel" ),
163 controlWidth = width,
164 controlHeight = control.outerHeight( true ),
166 controlOffset = null;
169 if ( view[0].nodeName === "VIDEO" ) {
170 controlOffset = control.offset();
171 controlOffset.left = offset.left;
172 controlOffset.top = offset.top + height - controlHeight;
173 control.offset( controlOffset );
176 control.width( controlWidth );
180 availableWidth = control.width() - ( buttons.outerWidth( true ) * buttons.length );
181 availableWidth -= ( parseInt( buttons.eq( 0 ).css( "margin-left" ), 10 ) + parseInt( buttons.eq( 0 ).css( "margin-right" ), 10 ) ) * buttons.length;
182 if ( !self.isVolumeHide ) {
183 availableWidth -= volumeControl.outerWidth( true );
185 seekBar.width( availableWidth );
188 if ( durationLabel && !isNaN( viewElement.duration ) ) {
189 durationLabel.find( "p" ).text( self._convertTimeFormat( viewElement.duration ) );
192 if ( viewElement.autoplay && viewElement.paused === false ) {
193 playpauseButton.removeClass( "ui-play-icon" ).addClass( "ui-pause-icon" );
196 _resizeFullscreen : function ( isFullscreen ) {
199 parent = view.parent(),
200 control = view.parent().find( ".ui-multimediaview-control" ),
201 playpauseButton = control.find( ".ui-playpausebutton" ),
202 timestampLabel = control.find( ".ui-timestamplabel" ),
203 seekBar = control.find( ".ui-seekbar" ),
204 durationBar = seekBar.find( ".ui-duration" ),
205 currenttimeBar = seekBar.find( ".ui-currenttime" ),
209 if ( isFullscreen ) {
210 if ( !self.backupView ) {
212 width : view[0].style.getPropertyValue( "width" ) || "",
213 height : view[0].style.getPropertyValue( "height" ) || "",
214 position : view.css( "position" ),
215 zindex : view.css( "z-index" )
218 docWidth = $( "body" )[0].clientWidth;
219 docHeight = $( "body" )[0].clientHeight;
221 view.width( docWidth ).height( docHeight - 1 );
222 view.addClass( "ui-" + self.role + "-fullscreen" );
228 if ( !self.backupView ) {
232 view.removeClass( "ui-" + self.role + "-fullscreen" );
234 "width" : self.backupView.width,
235 "height" : self.backupView.height,
236 "position": self.backupView.position,
237 "z-index": self.backupView.zindex
239 self.backupView = null;
243 _addEvent : function () {
246 viewElement = view[0],
247 control = view.parent().find( ".ui-multimediaview-control" ),
248 playpauseButton = control.find( ".ui-playpausebutton" ),
249 timestampLabel = control.find( ".ui-timestamplabel" ),
250 durationLabel = control.find( ".ui-durationlabel" ),
251 volumeButton = control.find( ".ui-volumebutton" ),
252 volumeControl = control.find( ".ui-volumecontrol" ),
253 volumeBar = volumeControl.find( ".ui-volumebar" ),
254 volumeGuide = volumeControl.find( ".ui-guide" ),
255 volumeHandle = volumeControl.find( ".ui-handler" ),
256 fullscreenButton = control.find( ".ui-fullscreenbutton" ),
257 seekBar = control.find( ".ui-seekbar" ),
258 durationBar = seekBar.find( ".ui-duration" ),
259 currenttimeBar = seekBar.find( ".ui-currenttime" );
261 view.bind( "loadedmetadata.multimediaview", function ( e ) {
262 if ( !isNaN( viewElement.duration ) ) {
263 durationLabel.find( "p" ).text( self._convertTimeFormat( viewElement.duration ) );
266 }).bind( "timeupdate.multimediaview", function ( e ) {
267 self._updateSeekBar();
268 }).bind( "play.multimediaview", function ( e ) {
269 playpauseButton.removeClass( "ui-play-icon" ).addClass( "ui-pause-icon" );
270 }).bind( "pause.multimediaview", function ( e ) {
271 playpauseButton.removeClass( "ui-pause-icon" ).addClass( "ui-play-icon" );
272 }).bind( "ended.multimediaview", function ( e ) {
273 if ( typeof viewElement.loop == "undefined" || viewElement.loop === "" ) {
276 }).bind( "volumechange.multimediaview", function ( e ) {
277 if ( viewElement.volume < 0.1 ) {
278 viewElement.muted = true;
279 volumeButton.removeClass( "ui-volume-icon" ).addClass( "ui-mute-icon" );
281 viewElement.muted = false;
282 volumeButton.removeClass( "ui-mute-icon" ).addClass( "ui-volume-icon" );
285 if ( !self.isVolumeHide ) {
286 self._updateVolumeState();
288 }).bind( "durationchange.multimediaview", function ( e ) {
289 if ( !isNaN( viewElement.duration ) ) {
290 durationLabel.find( "p" ).text( self._convertTimeFormat( viewElement.duration ) );
293 }).bind( "error.multimediaview", function ( e ) {
294 switch ( e.target.error.code ) {
295 case e.target.error.MEDIA_ERR_ABORTED :
296 window.alert( 'You aborted the video playback.' );
298 case e.target.error.MEDIA_ERR_NETWORK :
299 window.alert( 'A network error caused the video download to fail part-way.' );
301 case e.target.error.MEDIA_ERR_DECODE :
302 window.alert( 'The video playback was aborted due to a corruption problem or because the video used features your browser did not support.' );
304 case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED :
305 window.alert( 'The video could not be loaded, either because the server or network failed or because the format is not supported.' );
308 window.alert( 'An unknown error occurred.' );
311 }).bind( "vclick.multimediaview", function ( e ) {
312 if ( !self.options.controls ) {
316 control.fadeToggle( "fast", function () {
317 var offset = control.offset();
318 self.isControlHide = !self.isControlHide;
319 if ( self.options.mediatype == "video" ) {
326 playpauseButton.bind( "vclick.multimediaview", function () {
329 if ( viewElement.paused ) {
335 if ( self.options.mediatype == "video" ) {
340 fullscreenButton.bind( "vclick.multimediaview", function () {
341 self.fullscreen( !self.options.fullscreen );
342 control.fadeIn( "fast" );
346 seekBar.bind( "vmousedown.multimediaview", function ( e ) {
348 duration = viewElement.duration,
349 durationOffset = durationBar.offset(),
350 durationWidth = durationBar.width(),
351 timerate = ( x - durationOffset.left ) / durationWidth,
352 time = duration * timerate;
354 viewElement.currentTime = time;
361 $( document ).bind( "vmousemove.multimediaview", function ( e ) {
363 timerate = ( x - durationOffset.left ) / durationWidth;
365 viewElement.currentTime = duration * timerate;
369 }).bind( "vmouseup.multimediaview", function () {
370 $( document ).unbind( "vmousemove.multimediaview vmouseup.multimediaview" );
371 if ( viewElement.paused ) {
379 volumeButton.bind( "vclick.multimediaview", function () {
380 if ( self.isVolumeHide ) {
381 var view = self.element,
382 volume = viewElement.volume;
384 self.isVolumeHide = false;
386 volumeControl.fadeIn( "fast" );
387 self._updateVolumeState();
388 self._updateSeekBar();
390 self.isVolumeHide = true;
391 volumeControl.fadeOut( "fast", function () {
394 self._updateSeekBar();
398 volumeBar.bind( "vmousedown.multimediaview", function ( e ) {
399 var baseX = e.clientX,
400 volumeGuideLeft = volumeGuide.offset().left,
401 volumeGuideWidth = volumeGuide.width(),
402 volumeBase = volumeGuideLeft + volumeGuideWidth,
403 handlerOffset = volumeHandle.offset(),
404 volumerate = ( baseX - volumeGuideLeft ) / volumeGuideWidth,
405 currentVolume = ( baseX - volumeGuideLeft ) / volumeGuideWidth;
408 self._setVolume( currentVolume.toFixed( 2 ) );
413 $( document ).bind( "vmousemove.multimediaview", function ( e ) {
414 var currentX = e.clientX,
415 currentVolume = ( currentX - volumeGuideLeft ) / volumeGuideWidth;
417 self._setVolume( currentVolume.toFixed( 2 ) );
421 }).bind( "vmouseup.multimediaview", function () {
422 $( document ).unbind( "vmousemove.multimediaview vmouseup.multimediaview" );
424 if ( self.options.mediatype == "video" ) {
430 _removeEvent : function () {
433 control = view.parent().find( ".ui-multimediaview-control" ),
434 playpauseButton = control.find( ".ui-playpausebutton" ),
435 fullscreenButton = control.find( ".ui-fullscreenbutton" ),
436 seekBar = control.find( ".ui-seekbar" ),
437 volumeControl = control.find( ".ui-volumecontrol" ),
438 volumeBar = volumeControl.find( ".ui-volumebar" ),
439 volumeHandle = volumeControl.find( ".ui-handler" );
441 view.unbind( ".multimediaview" );
442 playpauseButton.unbind( ".multimediaview" );
443 fullscreenButton.unbind( ".multimediaview" );
444 seekBar.unbind( ".multimediaview" );
445 volumeBar.unbind( ".multimediaview" );
446 volumeHandle.unbind( ".multimediaview" );
448 _createControl : function () {
451 control = $( "<span></span>" ),
452 playpauseButton = $( "<span></span>" ),
453 seekBar = $( "<span></span>" ),
454 timestampLabel = $( "<span><p>00:00:00</p></span>" ),
455 durationLabel = $( "<span><p>00:00:00</p></span>" ),
456 volumeButton = $( "<span></span>" ),
457 volumeControl = $( "<span></span>" ),
458 volumeBar = $( "<div></div>" ),
459 volumeGuide = $( "<span></span>" ),
460 volumeValue = $( "<span></span>" ),
461 volumeHandle = $( "<span></span>" ),
462 fullscreenButton = $( "<span></span>" ),
463 durationBar = $( "<span></span>" ),
464 currenttimeBar = $( "<span></span>" );
466 control.addClass( "ui-" + self.role + "-control" );
467 playpauseButton.addClass( "ui-playpausebutton ui-button" );
468 seekBar.addClass( "ui-seekbar" );
469 timestampLabel.addClass( "ui-timestamplabel" );
470 durationLabel.addClass( "ui-durationlabel" );
471 volumeButton.addClass( "ui-volumebutton ui-button" );
472 fullscreenButton.addClass( "ui-fullscreenbutton ui-button" );
473 durationBar.addClass( "ui-duration" );
474 currenttimeBar.addClass( "ui-currenttime" );
475 volumeControl.addClass( "ui-volumecontrol" );
476 volumeBar.addClass( "ui-volumebar" );
477 volumeGuide.addClass( "ui-guide" );
478 volumeValue.addClass( "ui-value" );
479 volumeHandle.addClass( "ui-handler" );
481 seekBar.append( durationBar ).append( currenttimeBar ).append( durationLabel ).append( timestampLabel );
483 playpauseButton.addClass( "ui-play-icon" );
484 if ( view[0].muted ) {
485 $( volumeButton ).addClass( "ui-mute-icon" );
487 $( volumeButton ).addClass( "ui-volume-icon" );
490 volumeBar.append( volumeGuide ).append( volumeValue ).append( volumeHandle );
491 volumeControl.append( volumeBar );
493 control.append( playpauseButton ).append( seekBar ).append( volumeControl ).append( volumeButton );
495 if ( self.element[0].nodeName === "VIDEO" ) {
496 $( fullscreenButton ).addClass( "ui-fullscreen-on" );
497 control.append( fullscreenButton );
499 volumeControl.hide();
503 _startTimer : function ( duration ) {
512 control = view.parent().find( ".ui-multimediaview-control" ),
513 volumeControl = control.find( ".ui-volumecontrol" );
515 self.controlTimer = setTimeout( function () {
516 self.isVolumeHide = true;
517 self.isControlHide = true;
518 self.controlTimer = null;
519 volumeControl.hide();
520 control.fadeOut( "fast" );
523 _endTimer : function () {
524 if ( this.controlTimer ) {
525 clearTimeout( this.controlTimer );
526 this.controlTimer = null;
529 _convertTimeFormat : function ( systime ) {
530 var ss = parseInt( systime % 60, 10 ).toString(),
531 mm = parseInt( ( systime / 60 ) % 60, 10 ).toString(),
532 hh = parseInt( systime / 3600, 10 ).toString(),
533 time = ( ( hh.length < 2 ) ? "0" + hh : hh ) + ":" +
534 ( ( mm.length < 2 ) ? "0" + mm : mm ) + ":" +
535 ( ( ss.length < 2 ) ? "0" + ss : ss );
539 _updateSeekBar : function ( currenttime ) {
542 duration = view[0].duration,
543 control = view.parent().find( ".ui-multimediaview-control" ),
544 seekBar = control.find( ".ui-seekbar" ),
545 durationBar = seekBar.find( ".ui-duration" ),
546 currenttimeBar = seekBar.find( ".ui-currenttime" ),
547 timestampLabel = control.find( ".ui-timestamplabel" ),
548 durationOffset = durationBar.offset(),
549 durationWidth = durationBar.width(),
550 durationHeight = durationBar.height(),
553 if ( typeof currenttime == "undefined" ) {
554 currenttime = view[0].currentTime;
556 timebarWidth = parseInt( currenttime / duration * durationWidth, 10 );
557 durationBar.offset( durationOffset );
558 currenttimeBar.offset( durationOffset ).width( timebarWidth );
559 timestampLabel.find( "p" ).text( self._convertTimeFormat( currenttime ) );
561 _updateVolumeState : function () {
564 control = view.parent().find( ".ui-multimediaview-control" ),
565 volumeControl = control.find( ".ui-volumecontrol" ),
566 volumeButton = control.find( ".ui-volumebutton" ),
567 volumeBar = volumeControl.find( ".ui-volumebar" ),
568 volumeGuide = volumeControl.find( ".ui-guide" ),
569 volumeValue = volumeControl.find( ".ui-value" ),
570 volumeHandle = volumeControl.find( ".ui-handler" ),
571 handlerWidth = volumeHandle.width(),
572 handlerHeight = volumeHandle.height(),
573 volumeGuideHeight = volumeGuide.height(),
574 volumeGuideWidth = volumeGuide.width(),
578 handlerOffset = null,
579 volume = view[0].volume;
581 volumeGuideTop = parseInt( volumeGuide.offset().top, 10 );
582 volumeGuideLeft = parseInt( volumeGuide.offset().left, 10 );
583 volumeBase = volumeGuideLeft;
584 handlerOffset = volumeHandle.offset();
585 handlerOffset.top = volumeGuideTop - parseInt( ( handlerHeight - volumeGuideHeight ) / 2, 10 );
586 handlerOffset.left = volumeBase + parseInt( volumeGuideWidth * volume, 10 ) - parseInt( handlerWidth / 2, 10 );
587 volumeHandle.offset( handlerOffset );
588 volumeValue.width( parseInt( volumeGuideWidth * ( volume ), 10 ) );
590 _setVolume : function ( value ) {
591 var viewElement = this.element[0];
593 if ( value < 0.0 || value > 1.0 ) {
597 viewElement.volume = value;
599 _fitContentArea: function ( page, parent ) {
600 if ( typeof parent == "undefined" ) {
604 var $page = $( page ),
605 $content = $( ".ui-content:visible:first" ),
606 hh = $( ".ui-header:visible" ).outerHeight() || 0,
607 fh = $( ".ui-footer:visible" ).outerHeight() || 0,
608 pt = parseFloat( $content.css( "padding-top" ) ),
609 pb = parseFloat( $content.css( "padding-bottom" ) ),
610 wh = ( ( parent === window ) ? window.innerHeight : $( parent ).height() ),
611 height = wh - ( hh + fh ) - ( pt + pb );
617 width : function ( value ) {
622 if ( args.length === 0 ) {
625 if ( args.length === 1 ) {
630 height : function ( value ) {
635 if ( args.length === 0 ) {
636 return view.height();
638 if ( args.length === 1 ) {
639 view.height( value );
643 size : function ( width, height ) {
647 view.width( width ).height( height );
650 fullscreen : function ( value ) {
653 control = view.parent().find( ".ui-multimediaview-control" ),
654 fullscreenButton = control.find( ".ui-fullscreenbutton" ),
656 option = self.options,
657 currentPage = $( ".ui-page-active" );
659 if ( args.length === 0 ) {
660 return option.fullscreen;
662 if ( args.length === 1 ) {
663 view.parents( ".ui-content" ).scrollview( "scrollTo", 0, 0 );
665 this.options.fullscreen = value;
667 currentPage.children( ".ui-header" ).hide();
668 currentPage.children( ".ui-footer" ).hide();
669 this._fitContentArea( currentPage );
670 fullscreenButton.removeClass( "ui-fullscreen-on" ).addClass( "ui-fullscreen-off" );
672 currentPage.children( ".ui-header" ).show();
673 currentPage.children( ".ui-footer" ).show();
674 this._fitContentArea( currentPage );
675 fullscreenButton.removeClass( "ui-fullscreen-off" ).addClass( "ui-fullscreen-on" );
680 refresh : function () {
685 $( document ).bind( "pagecreate create", function ( e ) {
686 $.tizen.multimediaview.prototype.enhanceWithin( e.target );
688 } ( jQuery, document, window ) );