//*****************************
// Tools
//*****************************
if (!Array.prototype.forEach)
{
    Array.prototype.forEach = function(fun /*, thisp*/)
    {
        var len = this.length >>> 0;
        if (typeof fun != "function")
            throw new TypeError();
        
        var thisp = arguments[1];
        for (var i = 0; i < len; i++)
        {
            if (i in this)
            fun.call(thisp, this[i], i, this);
        }
    };
}

function makeid()
{
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for( var i=0; i < 10; i++ )
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}

// parseUri 1.2.2
// (c) Steven Levithan <stevenlevithan.com>
// MIT License
function parseUri (str) {
    var o   = parseUri.options,
        m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
        uri = {},
        i   = 14;

    while (i--) uri[o.key[i]] = m[i] || "";

    uri[o.q.name] = {};
    uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
        if ($1) uri[o.q.name][$1] = $2;
    });

    return uri;
};

parseUri.options = {
    strictMode: false,
    key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
    q:   {
        name:   "queryKey",
        parser: /(?:^|&)([^&=]*)=?([^&]*)/g
    },
    parser: {
        strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
        loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
    }
};


//*****************************
// Communication API
//*****************************

// API - global object
var assemblive = {
    serverCommId : makeid(),

    _debug: true,
    _callbacks : [],
    _hasHTML5PostMessage : !!window.postMessage,
    _console : undefined,
    _connectionTimer: undefined,
    _bridgeFlash : undefined,
    _site: "http://www.assemblive.com",

    // Internal: console
    _initializeConsole : function()
    {
        var c = new Object();
        c.log = c.warn = c.error = c.debug = function(){};
        
        if( "console" in window && assemblive._debug )
        {
            c = window.console;
            if( !( "debug" in c ) )
                c.debug = c.log;
        }
        return c;
    },

    // Internal: debug method
    _log : function( msg )
    {
        if( assemblive._console == undefined )
            assemblive._console = assemblive._initializeConsole();
        assemblive._console.debug( "Assemblive client: " + msg );
    },

    // Register a callback notified when assemblive message is received
    subscribe : function(fn)
    {
        assemblive._callbacks.push(fn);
    },

    // Unregister callback
    unsubscribe : function(fn) 
    {
        assemblive._callbacks = assemblive._callbacks.filter( function(el) {
                if ( el !== fn )
                    return el;
                });
    },

    // Internal: dispatch event to all subscribers
    _fire : function(o, thisObj)
    {
        var scope = thisObj || window;
        assemblive._callbacks.forEach( function(el) {
                el.call(scope, o);
                });
    },

    // Internal: called when a message is received from assemblive, through HTML5 postmessage() method
    _onJsonMessage : function(e)
    {
        data = JSON.parse( e.data );
        assemblive._onMessage( data );
    },

    // Internal: called when a message is received from assemblive
    _onMessage : function(data)
    {
        assemblive._log( data.cmd + " - " + data.args );
        if( data.cmd == "onWebComponentRegistered" && assemblive._connectionTimer != undefined )
        {
            clearInterval( assemblive._connectionTimer );
            assemblive._connectionTimer = undefined;
        }
        assemblive._fire( data );
    },

    _onSwfObjectCreated : function( e )
    {
        if( e.success )
            assemblive._bridgeFlash = e.ref;
    },

    // Communication initiation, by registering to assemblive
    //  - using HTML5 postmessage() for recent browsers
    //  - using a Flash fallback on dinosaurs browsers (IE6/7)
    listenToEvents: function( url)
    {
        assemblive.dependences.checkAll();
        url = url || assemblive._site;
        if( assemblive._hasHTML5PostMessage )
        {
            assemblive._log( "listenToEvents - postMessage" );
            if( window.addEventListener )
                window.addEventListener("message", assemblive._onJsonMessage, false);
            else if( window.attachEvent )
                window.attachEvent("onmessage", assemblive._onJsonMessage);
            assemblive._connectionTimer = setInterval( "assemblive.send( 'register', '' )", 500 );
        }
        else
        {
            assemblive._log( "listenToEvents - flash" );
            
            var divContainer = document.createElement("div");
            divContainer.setAttribute( "id", "assembliveBridgeFlashClient" );
            document.body.appendChild( divContainer );
            
            var flashvars = { serverCommId: assemblive.serverCommId };
            var params = { allowscriptaccess: "always" };
            swfobject.embedSWF( url + "/static/api/BridgeClient.swf", "assembliveBridgeFlashClient", "1", "1", "10.0.0","expressInstall.swf", flashvars, params, null, assemblive._onSwfObjectCreated );
        }
    },

    send: function( cmd, args )
    {
        if( assemblive._hasHTML5PostMessage )
        {
            var msg = { cmd: cmd, args: args };
            window.frames["assembliveContent"].postMessage( JSON.stringify( msg ), '*' );
        }
        else if( assemblive._bridgeFlash )
            assemblive._bridgeFlash.send( cmd, args );
    }
}

