// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2011 Strobe Inc. and contributors.
// Portions ©2008-2011 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
sc_require('views/controls');
sc_require('views/mini_controls');
sc_require('media_capabilities');
/**
@class
Renders a audioView using different technologies like HTML5 audio tag,
quicktime and flash.
This view wraps the different technologies so you can use one standard and
simple API to play audio.
You can specify and array with the order of how the technologies will degrade
depending on availability. For example you can set degradeList to be
['html5', 'flash'] and it will load your audio in an audio tag if the
technology is available otherwise flash and if neither of the technologies
are available it will show a message saying that your machine needs to install
one of this technologies.
@extends SC.View
@since SproutCore 1.1
*/
SC.AudioView = SC.View.extend(
/** @scope SC.AudioView.prototype */{
/**
Audio view className.
@type String
*/
classNames: 'sc-audio-view',
/**
Properties that trigger a re render of the view. If the value changes, it
means that the audio url changed.
@type Array
*/
displayProperties: ['value', 'shouldAutoResize'],
/**
Reference to the audio object once is created.
@type Object
*/
audioObject:null,
/**
Array containing the technologies and the order to load them depending
availability
@type Array
*/
degradeList: ['html5','quicktime', 'flash'],
/**
Current time in secs
@type Number
*/
currentTime : function(key, value) {
if (!SC.empty(value) && this._currentTime != value) {
this._currentTime = value;
this.seek(value);
}
return this._currentTime;
}.property('_currentTime'),
/**
Current time in secs
@type Number
@private
*/
_currentTime : 0,
/**
Duration in secs
@type Number
*/
duration: 0, //audio duration in secs
/**
Volume. The value should be between 0 and 1
@type Number
*/
volume:0, //volume value from 0 to 1
/**
Tells you if the audio is paused or not.
@type Boolean
*/
paused: YES, //is the audio paused
/**
Tells you if the audio is loaded.
@type Boolean
*/
loaded: NO, //has the audio loaded
/**
Indicates if the audio has reached the end
@type Boolean
*/
ended: NO, //did the audio finished playing
/**
Indicates if the audio is ready to be played.
@type Boolean
*/
canPlay: NO, //can the audio be played
loadedTimeRanges:[], //loaded bits
/**
Formatted currentTime. (00:00)
@type String
*/
time: function(){
var currentTime=this.get('currentTime'),
totaltimeInSecs = this.get('duration');
var formattedTime = this._addZeros(Math.floor(currentTime/60))+':'+this._addZeros(Math.floor(currentTime%60))+"/"+this._addZeros(Math.floor(totaltimeInSecs/60))+':'+this._addZeros(Math.floor(totaltimeInSecs%60));
return formattedTime;
}.property('currentTime', 'duration').cacheable(),
/**
Renders the appropriate HTML according for the technology to use.
@param {SC.RenderContext} context the render context
@param {Boolean} firstTime YES if this is creating a layer
@returns {void}
*/
render: function(context, firstTime) {
var i, j, listLen, pluginsLen, id = SC.guidFor(this);
if(firstTime){
for(i=0, listLen = this.degradeList.length; i');
this.loaded='html5';
return;
case "quicktime":
if(!SC.mediaCapabilities.get('isQuicktimeSupported'))
{
break;
}
// TODO: this doesn't seem like the best way to determine what tags to use!
if(SC.browser.name === SC.BROWSER.ie){
context.push(' ');
}
context.push(''+
'');
this.loaded='quicktime';
return;
case "flash":
if(!SC.mediaCapabilities.get('isFlashSupported'))
{
break;
}
var flashURL= sc_static('videoCanvas.swf');
var movieURL = this.get('value');
if (!movieURL) return;
if(movieURL.indexOf('http:')==-1){
movieURL=location.protocol+'//'+location.host+movieURL;
}
if(movieURL.indexOf('?')!=-1){
movieURL=movieURL.substring(0, movieURL.indexOf('?'));
}
movieURL = encodeURI(movieURL);
context.push('');
this.loaded='flash';
SC.AudioView.addToAudioFlashViews(this);
return;
default:
context.push('audio is not supported by your browser');
return;
}
}
}
},
valueObserver:function(){
this.set('currentTime', 0);
this.set('duration', 0);
this.set('volume', 0);
this.set('paused', YES);
this.set('loaded', NO);
this.set('ended', NO);
this.set('canPlay', NO);
this.set('loadedTimeRanges', []);
this.replaceLayer();
}.observes('value'),
/**
In didCreateLayer we add DOM events for audio tag or quicktime.
@returns {void}
*/
didCreateLayer :function(){
if(this.loaded==="html5"){
this.addAudioDOMEvents();
}
if(this.loaded==="quicktime"){
this.addQTDOMEvents();
}
},
didAppendToDocument :function(){
if(this.loaded==="quicktime"){
this.addQTDOMEvents();
}
},
/**
Adds all the necessary audio DOM elements.
@returns {void}
*/
addAudioDOMEvents: function() {
var audioElem, view=this;
audioElem = this.$('audio')[0];
audioElem.volume = this.get('volume');
this.set('audioObject', audioElem);
SC.Event.add(audioElem, 'durationchange', this, function () {
SC.run(function() {
view.set('duration', audioElem.duration);
});
}) ;
SC.Event.add(audioElem, 'timeupdate', this, function() {
SC.run(function() {
view._currentTime = audioElem.currentTime;
view.propertyDidChange('currentTime');
});
});
SC.Event.add(audioElem, 'loadstart', this, function () {
SC.run(function() {
view.set('volume', audioElem.volume);
});
});
SC.Event.add(audioElem, 'play', this, function () {
SC.run(function() {
view.set('paused', NO);
});
});
SC.Event.add(audioElem, 'pause', this, function () {
SC.run(function() {
view.set('paused', YES);
});
});
SC.Event.add(audioElem, 'canplay', this, function () {
SC.run(function() {
view.set('canPlay', YES);
});
});
SC.Event.add(audioElem, 'ended', this, function () {
SC.run(function() {
view.set('ended', YES);
});
});
SC.Event.add(audioElem, 'progress', this, function (e) {
SC.run(function() {
this.loadedTimeRanges=[];
for (var j=0, jLen = audioElem.seekable.length; j