/*!
* DTS Player jQuery plugin
* http://johndyer.name/
*
* Creates a controller bar for HTML5 <video> tags
* and falls back to a Flash player for browsers that
* do not support <video> or cannot play the video type.
* Currently designed for H.264.
*
* Copyright 2010, John Dyer
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Date: Thursday June 06 11:22:48 2010 -0600
*/

// TODO:
// make volume be event driven, remember setting (cookie, startup)
// loaded/ready event

(function($) {

// for IE
var v = document.createElement('video');

// Flash Bridge
var html5 = {};

// Flash makes calls through this object to the fake <video> object
html5.FlashBridge = (function() {

var players = [];

return {
// when Flash is ready, it calls out to this method
initPlayer: function(id) {
var player = players[id];

// magic
if (navigator.appName.indexOf("Microsoft") != -1) {
player.flashObj = window[id];
} else {
player.flashObj = document[id];
}

if (player.init)
player.init(player);
}

// receives events from FLASH and translates them to HTML5 media events
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
,fireEvent: function(id, flashEventName, value) {

var player = players[id];
player.ended = false;
player.paused = false;

// fake event object to mimic real <video> event.
var e = {};

switch (flashEventName) {
case 'ready':
e.type = 'loadeddata';
break;
case 'progress':
e.type = 'progress';
e.total = value.total;
e.loaded = value.loaded;
console.log(value, e);
break;
case 'playhead':
e.type = 'timeupdate';
player.currentTime = value;
break;
case 'seeked':
e.type = 'seeked';
player.currentTime = value;
break;
case 'completed':
e.type = 'ended';
player.currentTime = value;
player.ended = true;
break;
case 'state':

switch (value) {
case 'playing':
e.type = 'play';
break;
case 'stopped':
e.type = 'pause';
player.paused = true;
break;
case 'paused':
e.type = 'paused';
player.paused = true;
break;
default:
// ignore unknown for now
return;
}
break;
case 'metadatareceived':
// set properties which look like <video> properties
e.type = 'loadedmetadata';
player.duration = value.duration;
player.metadata = value;
break;

}

e.srcElement = player;
player.dispatchEvent(e.type, e);
}
, createPlayer: function($v, flashUrl, videoUrl, posterUrl, init) {

// setup objects
//var flashUrl = 'html5player.swf?'; //debug=true&';
var flashid = $v.attr('id');
var container = $('<div class="flash-video"></div>');

// attempt to create a Flash player that mimics HTML5 video
var player = html5.FlashPlayer(flashid);
if (init)
player.init = init;
players[flashid] = player;

// replace <video> with container <div> (IE doesn't like HTML5 elements)
var width = $v.attr('width');
var height = $v.attr('height');

$v.before(container);
$v.remove();

// create <embed> or <object>
if (navigator.appName.indexOf("Microsoft") != -1) {
container[0].innerHTML =
'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" \
id="' + flashid + '" width="' + width + '" height="' + height + '"> \
<param name="quality" value="high" /> \
<param name="bgcolor" value="#000000" /> \
<param name="wmode" value="transparent" /> \
<param name="allowScriptAccess" value="sameDomain" /> \
<param name="allowFullScreen" value="true" /> \
<param name="movie" value="' + flashUrl + '?' + 'file=' + videoUrl +'" /> \
</object>';

} else {

container.html(
'<embed name="' + flashid + '" quality="high" play="true" loop="false" quality="high" allowScriptAccess="sameDomain" wmode="transparent" allowFullScreen="true" \
align="middle" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" \
src="' + flashUrl + '?' + 'file=' + videoUrl +'" bgcolor="#000000" width="' + width + '" height="' + height + '"></embed>');
}

return player;

}
};
}());

/*
Mimics the <video> element by calling Flash's External Interface
*/
html5.FlashPlayer = function(flashid) {

var events = {};

// JavaScript values and ExternalInterface methods that match HTML5 video properties methods
// http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
return {

// special
id: flashid,
isFlash: true,
flashObj: null,

// get properties
paused: true,
ended: false,
seeking: false,

// time ranges
seekable: [],
played: [],

// fake get/set properties
muted: false,
volume: 1,
currentTime: 0,
duration: 0,

// methods
play: function(url) {
this.flashObj.playMedia(url);
this.paused = false;
}
, load: function(url) {
this.flashObj.loadMedia(url);
this.paused = false;
}
, pause: function() {
this.flashObj.pauseMedia();
this.paused = true;
}
, stop: function() {
this.flashObj.stopMedia();
this.paused = true;
}
, setCurrentTime: function(time) {
this.flashObj.setCurrentTime(time);
this.currentTime = time;
}

, setVolume: function(volume) {
this.flashObj.setVolume(volume);
this.volume = volume;
}

, setMuted: function(muted) {
this.flashObj.setMuted(muted);
this.muted = muted;
}

, setFullscreen: function(fullscreen) {
this.flashObj.setFullscreen(fullscreen);
}

// start: fake events
, addEventListener: function (eventName, callback, bubble) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
}
, dispatchEvent: function (eventName) {
//console.log(eventName, events[eventName]);

var i, callbacks = events[eventName], args;
if (callbacks) {
args = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < callbacks.length; i++) {

callbacks[i].apply(null, args);
}
}
}
}
};
window.html5 = html5;


