/*
* Speaker Version 0.7
*
* Copyright 2011, Pawel Dobierski
* Dual licensed under the MIT or GPL Version 2 licenses.
* 
* 
* Requires jQuery Liblary 1.3 or higher
* Requires swfobject Liblary 2 or higher
*/ 

var speaker = function(){
    // Dom variables
    var W = window,D=document,N=navigator,
    // Regular expressions
    regex = {
        // Empty string
        empty:/^[\s\n\r\t]*$/gi,
        // Used for getting index
        getIndex:/[^0-9]+/g
    },
    // Number of players
    nop = 0,
    // Public data of registered players
    data = [],
    // Error data
    errors = [],
    // Application status
    appStatus=0,
    // Current player
    current=0,
    // Use HTML Audio
    useHtmlAudio=false,
    // Names of DOM nodes
    panelName,fldivName,flObjName='',
    // Application name (must be the same as singleton name)
    appName='speaker',
    // Default Options when creating new player
    opt = {
        // Default host
    	appHost:'',
    	// Parent ID ???
    	// TODO:: REMOVE IT FROM HERE!!!!
    	parentID:null,
    	// Path to speaker.swf flash player
    	flashPath:"flash/speaker.swf?v=0.1",
    	// Default HTML for STOP button
    	stopHTML:'<span class="speaker speakerStop"></span>',
    	// Default HTML for PLAY button
    	playHTML:'<span class="speaker speakerPlay"></span>',
    	// Accessibility description for PLAY button
    	accPlay:'Listen: ',
    	// Accessibility description for STOP button
    	accStop:'Stop: ',
    	// Accessibility OPEN tag
    	accOpen:'<span style="position:fixed;top:-9999px;left:1px;width:1px;height:1px" >',
    	// Accessibility CLOSE tag 
    	accClose:'</span>',
    	onLoadEvent:function(){},
    	panelClass:'abs speakerPanelClass'
    }; 

       
   
    // Configuration of global parameters
    function configure(k){
    	if(!k) k = opt;else{for(var dfo in opt){if(isSet(k[dfo])) opt[dfo] = k[dfo];}}
    }
    // Helper function for variable checking
    function isSet(a){
    	if(typeof a != 'undefined') return true;
    	else return false;
    }
    function isString(a){
        if((typeof a).toLowerCase() == 'string') return true;
        else return false;
    }
    function isObject(a){
        if((typeof a).toLowerCase() == 'object') return true;
        else return false;
    }
    
    /*
     @param a - Player data 
    
     Returns HTML of player anchors with accessibility description
    
    */ 
    function buildLinks(a){
        a.title = a.title||'';
        //Opera Mini run under iPhone and Blackberry has problems with displaying absolute positioned elements, heh :)
        if(!isOperaMini){
            a.linkPlay = ( isSet(a.playHTML)?a.playHTML:opt['playHTML'])+opt['accOpen']+opt['accPlay']+a.title+opt['accClose'];
        	a.linkStop = ( isSet(a.stopHTML)?a.stopHTML:opt['stopHTML'])+opt['accOpen']+opt['accStop']+a.title+opt['accClose'];
        // All other browsers
        }else{
            a.linkPlay = ( isSet(a.playHTML)?a.playHTML:opt['playHTML']);
        	a.linkStop = ( isSet(a.stopHTML)?a.stopHTML:opt['stopHTML']);
        }
        
    }
    /*
 
     @param a - url String
     @param b - title String
     @param c - options Dictionary
 
 
     @return nop Int - number of the player
     */
    function create(a,b,c){
        
        // Unesacape url if it is escaped
    	if(a.indexOf('http%3A') != -1 || a.indexOf('https%3A') != -1 ) a = unescape(a);
    	
    	// Increment number of all players
    	nop++;
    	
        var playerDefaultOptions = {   
            // Override this for custom Callbacks
            // Remember to call speaker.play and speaker.stop
            // with current player index returned by this function
            playCallback:null,
            stopCallback:null,
            soundEnded:function(){},  
            parentID:null
        }
        // Declare new options object
        var options;
        // If second parameter is a string
        if(isString(b)){
            c = c||{}
            options = $.extend(true,playerDefaultOptions,c);
            options.title = b;
            //options.url = a;
        }else if(isObject(b)){
            options = $.extend(true,playerDefaultOptions,b);
        }
        // Assign url
        options.url = a;
        // Assign Element ID atrribute
        options.linkID = appName+'_playLink'+nop;
        // Set state to stopped 
        options.isPlaying = 0;
         
        
        
    	
    	//Create new array for the player
    	data[nop] = {};
    	
    	
	    // Merge options 
	    $.extend(true,data[nop],options);
        
	    // Build links with accessibility description
	    // This one appends linkPlay and linkStop to data[nop]
    	buildLinks(data[nop]);

    	
    	// Assign players HTML to a variable
    	var playerHTML = ''+
    	'<span id="'+appName+'_playerNo'+nop+'" class="accCont" style="position:relative"> '+
	        '<a id="'+data[nop].linkID+'" style="position:relative" '+'href="'+(noFlashAndHtmlAudio?a:'javascript:void null')+'" >'+
	        data[nop].linkPlay+
	        '</a>'+
	    '</span>';
        
        // Embedding to parent ID
        if(data[nop].parentID!=null){
            $("#"+data[nop].parentID).html(playerHTML)
        // Embedding in place of script execution
        }else{
            document.write(playerHTML);
        }
    	
    	// Create HTML5 Audio Object   
	    if(useHtmlAudio){
	        
            var myau = data[nop].html5audio = document.createElement('audio');
            myau.id = "speakerAudioObj_"+nop;
            
            if(!regex.empty.test(a)){

                myau.setAttribute('src',a);
                myau.load();
            }
            // Add Event Listeners
            _html_addListeners(myau);
            
            // Is this really needed ???
            $("#"+appName+'_playerNo'+nop).append(myau);

        }else{
            data[nop].html5audio = false; 
        } 

    	// Custom callback case
    	if( $.isFunction(data[nop].playCallback) ){
        	$("#"+data[nop].linkID).bind("click",function(){
        	    speaker.data[getPlayerIndex(this.id)].playCallback();
        	});
        // Standard way	   
    	}else{
    	    $("#"+data[nop].linkID).bind("click",function(){
        	    speaker.play(getPlayerIndex(this.id));
        	});
    	}    
	    return nop;
    }
    // Returns player's number from player's link id
    function getPlayerIndex(id){
        return parseInt(id.replace(regex.getIndex,''));
        //return parseInt(id.toString().replace(appName+'_playLink',''))
    }
    function _ui_updateStopped(a /* Player ID */){
        // Set PLAY HTML
        $("#"+data[a].linkID).html(data[a]['linkPlay']);
        // Unbind Events For HTML button
        $("#"+data[a].linkID).unbind("click");
        // Perform Stop Action
        if($.isFunction(data[a].playCallback)){
            $("#"+data[a].linkID).bind("click",function(){ 
                speaker.data[getPlayerIndex(this.id)].playCallback();
            });
        }else{
            $("#"+data[a].linkID).bind("click",function(){
                speaker.play(getPlayerIndex(this.id));
            });
        }
        data[a].isPlaying = 0;  
    	
    }
    function _ui_updatePlaying(a /* Player ID */){
        // Set STOP HTML
        $('#'+appName+'_playLink'+a).html(data[a].linkStop);
        // Unbind Events For HTML button
        $("#"+data[a].linkID).unbind("click");
        
        // Perform Stop Action
        if($.isFunction(data[a].stopCallback)){
            $("#"+data[a].linkID).bind("click",function(){ 
                speaker.data[getPlayerIndex(this.id)].stopCallback();
            });
        }else{
            $("#"+data[a].linkID).bind("click",function(){
                speaker.stop(getPlayerIndex(this.id));
            });
        }
        
        data[a].isPlaying = 1;
          	
    	
    }
    
    /**

    @oaram b - current player

    */
    function play(b){
        for(var i=1;i<data.length;i++){
    	    if(i != b){
    		    _html_stop(i);
    			_ui_updateStopped(i);
    		}else{
    		    // Jesli HTML5 Audio
        	    if(useHtmlAudio){
        	        _html_play(i);
        	        _ui_updatePlaying(i);
                // Jesli Flash
                }else{
                    _flash_loadAudio(data[i].url,b);
                    _ui_updatePlaying(i); 
                }
                
    		}
    	}
    	// Set current
    	current = b;
    	
	
    }
    function stop(b){
        // Jesli HTML5 Audio
	    if(useHtmlAudio){
	        _html_stop(b);
	        _ui_updateStopped(b);
	        if( $.isFunction(data[b]['soundEnded']) )  data[b]['soundEnded']();
	    }else{
	        _flash_stopAudio();
	        for(var a=1;a<data.length;a++){   
                _ui_updateStopped(a);
        	}
	    }
	    // Options Callbacks
		// Sound Ended
		//
		
		
	    // Set current to 0
	    current = 0;
	}
    
    /**
     *
     * HTML5 Functions
     *
     **/
    function _html_play(a){
        _html_getAudioAtIndex(a).play();
    }
    function _html_pause(a){
        var au = _html_getAudioAtIndex(a)
        au.pause();
    }
    function _html_stop(a){
        var au = _html_getAudioAtIndex(a)

        if(data[a].isPlaying && !!(_html_getAudioAtIndex(a)) ){  
            try{                   
                au.pause();
                au.currentTime = 0;
                
                if(au.currentTime!=0){
                    au.src = au.src;
                    
                }
                
            }catch(e){
                au.src = au.src;
                 
            } 
        }
    }
    function debug(a){
        alert(a);
    }
    function _html_getAudioAtIndex(a){
        return data[a]['html5audio'];
    }
    function _html_getIndexFromEvent(e){
        return (getPlayerIndex(e.target.id))
    }
    function _html_getAudioFromEvent(e){
        var i = _html_getIndexFromEvent(e);
        return data[i]['html5audio'];
    }
    function _html_addListeners(a){
        var f = function(x,y){
            a.addEventListener(x,y,false)
        };
         
        f("timeupdate",function(event){
            //pr('timeupdate');
        });
        f("progress",function(event){
            //pr('progress');
        });
        f("durationchange",function(event){
            //pr(_html_getIndexFromEvent(event));
        });
		f("play",function(event){
		    //pr('play');
		});
		f("playing",function(event) {
		    //pr('playing');
		});
		f("pause",function(event) {
		    //pr('pause');
		});
		f("waiting",function(event) {
		    //pr('waiting');
		});
		f("canplay",function(event) {
		    //pr('canplay');
		});
		f("seeking",function(event) {
		    //pr('seeking');
		});
		f("seeked",function(event) {
		    //pr('seeked');
		});
		f("suspend",function(event) {
		    //pr('suspend');
		});
		f("ended",function(event) {
		    //var au = _html_getAudioFromEvent(event);
		    var i = _html_getIndexFromEvent(event)
		    _html_stop(i);
		    
		    speaker.soundEnded(i);
		});
		f("error",function(event){
		});
    }
    /**
     *
     * Flash Functions
     *
     **/
    function _flash_loadAudio(a,b){
        //pr('flashload');  
        getFlash(flObjName).loadAudio(a,b);
        // Call custom onLoadEvent
    	opt.onLoadEvent();
    	
    }
    function _flash_stopAudio(){
        getFlash(flObjName).stopAudio();
    }
    // Get flash object
    function getFlash(mN){
    	if (D.getElementById(mN) && D.getElementById(mN) != 'undefined') return D.getElementById(mN);
    	else{
    		if (window.document[mN] && window.document[mN] != 'undefined') return window.document[mN];
    		if (!isIE && D.embeds && document.embeds[mN] != 'undefined') return D.embeds[mN];
    	}
    	return null;
	
    }
    // User agent checking
    function ua(v){
    	if(navigator.userAgent.toLowerCase().indexOf(v) != -1) return true;
    	else return false;
    }
    // User app version checking
    function uav(v){
    	if(navigator.appVersion.toLowerCase().indexOf(v) != -1) return true;
    	else return false;
    }
    function detectHTML5Audio(){
        var a = document.createElement('audio');
        var mp3 = !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
        var aac = !!(a.canPlayType && a.canPlayType('audio/mp4; codecs="mp4a.40.2"').replace(/no/, ''));
        var wav = !!(a.canPlayType && a.canPlayType('audio/wav; codecs="1"').replace(/no/, ''));
        var ogg = !!(a.canPlayType && a.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''));
        return mp3;
    }
    // Flash Detect library (BSD, (C) 2007) by Carl "DocYes" S. Yestrau
    // http://featureblend.com/javascript-flash-detection-library.html / http://featureblend.com/license.txt
    function detectFlash() {
        
        var a = false, n = navigator, nP = n.plugins, obj, type, types, AX = W.ActiveXObject;

        if (nP && nP.length) {
          type = 'application/x-shockwave-flash';
          types = n.mimeTypes;
          if (types && types[type] && types[type].enabledPlugin && types[type].enabledPlugin.description) {
            a = true;
          }

        }else if (isSet(AX)) {
          try {
            obj = new AX('ShockwaveFlash.ShockwaveFlash');
          } catch(e) {}
          a = (!!obj);

        }
        return !!(a);

    };
    // Set up environment variables like browser settings etc
    (function(){

    	// Detect Internet Explorer
    	isIE = uav("msie");
    	// Detect Opera 
    	isOpera = ua("opera") ? true : false;
    	// Detect Opera Mini
    	isOperaMini = (ua("opera") && ua("mini")) ? true : false;
    	// Detect Webkit Engine   
    	isWK = ua("applewebkit");
    	 
    	//isMOB = (isAMB || isWMB) ? true : false;
    	isAndroid = uav("android");
    	isFlash = detectFlash();
    	isHTML5 = detectHTML5Audio(); 
    	useHtmlAudio = (!isFlash||isWK||isAndroid)&&isHTML5; 
    	
    	// If client doesent have flash or html5 support
    	noFlashAndHtmlAudio = (!isFlash && !useHtmlAudio);
    	
    	if(isFlash){
    	    try{ //Checking for required liblaries
                if(typeof $ == 'undefined'){ throw new Error("Speaker requires jQuery as window.$");}
                if(typeof swfobject == 'undefined'){ throw new Error("Speaker requires swfObject as window.swfobject");}
            }catch(e){alert(e);return{};}
    	}
    	
       
    	
    	

    })();
    
    //DomnLoadEvent
    if(typeof SpeakerAsyncInit != 'function' && typeof SpeakerAsyncCommands == 'undefined'){
        // Append hidden flash audio manager
        $(D).ready(function(){
            speakerOnDomLoadFunction();
        });            
    } else {
        try{setTimeout(function(){
            speakerOnDomLoadFunction();
        },500)}catch(e){}
    }

    
    function speakerOnDomLoadFunction(){
        
        
        
        panelName = appName+"Panel";
		fldivName = appName+"FlDiv";
		flObjName = appName+"FlObj";

		var body = document.body;
		var panel = document.createElement("div");
		var fldiv = document.createElement("div");

		panel.id = panelName;
		panel.className = opt.panelClass;

		fldiv.id = fldivName;
		fldiv.className = "k ";

		panel.appendChild(fldiv);
		body.appendChild(panel);

        if(!useHtmlAudio && isFlash){
            var flashvars = {};
    		var attributes = {
    		    id:flObjName,
    		    name:flObjName
    		};
    		var params = {
    		    quality:"low",
    		    swliveconnect:"true",
    		    allowscriptaccess:"always"
    		};
            if (swfobject.hasFlashPlayerVersion("10")){	
    			isFlash = 1;
    			swfobject.embedSWF(opt['appHost']+opt['flashPath'], fldivName, "1", "1", "10.0.0",'', flashvars, params, attributes);
    		}
    		else{
    			isFlash=0;
    		}
        }
        
          
    }
      
    // Public object properties
    return{
    	create:function(a,b,c){
    	    
    		return create(a,b,c)
    	},
    	stop:function(a){
    		stop(a)
    	},
    	play:function(a){
    		play(a)
    	},
    	config:function(k,v){
    	    
    		configure(k,v)
    	},
    	/*  Update player
    	    @param a Int- number of player 
    	    @param b String - sound url
    	    @param c Dict - options
    	*/
    	update:function(a,b,c){
    	    // If there is no player at index a,  return silently
            if(!isSet(data[a])) return;
            //Update sound url if not empty
    	    if(!regex.empty.test(b)){
    	        data[a]['url'] = b;
    	        // Update HTML5
    	        if(useHtmlAudio){
    	            audio = _html_getAudioAtIndex(a);
    	            $(audio).attr('src',b);
    	        }
    	        // No flash or HTML5 Support
    	        if(noFlashAndHtmlAudio){
    	            $("#"+data[a]['linkID']).attr("href",b);
    	        }
    	    }
    	    // Options
    	    if(isSet(c)){
    	      // Extend players object with new one  
    	      $.extend(true,data[a],c);
    	      // If user is updating PlayHTML or stopHTML attributes
    	      if(isSet(c.playHTML) || isSet(c.stopHTML)){
    	          buildLinks(data[a]);
    	          if(data[a]['isPlaying']){
    	              $('#'+appName+'_playLink'+a).html(data[a]['linkStop']);
	              }else{
	                  $('#'+appName+'_playLink'+a).html(data[a]['linkPlay']);
	              }
          	  }
          	  // If user forces player to play on update
    	      if(isSet(c.shouldPlay) && c.shouldPlay==true){
  		        play(a);
  		      }else{
  		          stop(a);
  		      }
    	        
    	    }
    	},
    	soundEnded:function(a){
    	    a = a||current;
    		_ui_updateStopped(a);
    		// Options Callbacks
    		// Sound Ended
    		if( $.isFunction(data[a]['soundEnded']) )  data[a]['soundEnded']();
    		
		    
    	},
    	data:data
    }          
	
}();
// Invoke asynchronous loading function if exists
if(typeof window.SpeakerAsyncInit == 'function'){
    SpeakerAsyncInit();
}

