// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
// Portions ©2008-2010 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
sc_require('views/controls');
sc_require('views/mini_controls');
/**
@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({
/**
Audio view className.
@property {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.
@property {Array}
*/
displayProperties: ['value', 'shouldAutoResize'],
/**
Reference to the audio object once is created.
@property {Object}
*/
audioObject:null,
/**
Array containing the technologies and the order to load them depending
availability
@property {Array}
*/
degradeList: ['html5','quicktime', 'flash'],
/**
Current time in secs
@property {Number}
*/
currentTime: 0,
/**
Duration in secs
@property {Number}
*/
duration: 0, //audio duration in secs
/**
Volume. The value should be between 0 and 1
@property {Number}
*/
volume:0, //volume value from 0 to 1
/**
Tells you if the audio is paused or not.
@property {Boolean}
*/
paused: YES, //is the audio paused
/**
Tells you if the audio is loaded.
@property {Boolean}
*/
loaded: NO, //has the audio loaded
/**
Indicates if the audio has reached the end
@property {Boolean}
*/
ended: NO, //did the audio finished playing
/**
Indicates if the audio is ready to be played.
@property {Boolean}
*/
canPlay: NO, //can the audio be played
loadedTimeRanges:[], //loaded bits
/**
Formatted currentTime. (00:00)
@property {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 appropiate 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;
}
break;
case "quicktime":
if(SC.browser.msie){
context.push(' ');
}
context.push(''+
'');
this.loaded='quicktime';
return;
case "flash":
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 neccesary audio DOM elements.
@returns {void}
*/
addAudioDOMEvents: function() {
var audioElem, view=this;
audioElem = this.$('audio')[0];
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.set('currentTime', audioElem.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, 'loadedmetadata', this, function () {
SC.run(function() {
// view.set('audioWidth', audioElem.audioWidth);
// view.set('audioHeight', audioElem.audioHeight);
});
});
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