//
// Fallback flash bridge communication
// Method called by the flash bridge
//
assemblive.flashBridge = {
    debug: function( msg )
    {
        assemblive._log( "FlashClientBridge: " + msg );
    },

    onConnected: function()
    {
        var msg = { cmd: "onWebComponentRegistered", args:  "" };
        assemblive._onMessage( msg );
    },

    onMessage : function( cmd, args )
    {
        var msg = { cmd: cmd, args: args };
        assemblive._onMessage( msg );
    },

    onBridgeReady : function()
    {
        document.getElementById( "assembliveBridgeFlashClient" ).start();
    }
}

//*****************************
// Embedding API
//*****************************

assemblive.widget = {    
    //
    // Tools
    //
    
    // Prefix all ASSEMBLIVE query keys
    _getKey: function(key)
    {
        return "ae." + key;
    },

    _buildUrl: function( url )
    {
        var query = window.location.search.substring( 1 );
        var output = parseUri( url );
        var nocache = assemblive.widget._getKey("no.cache") + "=" + new Date().getTime();
        if( output.query != null && output.query != '' ) {
            url = url + '&' + nocache;
            if ( query != null && query != '' )
                url = url + '&' + query;
        } else {
            url = url + '?' + nocache;
            if ( query != null && query != '' )
                url = url + '&' + query; 
        }
        return url;        
    },

    _buildIFrame: function( url, width, height )
    {    
        //$$$ It sucks - use document.createElement('IFRAME') ...  <=== Pb with backward compatibility
        document.write('<iframe id="assembliveContent" name="assembliveContent" src="' + url + '" width="' + width + '" height="' + height + '" frameborder="0" allowTransparency="true" scrolling="no">\n');
        document.write('</iframe>\n');
    },

    //
    // Event list widget
    //
    _buildEventListUrl: function( url, code, interval, target, locale )
    {
        var fullUrl = assemblive.widget._buildUrl( url );
        fullUrl += "&" + assemblive.widget._getKey("user.code") + "=" + code;
        fullUrl += "&" + assemblive.widget._getKey("interval") + "=" + interval;
        fullUrl += "&" + assemblive.widget._getKey("target") + "=" + target;
        if ( locale != undefined )
            fullUrl += "&" + assemblive.widget._getKey("locale") + "=" + locale;
        return fullUrl;
    },
    
    /**
     * buildEventList( { url:"http://www.assemblive.com/widget", xxx } ), with following parameters
     *   - url (default: "http://www.assemblive.com/widget")
     *   - width (default: 250)
     *   - height (default: 250)
     *   - code (default: "default")
     *   - interval (default: 10)
     *   - target (default: "_blank")
     */
    buildEventListWidget: function( params )
    {
        params = params || {};
        params.width = params.width || 250;
        params.height = params.height || 250;
        params.url = params.url || assemblive._site + "/widget";
        params.code = params.code || "assemblive";
        params.interval = params.interval || 10;
        params.target = params.target || "_blank";
        assemblive.widget._buildIFrame( assemblive.widget._buildEventListUrl( params.url, params.code, params.interval, params.target, params.locale ), params.width, params.height );
    },
    
    //
    // Event widget
    //
    _buildEventWidgetUrl: function( url, code, logoutUrl, locale, user )
    {
        var fullUrl = assemblive.widget._buildUrl( url );
        if( code != undefined )
            fullUrl += "&" + assemblive.widget._getKey("event.code") + "=" + code;
        if( logoutUrl != undefined )
            fullUrl += "&" + assemblive.widget._getKey("logout.next") + "=" + encodeURIComponent( logoutUrl );
        if ( locale != undefined )
            fullUrl += "&" + assemblive.widget._getKey("locale") + "=" + locale;
        if ( user != undefined )
            fullUrl += "&" + assemblive.widget._getKey("user.code") + "=" + user;
        fullUrl += "&" + assemblive.widget._getKey("commId") + "=" + assemblive.serverCommId;
        return fullUrl;
    },
    
    /**
     * buildEventWidget( { url:"http://www.assemblive.com/widget", xxx } ), with following parameters
     *   - url (default "http://www.assemblive.com/internal/event/")
     *   - width (default "100%")
     *   - height (default "100%")
     *   - code (optional)
     *   - logoutUrl (optional)
     *   - locale (optional)
     */
    buildEventWidget: function( params )
    {
        params.url = params.url || assemblive._site + "/internal/event/";
        params.width = params.width || "100%";
        params.height = params.height || "100%";
        assemblive.widget._buildIFrame( assemblive.widget._buildEventWidgetUrl( params.url, params.code, params.logoutUrl, params.locale, params.user ), params.width, params.height );
    }    
}

