/***************************************************** * * Copyright 2010 Adobe Systems Incorporated. All Rights Reserved. * ***************************************************** * The contents of this file are subject to the Berkeley Software Distribution (BSD) Licence * (the "License"); you may not use this file except in * compliance with the License. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * * The Initial Developer of the Original Code is Adobe Systems Incorporated. * Portions created by Adobe Systems Incorporated are Copyright (C) 2010 Adobe Systems * Incorporated. All Rights Reserved. * *****************************************************/ /** * */ (function($, undefined){ /** * */ var StrobeMediaPlaybackHtml5 = function(element, options, javascriptCallbackFunctions){ this.$window = $(window); this.$document = $(document); this.element = element; this.$element = $(element); this.options = $.extend({}, $.fn.strobemediaplaybackhtml5.defaults, options); this.jsCallbackFunctions = javascriptCallbackFunctions; }; var strobeMediaPlaybackHtml5Methods = { initialize: function(){ $(document).bind('webkitfullscreenchange', this, this.onFullscreenChange); this.$element.bind("mousemove", this, this.onMouseMove); this.$player = this.$element.find("video"); //$("#" + this.options.id) this.player = this.$player.get(0); if(this.player == null) { this.$player = $("#" + this.options.id); this.player = this.$player.get(0); } this.$player.bind("timeupdate", this, this.onTimeUpdate); this.$player.bind('play', this, this.onPlay); this.$player.bind("playing", this, this.onPlaying); this.$player.bind('pause', this, this.onPause); this.$player.bind('ended', this, this.onEnded); this.$player.bind("loadeddata", this, this.onLoadedData); this.$player.bind("loadedmetadata", this, this.onMetaDataLoaded); this.$player.bind("progress", this, this.onProgress); this.$player.bind("click", this, this.onClick); this.$player.bind("dblclick", this, this.onDoubleClick); //this.$player.bind("contextmenu", this, function(event){ event.preventDefault(); }); //disable context menu this.$player.bind("error", this, onError); this.hidedelaytimeout, this.isdragging, this.trackswidth; this.controlbar = this.$element.find(this.options.controlbarselector); this.progressbar = this.controlbar.find(this.options.progressbarselector); this.progressbar.bind('click', this, this.onSeekClick); this.tracks = this.progressbar.find(this.options.tracksselector); this.seekbar = this.progressbar.find(this.options.seekbarselector); this.playedbar = this.progressbar.find(this.options.playedbarselector); this.bufferbar = this.progressbar.find(this.options.bufferedbarselector); this.playtoggle = this.controlbar.find(this.options.playtoggleselector); this.playtoggle.bind('click', this, this.onPlayToggleClick); this.playtoggle.bind("mousedown mouseup touchstart touchend", this, this.onButtonHover); this.slider = this.controlbar.find(this.options.sliderselector); this.slider.draggable({disabled: false, containment: "parent", axis: "x"}); this.slider.bind("dragstart", this, this.onSliderDragStart); this.slider.bind("drag", this, this.onSliderDragging); this.slider.bind("dragstop", this, this.onSliderDragStop); this.slider.bind("mousedown mouseup touchstart touchend", this, this.onButtonHover); this.volumeHigh = this.controlbar.find(this.options.volumeHigh); this.volumeSlider = this.controlbar.find(this.options.volumeSlider); this.volumeContainer = this.controlbar.find(this.options.volumeContainer); this.fullview = this.controlbar.find(this.options.fullviewselector); this.fullview.bind("click", this, this.onFullViewClick); this.fullview.bind("mousedown mouseup touchstart touchend", this, this.onButtonHover); //disable the fullview button until metadata is loaded; necessary for iPad native fullscreen to work this.fullview.addClass("disabled"); this.$player.attr("preload", "true"); //start loading the video, if not already started this.currenttime = this.controlbar.find(this.options.currenttimeselector); this.duration = this.controlbar.find(this.options.durationtimeselector); this.errorwindow = this.$element.find(this.options.errorwindowselector); this.playOverlay = this.$element.find(this.options.playoverlay); this.playOverlay.bind("click",this,this.onPlayToggleClick); this.monitor = new VideoElementMonitor(null); this.options.originalWidth = this.player.clientWidth; if(isDesktop) { this.controlbar.css("width", this.options.originalWidth - 1.5); if(this.options.originalWidth > 180) this.progressbar.css("width", this.options.originalWidth - 180); else this.progressbar.css("width", "50%"); } }, onSliderDragStart: function(event){ event.data.isdragging = true; event.data.seekbar.css("width", event.data.playedbar.width()+"px").show(); }, onSliderDragging: function(event, ui){ event.data.seekbar.css("width", ui.position.left+"px"); }, onSliderDragStop: function(event, ui){ event.data.isdragging = false; event.data.seekbar.hide(); event.data.player.currentTime = event.data.player.duration*((ui.position.left+event.data.slider.width()/2)/$(this).parent().width()); event.data.onTimeUpdate(event); }, onProgress: function(event){ try{ var start = event.target.buffered.start(); var end = event.target.buffered.end() var duration = event.target.duration; event.data.bufferbar.css("width", ((end/duration)*100)+"%"); }catch(exception){} }, onPlay: function(event){ event.data.playtoggle.addClass('paused'); playbackStart(event.data.options.id); }, onPlaying: function(event){ event.data.slider.draggable("option", "disabled", false); }, onPause: function(event){ event.data.playtoggle.removeClass('paused'); playbackPause(event.data.options.id); }, onEnded: function(event){ event.data.showControls(); event.data.playtoggle.removeClass('paused'); playbackComplete(event.data.options.id); }, onLoadedData: function(event){ event.data.bufferbar.css("width", "100%"); event.data.showControls(); event.data.duration.html(formatTimeStatus(0, event.target.duration)[1]); event.data.trackswidth = event.data.tracks.width(); }, onMetaDataLoaded: function(event){ if(event.data.jsCallbackFunctions.onLoad != null) event.data.jsCallbackFunctions.onLoad(); event.data.fullview.removeClass("disabled"); }, onMouseMove: function(event){ event.data.showControls(); if(!event.data.player.paused && !event.data.player.ended){ clearTimeout(event.data.hidedelaytimeout); event.data.hidedelaytimeout = setTimeout(function(){event.data.hideControls(event);}, event.data.options.hidedelay); } }, onButtonHover: function(event){ $(this).toggleClass("hover"); }, onPlayToggleClick: function(event){ if(event.data.player.paused) event.data.player.play(); else if(event.data.player.ended) { //event.data.player.ended = false; event.data.player.play(); event.data.onPlay(event); } else event.data.player.pause(); }, onSeekClick: function(event){ var time = event.data.player.duration * ((event.clientX - event.data.progressbar.offset().left) / event.data.progressbar.outerWidth()); if(time > 0) event.data.player.currentTime = time; if(event.data.player.paused) event.data.onPause(event); else event.data.onPlay(event); }, onClick: function(event){ if(event.data.player.ended) event.data.hideControls(event); else event.data.onMouseMove(event); }, onDoubleClick: function(event){ event.data.onFullViewClick(event); }, onFullViewClick: function(event){ try{ if(event.data.fullview.hasClass("disabled")) return; //do not do anything if the button is disabled if(navigator.userAgent.match(/(Macintosh|Windows|Safari|Version\/5\.[1-9])/gi).length >= 3){ //at a minimum match one of the OSes, Safari, and the version if($(document).context.webkitIsFullScreen) $(document).context.webkitCancelFullScreen(); else event.data.$element.context.webkitRequestFullScreen(); }else event.data.player.webkitEnterFullScreen(); }catch(error){ event.data.onFullscreenChange(event); } }, onFullscreenChange: function(event){ event.data.$element.toggleClass(event.data.options.fullscreenclass); event.data.fullview.toggleClass(event.data.options.fullviewactiveclass); /*event.data.progressbar.toggleClass("progressFullscreen");*/ var width = event.data.$element.css("width"); var height = event.data.$element.css("height"); width = width.substring(0, width.indexOf("px")); height = height.substring(0, height.indexOf("px")); if(isDesktop) { event.data.controlbar.css("width", width - 1.5); if(this.options.originalWidth > 180) this.progressbar.css("width", width - 180); else this.progressbar.css("width", "50%"); } event.data.trackswidth = event.data.tracks.width(); if(event.data.player.ended) event.data.onTimeUpdate(event); handlePlayoverlay(event.data.player.id, width, height); document.getElementById(event.data.player.id).scrollIntoView(); }, onTimeUpdate: function(event){ var percent = event.data.player.currentTime/event.data.player.duration; var px = percent*event.data.trackswidth; event.data.playedbar.css("width", px+"px"); if(!event.data.isdragging) event.data.slider.css("left", px+"px"); var times = formatTimeStatus(event.data.player.currentTime, event.data.player.duration); event.data.currenttime.html(times[0]); event.data.duration.html(times[1]); }, showControls: function(){ if(this.controlbar.is(":visible")) return; this.controlbar.fadeIn(this.options.fadeinspeed); }, hideControls: function(event){ if(event.data.player.paused && !event.data.player.ended) return; event.data.controlbar.fadeOut(event.data.options.fadeoutspeed); } }; StrobeMediaPlaybackHtml5.prototype = strobeMediaPlaybackHtml5Methods; /** * jQuery plugin hook */ $.fn.strobemediaplaybackhtml5 = function(options, javascriptCallbackFunctions){ var instances = [], i; var instance; var result = this.each(function(){ instance = new StrobeMediaPlaybackHtml5(this, options, javascriptCallbackFunctions); instances.push(instance); }); for (i = 0; i < instances.length; i++) { instances[i].initialize(); } return instance; }; /** * jQuery plugin defaults */ $.fn.strobemediaplaybackhtml5.defaults = { autoplay: false, hidedelay: 6000, fadeinspeed: "fast", fadeoutspeed: 500, controlbarselector: ".controls", progressbarselector: ".progress", tracksselector: ".tracks", sliderselector: ".slider", seekbarselector: ".seeking", playedbarselector: ".played", bufferedbarselector: ".buffered", playtoggleselector: ".icon.playtoggle", currenttimeselector: ".timestamp.current", durationtimeselector: ".timestamp.duration", errorwindowselector: ".errorwindow", fullviewselector: ".icon.fullview", fullscreenclass: "fullscreen", fullviewactiveclass: "fullscreen", playoverlay: ".playoverlay", volumeHigh: ".icon.volume.high", volumeSlider: ".volume-slider", volumeContainer: ".volume-container" }; function formatTimeStatus(currentPosition, totalDuration){ var h; var m; var s; function prettyPrintSeconds(seconds, leadingMinutes, leadingHours){ seconds = Math.floor(isNaN(seconds) ? 0 : Math.max(0, seconds)); h = Math.floor(seconds / 3600); m = Math.floor(seconds % 3600 / 60); s = seconds % 60; return ((h > 0 || leadingHours) ? (h + ":") : "") + (((h > 0 || leadingMinutes) && m < 10) ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; } var totalDurationString = prettyPrintSeconds(totalDuration); var currentPositionString = prettyPrintSeconds(currentPosition, h > 0 || m > 9, h > 0); return [currentPositionString, totalDurationString]; } var VideoElementMonitor = function($strobeMediaPlayback) { this.videoElement = { duration: 0, currentTime: 0, paused: true, muted: false }; this.$videoElement = $(this.videoElement); } StrobeMediaPlaybackHtml5.prototype = strobeMediaPlaybackHtml5Methods; })(jQuery);