// DTS Player jQuery extension

// default player values
var defaults = {
width: 240,
height: 130,
flashUrl: 'dtsplayer.swf'
}

// utility methods
function formatTime (seconds) {
    seconds = Math.round(seconds);
    minutes = Math.floor(seconds / 60);
    minutes = (minutes >= 10) ? minutes : "0" + minutes;
    seconds = Math.floor(seconds % 60);
    seconds = (seconds >= 10) ? seconds : "0" + seconds;
    return minutes + ":" + seconds;
}

jQuery.fn.dtsplayer = function(options) {
return this.each(function(){

var o = $.extend(defaults, options);

return createdtsplayer($(this), o);
});
};

function createdtsplayer($v, options) {

var id = $v.attr('id') + '_dtsplayer';

var html = $('<div id="' + id + '" class="dtsplayer-container"> \
<div class="dtsplayer-video"> \
</div> \
<ul class="dtsplayer-controls"> \
<li class="dtsplayer-playpause-button dtsplayer-play"><span></span></li> \
<li class="dtsplayer-time-rail"> \
<span class="dtsplayer-time-total"></span> \
<span class="dtsplayer-time-loaded"></span> \
<span class="dtsplayer-time-current"></span> \
</li> \
<li class="dtsplayer-time"> \
<span class="dtsplayer-currenttime"></span> \
<span>|</span> \
<span class="dtsplayer-duration"></span> \
</li> \
<li class="dtsplayer-volume-button dtsplayer-mute"> \
<span></span> \
<div class="dtsplayer-volume-slider"> \
<div class="dtsplayer-volume-rail"><div class="dtsplayer-volume-handle"></div></div> \
</div> \
</li> \
<li class="dtsplayer-fullscreen-button"><span></span></li> \
</ul> \
<div style="clear:both;"><div> \
</div>');

// insert and switch position
$v.before(html);
var container = $('#' + id);
container.find('.dtsplayer-video').append($v);

// set container size to video size
container
.width( $v.attr('width') )
.height( $v.attr('height') );

// controls bar
var controls = container.find('.dtsplayer-controls')

// show/hide controls
container
.bind('mouseenter',function() { controls.show(); })
.bind('mouseleave',function() { controls.hide(); });


// get native video object
var video = $v[0];

// TEST for HTML5 video
var supportsVideoTag = false;


// ipad/iphone test
var u = navigator.userAgent;
var isMobileAppleProduct = (u.match(/iPad/i) != null || u.match(/iPhone/i) != null );


if (isMobileAppleProduct) {
// add controls and stop
$v.attr('controls','controls');

// override Apple's autoplay override
if ($v.attr('autoplay') != undefined) {

function fakeClick(fn) {
// create a link that will play our video
var $a = $('<a href="#" id="fakeClick"></a>');
$a.bind("click", function(e) {
e.preventDefault();
fn();
});

// add the link to the body
$("body").append($a);

// grab the link
var el = $("#fakeClick").get(0);

// fire a fake event
if (document.createEvent) {
var evt = document.createEvent("MouseEvents");
if (evt.initMouseEvent) {
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
el.dispatchEvent(evt);
}
}

// remove, and don't tell Stevie J.
$(el).remove();
}
fakeClick(function() {
video.play();
});
}

return;
} else {
// remove native controls if accidentally set
$v.removeAttr('controls');
}


// get current types
var availableTypes = [];
if ($v.attr('type') != '')
availableTypes.push($v.attr('type'));
$v.find('source').each(function() {
if ($(this).attr('type') != '')
availableTypes.push($(this).attr('type'));
});

// test for <video> playback and video file type
var videoTag = document.createElement('video');
if (typeof videoTag.canPlayType != 'undefined') {
for (var i in availableTypes) {
if (videoTag.canPlayType( availableTypes[i] ) != '') {
supportsVideoTag = true;
}
}
}

if (supportsVideoTag) {

// add a few extra methods to allow cross/flash usage
$.extend(video,
{
setCurrentTime: function(time) {
video.currentTime = time;
}
, setMuted : function(muted) {
video.muted = muted;
}
, setVolume : function(volume) {
video.volume = volume;
}
});

setupControls(video);
} else {
// build alternate flash player

// TODO: double check that flash can play this video file
var videoUrl = $v.find('source').attr('src');
var posterUrl = $v.attr('poster');

video = html5.FlashBridge.createPlayer($v, options.flashUrl, videoUrl, posterUrl, setupControls);
}

function setupControls(video) {
// find controls
var playpause = controls.find('.dtsplayer-playpause-button');
var fullscreen = controls.find('.dtsplayer-fullscreen-button');

var currentTime = controls.find('.dtsplayer-currenttime');
var duration = controls.find('.dtsplayer-duration');

var mute = controls.find('.dtsplayer-volume-button');
var volumeSlider= controls.find('.dtsplayer-volume-slider');
var volumeRail = controls.find('.dtsplayer-volume-rail');
var volumeHandle= controls.find('.dtsplayer-volume-handle');

var timeRail = controls.find('.dtsplayer-time-rail');
var timeCurrent = timeRail.find('.dtsplayer-time-current');
var timeLoaded = timeRail.find('.dtsplayer-time-loaded');
var timeTotal = timeRail.find('.dtsplayer-time-total');

// WebKit can't report the correct with on absolute and floated elements. Hooray.
// Reverting to manually sizing
function setRailSize() {
var usedWidth = 25 + //playpause.outerWidth(true) +
45 + // currentTime.outerWidth(true) +
45 + // duration.outerWidth(true) +
25 + //mute.outerWidth(true) +
25; //fullscreen.outerWidth(true);
var railWidth = container.outerWidth() - usedWidth - 5; // - (timeRail.outerWidth(true) - timeRail.outerWidth(false));

timeRail.width(railWidth);

timeCurrent.width(0);
timeLoaded.width(0);
timeTotal.width(railWidth-10);

}
setRailSize();

// play/pause button
playpause.bind('click', function() {
if (playpause.hasClass('dtsplayer-play')) {
video.play();
playpause.removeClass('dtsplayer-play').addClass('dtsplayer-pause');
} else {
video.pause();
playpause.removeClass('dtsplayer-pause').addClass('dtsplayer-play');
}
});

// VOLUME SLIDER
function volumeMove(e) {
//$('body').css('cursor','N-resize');

//console.log('page',e.pageY);
//console.log('handle',volumeHandle.offset().top);
//console.log('rail', volumeRail.offset().top);

// only allow it to move within the rail
var railHeight = volumeRail.height();
var newY = e.pageY - volumeRail.offset().top;
if (newY < 0)
newY = 0;
else if (newY > railHeight)
newY = railHeight;

// set position
volumeHandle.css('top',newY - (volumeHandle.height()/2));

// calculate volume
var volume = (railHeight - newY) / railHeight;

// make sure to check mute status
if (volume == 0) {
video.setMuted(true);
mute.removeClass('dtsplayer-mute').addClass('dtsplayer-unmute');
} else {
video.setMuted(false);
mute.removeClass('dtsplayer-unmute').addClass('dtsplayer-mute');
}

video.setVolume(volume);
};
function positionVolumeHandle(volume) {
volumeHandle.css('top', volumeRail.height() - (volumeRail.height() * volume) - (volumeHandle.height()/2));
}
function removeMouseMove() {
//$(document).css('cursor','');
$(document)
.unbind('mousemove',volumeMove)
.unbind('mouseup', removeMouseMove);
}
volumeSlider.bind('mousedown', function(e) {
volumeMove(e);
$(document)
.bind('mousemove',volumeMove)
.bind('mouseup', removeMouseMove);
});



// MUTE
mute.find('span').bind('click', function() {
if (video.muted) {
video.setMuted(false);
mute.removeClass('dtsplayer-unmute').addClass('dtsplayer-mute');
positionVolumeHandle(1);
} else {
video.setMuted(true);
mute.removeClass('dtsplayer-mute').addClass('dtsplayer-unmute');
positionVolumeHandle(0);
}
});

// FULLSCREEN
var isFullScreen = false;
var normalHeight = 0;
var normalWidth = 0;
fullscreen.bind('click', function() {

if (video.isFlash) {
video.setFullscreen(!isFullScreen);
} else {

if (isFullScreen) {

container.removeClass('dtsplayer-container-fullscreen');
container.width(normalWidth).height(normalHeight);
$v.width(normalWidth).height(normalHeight);
setRailSize();
fullscreen.removeClass('dtsplayer-unfullscreen').addClass('dtsplayer-fullscreen');

} else {
// store
normalHeight = $v.height();
normalWidth = $v.width();

// make full size
container.addClass('dtsplayer-container-fullscreen');
container.width('100%').height('100%');
$v.width('100%').height('100%');

setRailSize();
fullscreen.removeClass('dtsplayer-fullscreen').addClass('dtsplayer-unfullscreen');
}
}
isFullScreen = !isFullScreen;
});

// time rail
timeRail.delegate('span','click',function (e) {
// mouse position relative to the object!
var x = e.pageX;
var offset = timeTotal.offset();
var width = timeTotal.outerWidth();
var percentage = ((x - offset.left) / width);
var newTime = percentage * video.duration;

video.setCurrentTime( newTime );
});


// attach events to <video>
video.addEventListener('timeupdate', function(e) {
// update current:duration
currentTime.html( formatTime(video.currentTime) );
if (video.duration)
duration.html( formatTime(video.duration) );

// update time bar
timeCurrent.width( timeTotal.width() * video.currentTime/video.duration );

}, true);

// bytes loaded
// doeesn't work for local HTML files, but does work online
video.addEventListener('progress', function(e) {

var percent = (e.loaded / e.total);

// update loaded bar
timeLoaded.width( timeTotal.width() * percent );

}, true);


video.addEventListener('click', function(e) {

if (video.paused)
video.play();
else
video.pause();

}, true);
video.addEventListener('play', function(e) {
playpause.removeClass('dtsplayer-play').addClass('dtsplayer-pause');
}, true);
video.addEventListener('pause', function(e) {
playpause.removeClass('dtsplayer-pause').addClass('dtsplayer-play');
}, true);
video.addEventListener('ended', function(e) {
playpause.removeClass('dtsplayer-pause').addClass('dtsplayer-play');
}, true);

// event testing
/*
var events = 'loadeddata loaded playing play pause paused seeking canplay seeking seeked waiting stalled error'.split(' ');
for (var i in events)
video.addEventListener(events[i], function(e) { console.log(e.type, e); }, false);
*/
}



return {
play: function() {
video.play();
},
pause: function() {
video.pause();
}
}

}

})(jQuery);