//**********************************
// Legacy API Compatibility code
//**********************************
function buildEventListWidget( url, code, interval, target, width, height )
{
    params = {};
    params.url = url;
    params.code = code;
    params.interval = interval;
    params.target = target;
    params.width = width;
    params.height = height;
    assemblive.widget.buildEventListWidget( params )    
}

function buildEventListWidget2( params )
{
    assemblive.widget.buildEventListWidget( params )
}

function buildEventWidget( url, code, logoutUrl, width, height )
{
    var params = {};
    params.url = url;
    params.code = code;
    params.logoutUrl = logoutUrl;
    params.width = width;
    params.height = height;
    assemblive.widget.buildEventWidget( params );
}

function buildEventWidget2( params )
{
    assemblive.widget.buildEventWidget( params );
}

/**
 * JavaScript dependences for APIs
 */
assemblive.dependences = {

	/**
	 * Check if jQuery is available
	 */
    checkJQuery: function()
    {
        if (typeof jQuery != 'undefined')
        {
        	return true;
        }
        alert("Please include jquery-1.4.2.min.js");
        return false;
    },

	/**
	 * Check if jQuery Timers is available
	 */
    checkJQueryTimers: function()
    {
    	if (!assemblive.dependences.checkJQuery())
    	{
    		return false;
    	}
    	if (typeof jQuery.timer != 'undefined')
    	{
    	    return true;
    	}
        alert("Please include jquery.timers-1.2.js");
        return false;
    },
   
	/**
	 * Check if JSON is available
	 */
    checkJSON: function()
    {
        if (typeof JSON != 'undefined')
        {
            return true
        }
        alert("Please include json2.min.js");
        return false
    },

	/**
	 * Check if SwfObject is available
	 */
    checkSwfobject: function()
    {
        if (typeof swfobject != 'undefined')
        {
        	return true;
        }
        alert("Please include swfobject.js");
        return false;
    },
    
	/**
	 * Check if all dependences are available
	 */
    checkAll: function()
    {
        var flag = assemblive.dependences.checkJQuery();
        flag = flag && assemblive.dependences.checkJQueryTimers();
        flag = flag && assemblive.dependences.checkJSON();
        flag = flag && assemblive.dependences.checkSwfobject();
        return flag;
    }
}

/**
 * LOCALES for ASSEMBLIVE
 */
assemblive.locale = {
	
	/**
	 * English
	 */
    ENGLISH: "en",
    
    /**
     * French
     */
    FRENCH: "fr",
    
    /**
     * Japanese
     */
    JAPANESE: "ja",
    
    /**
     * Simplified Chinese
     */
    SIMPLIFIED_CHINESE: "zh_CN"
}

/**
 * Systems
 */
assemblive.system = {
    
	/**
	 * Check if on Windows
	 */
    isWindows: function()
    {
        var ua = navigator.userAgent.toLowerCase();
        return (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1);
    },

    /**
     * Check if on MacOS
     */
    isMac: function()
    {
        var ua = navigator.userAgent.toLowerCase();
        return (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1);
    },
    
    /**
     * Check if on Intel MacOS
     */
    isMacIntel: function()
    {
        return (navigator.platform == "MacIntel");    
    }
}

/**
 * Plug ins management
 */
assemblive.plugins = {
	
	/**
	 * Check if all required plug ins for ASSEMBLIVE are installed
	 */
    areInstalled: function()
    {
        return assemblive.plugins.flash.isInstalled() && assemblive.plugins.unity.isInstalled();
    },

    /**
     * Refresh plug ins list
     */
    refresh: function()
    {
        navigator.plugins.refresh();    
    }

}

/**
 * Flash plug in management
 */
assemblive.plugins.flash = {
	
	/**
	 * Get the flash installer URL
	 */
    getInstallerUrl: function()
    {
        return "http://www.adobe.com/go/getflashplayer";
    },

    /**
     * Get the icon of the flash installer
     */
    getInstallerIcon: function()
    {
        return assemblive._site + "/api/flash.png";
    },

    /**
     * Get the installer link
     */
    getInstallerLink: function()
    {
    	return '<a href="' + assemblive.plugins.flash.getInstallerUrl() + '" target="_blank"><img src="' + assemblive.plugins.flash.getInstallerIcon() + '"></img></a>';
    },
    
    /**
     * Check if flash version required for ASSEMBLIVE is installed
     */
    isInstalled: function()
    {
    	// Version 10.0 or upper required
        return assemblive.plugins.flash._isInstalled(10, 0)	
    },
    
    /**
     * INTERNAL USE ONLY
     * 
     * Check if version of flash specified with major and minor version is installed
     * Do not use SwfObject as it doesn't refresh the list of installed plug ins
     * 
     * - major: The major version,
     * - minor: The minor version.
     */
    _isInstalled: function(major, minor)
    {
    	if (jQuery.browser.msie)
    	{
            try
            {
                var activeXString = "ShockwaveFlash.ShockwaveFlash." + major.toString();
                var axo = new ActiveXObject(activeXString);
                var desc = axo.GetVariable("$version");
                var descArray = desc.split(" ");
                var tempArray = descArray[1].split(",");

                var versionMajor = parseInt(tempArray[0]);
                var versionMinor = parseInt(tempArray[1]);

                if (versionMajor > major)
                {
                    return true;
                }
                else
                {
                	return ((versionMajor == major) && (versionMinor >= minor));
                }
            }
            catch (e)
            {
            }
            return false;
    	}
    	else
    	{
            if (navigator.plugins != null && navigator.plugins.length > 0)
            {
                var flashPlugin = navigator.plugins["Shockwave Flash"];
                if (!flashPlugin)
                {
                    flashPlugin = navigator.plugins["Shockwave Flash 2.0"];
                }
                if (flashPlugin)
                {
                    var flashDescription = flashPlugin.description;
                    var descArray = flashDescription.split(" ");

                    var tempArrayMajor = descArray[2].split(".");   
                    var versionMajor = parseInt(tempArrayMajor[0]);
                    var versionMinor = parseInt(tempArrayMajor[1]);

                    if (versionMajor > major)
                    {
                        return true;
                    }
                    else
                    {
                        return ((versionMajor == major) && (versionMinor >= minor));
                    }
                }
            }
            return false;
        }
    },
    
    /**
     * Register functions to be notified when flash is not installed and when installation finished
     * 
     * - onInstalled   : Called when installation successful
     * - onNotInstalled: Called when flash is not installed
     */
    register: function(onInstalled, onNotInstalled)
    {
    	// Check if timers are available
    	if (assemblive.dependences.checkJQueryTimers())
    	{
    		// Get a label for the timer
	    	var label = "assemblive.plugins.flash";
	    	// Refresh the plug in list
	    	assemblive.plugins.refresh();
	    	// Check if flash already installed
	    	if (assemblive.plugins.flash.isInstalled())
	    	{
	    		// If already installed and call back defined, notify that flash is installed
	            if (onInstalled)
	            {
	            	// Actually notify
	            	onInstalled();
	            }
	    	}
	    	else
	    	{
	    		// If not installed and call back defined, notify that flash is not installed
	    		if (onNotInstalled)
	    		{
	    			// Actually notify
	    			onNotInstalled();
	    		}
	    		// Add a timer to check if flash installation was performed
	    		$(document).everyTime(2000, label, function() {
	    			// Refresh plug in list before checking
	    			assemblive.plugins.refresh();
	    			// Check if installed
	    	        if (assemblive.plugins.flash.isInstalled())
	    	        {
	    	        	// If installed, notify
	    	        	onInstalled();
	    	        	// Stop timer
	    	            $(document).stopTime(label);
	    	        }
	            });
	    	}
    	}
    },

    /**
     * Check if express install can be used
     */
    hasExpressInstall: function()
    {
    	// Require SwfObject
    	assemblive.dependences.checkSwfobject();
    	// Check if express install can be used
    	// IE. Check if flash >= 6.0.25 installed
        // http://kb2.adobe.com/cps/253/6a253b75.html
    	return swfobject.hasFlashPlayerVersion("6.0.25");
    },
    
    /**
     * Show express install. Installer has a width of 310px and a height of 137px
     * 
     * - id        : The id of the element where the express install will be inserted
     * - onCanceled: Function call if canceled
     */
    showExpressInstall: function(id, onCanceled)
    {
    	// Require SwfObject
    	assemblive.dependences.checkSwfobject();
    	// Get attributes
        var att = { data: assemblive._site + "/api/expressInstall.swf" };
        // Show express install
        swfobject.showExpressInstall(att, {}, id, onCanceled);
    }
    
}

/**
 * Unity plug in management
 */
assemblive.plugins.unity = {
	
	/**
	 * Get Unity installer URL
	 */
    getInstallerUrl: function()
    {
        if (assemblive.system.isWindows())
        {
            return "http://webplayer.unity3d.com/download_webplayer-2.x/UnityWebPlayer.exe";
        }
        else if (assemblive.system.isMac())
        {
            if (assemblive.system.isMacIntel())
            {
                return "http://webplayer.unity3d.com/download_webplayer-2.x/webplayer-i386.dmg";
            }
            else
            {
                return "http://webplayer.unity3d.com/download_webplayer-2.x/webplayer-ppc.dmg";
            }
        }
        else
        {
            return "http://www.unity3d.com/unity-web-player-2.x";
        }
    },
    
    /**
     * Get Unity installer icon
     */
    getInstallerIcon: function()
    {
        return assemblive._site + "/api/unity.png";
    },
    
    /**
     * Get the installer link
     */
    getInstallerLink: function()
    {
        return '<a href="' + assemblive.plugins.unity.getInstallerUrl() + '" target="_blank"><img src="' + assemblive.plugins.unity.getInstallerIcon() + '"></img></a>';
    },
    
    /**
     * Check if Unity is installed
     */
    isInstalled: function()
    {
    	if (assemblive.dependences.checkJQuery())
    	{
	        if (jQuery.browser.msie)
	        {
	            try
	            {
	                activeXObject = new ActiveXObject("UnityWebPlayer.UnityWebPlayer.1");
	                if (navigator.platform.toLowerCase().indexOf("Windows NT 6") != -1)
	                {
	                    if (activeXObject.GetPluginVersion() == "2.5.0f5")
	                    {
	                        return false;
	                    }
	                }
	                return true;
	            }
	            catch (e)
	            {
	                return false;
	            }
	        }
	        else
	        {
	            if (navigator.mimeTypes && navigator.mimeTypes["application/vnd.unity"])
	            {
	                if (navigator.mimeTypes["application/vnd.unity"].enabledPlugin && navigator.plugins && navigator.plugins["Unity Player"])
	                {
	                    return true;
	                }
	            }
	            return false;    
	        }
    	}
    	return false;
    },

    /**
     * Register functions to be notified when Unity is not installed and when installation finished
     * 
     * - onInstalled   : Called when installation successful
     * - onNotInstalled: Called when flash is not installed
     */
    register: function(onInstalled, onNotInstalled)
    {
    	// Check if timers are available
    	if (assemblive.dependences.checkJQueryTimers())
    	{
    		// Get the label for the timer
	    	var label = "assemblive.plugins.unity";
	    	// Refresh the plug ins list
	    	assemblive.plugins.refresh();
	    	// Check if already installed
	    	if (assemblive.plugins.unity.isInstalled())
	    	{
	    		// If installed and if call back exists, notify
	            if (onInstalled)
	            {
	            	// Actually notify
	            	onInstalled();
	            }
	    	}
	    	else
	    	{
	    		// If not installed and if call back exists, notify
	    		if (onNotInstalled)
	    		{
	    			// Actually notify
	    			onNotInstalled();
	    		}
	    		// Schedule a task that check if plug in was installed
	    		$(document).everyTime(2000, label, function() {
	    			// Refresh list
	    			assemblive.plugins.refresh();
	    			// Check if installed
	    	        if (assemblive.plugins.unity.isInstalled())
	    	        {
	    	        	// If installed, notify
	    	        	onInstalled();
	    	        	// Stop the timer
	    	            $(document).stopTime(label);
	    	        }
	            });
	    	}
    	}
    }
    
}
