/** * @fileOverview * Defines UnityObject2 */ //TODO: No need to polute the global space, just transfer this control to a 'static' variable insite unityObject! /** * @namespace */ //var unity = unity || {}; // We store all unityObject instances in a global scope, needed for IE firstFrameCallback and other internal tasks. //unity.instances = []; //unity.instanceNumber = 0; /** * Object expected by the Java Installer. We can move those to UnityObject2 if we update the java Installer. */ var unityObject = { /** * Callback used bt the Java installer to notify the Install Complete. * @private * @param {String} id * @param {bool} success * @param {String} errormessage */ javaInstallDone : function (id, success, errormessage) { var instanceId = parseInt(id.substring(id.lastIndexOf('_') + 1), 10); if (!isNaN(instanceId)) { // javaInstallDoneCallback must not be called directly because it deadlocks google chrome setTimeout(function () { UnityObject2.instances[instanceId].javaInstallDoneCallback(id, success, errormessage); }, 10); } } }; /** * @class * @constructor */ var UnityObject2 = function (config) { /** @private */ var logHistory = [], win = window, doc = document, nav = navigator, instanceNumber = null, //domLoaded = false, //domLoadEvents = [], embeddedObjects = [], //Could be removed? //listeners = [], //styleSheet = null, //styleSheetMedia = null, //autoHideShow = true, //fullSizeMissing = true, useSSL = (document.location.protocol == 'https:'), //This will turn off enableUnityAnalytics, since enableUnityAnalytics don't have a https version. baseDomain = useSSL ? "https://ssl-webplayer.unity3d.com/" : "http://webplayer.unity3d.com/", triedJavaCookie = "_unity_triedjava", triedJavaInstall = _getCookie(triedJavaCookie), triedClickOnceCookie = "_unity_triedclickonce", triedClickOnce = _getCookie(triedClickOnceCookie), progressCallback = false, applets = [], //addedClickOnce = false, googleAnalyticsLoaded = false, googleAnalyticsCallback = null, latestStatus = null, lastType = null, //beginCallback = [], //preCallback = [], imagesToWaitFor = [], //referrer = null, pluginStatus = null, pluginStatusHistory = [], installProcessStarted = false, //not used anymore? kInstalled = "installed", kMissing = "missing", kBroken = "broken", kUnsupported = "unsupported", kReady = "ready", //not used anymore? kStart = "start", kError = "error", kFirst = "first", //kStandard = "standard", kJava = "java", kClickOnce = "clickonce", //not used anymore? wasMissing = false, //identifies if this is a install attempt, or if the plugin was already installed unityObject = null, //The <embed> or <object> for the webplayer. This can be used for webPlayer communication. //kApplet = "_applet", //kBanner = "_banner", cfg = { pluginName : "Unity Player", pluginMimeType : "application/vnd.unity", baseDownloadUrl : baseDomain + "download_webplayer-3.x/", fullInstall : false, autoInstall : false, enableJava : true, enableJVMPreloading : false, enableClickOnce : true, enableUnityAnalytics : false, enableGoogleAnalytics : true, params : {}, attributes : {}, referrer : null, debugLevel : 0 }; // Merge in the given configuration and override defaults. cfg = jQuery.extend(true, cfg, config); if (cfg.referrer === "") { cfg.referrer = null; } //enableUnityAnalytics does not support SSL yet. if (useSSL) { cfg.enableUnityAnalytics = false; } /** * Get cookie value * @private * @param {String} name The param name * @return string or false if non-existing. */ function _getCookie(name) { var e = new RegExp(escape(name) + "=([^;]+)"); if (e.test(doc.cookie + ";")) { e.exec(doc.cookie + ";"); return RegExp.$1; } return false; } /** * Sets session cookie * @private */ function _setSessionCookie(name, value) { document.cookie = escape(name) + "=" + escape(value) + "; path=/"; } /** * Converts unity version to number (used for version comparison) * @private */ function _getNumericUnityVersion(version) { var result = 0, major, minor, fix, type, release; if (version) { var m = version.toLowerCase().match(/^(\d+)(?:\.(\d+)(?:\.(\d+)([dabfr])?(\d+)?)?)?$/); if (m && m[1]) { major = m[1]; minor = m[2] ? m[2] : 0; fix = m[3] ? m[3] : 0; type = m[4] ? m[4] : 'r'; release = m[5] ? m[5] : 0; result |= ((major / 10) % 10) << 28; result |= (major % 10) << 24; result |= (minor % 10) << 20; result |= (fix % 10) << 16; result |= {d: 2 << 12, a: 4 << 12, b: 6 << 12, f: 8 << 12, r: 8 << 12}[type]; result |= ((release / 100) % 10) << 8; result |= ((release / 10) % 10) << 4; result |= (release % 10); } } return result; } /** * Gets plugin and unity versions (non-ie) * @private */ function _getPluginVersion(callback, versions) { var b = doc.getElementsByTagName("body")[0]; var ue = doc.createElement("object"); var i = 0; if (b && ue) { ue.setAttribute("type", cfg.pluginMimeType); ue.style.visibility = "hidden"; b.appendChild(ue); var count = 0; (function () { if (typeof ue.GetPluginVersion === "undefined") { if (count++ < 10) { setTimeout(arguments.callee, 10); } else { b.removeChild(ue); callback(null); } } else { var v = {}; if (versions) { for (i = 0; i < versions.length; ++i) { v[versions[i]] = ue.GetUnityVersion(versions[i]); } } v.plugin = ue.GetPluginVersion(); b.removeChild(ue); callback(v); } })(); } else { callback(null); } } /** * Retrieves windows installer name * @private */ function _getWinInstall() { var url = cfg.fullInstall ? "UnityWebPlayerFull.exe" : "UnityWebPlayer.exe"; if (cfg.referrer !== null) { url += "?referrer=" + cfg.referrer; } return url; } /** * Retrieves mac plugin package name * @private */ function _getOSXInstall() { var url = "UnityPlayer.plugin.zip"; if (cfg.referrer != null) { url += "?referrer=" + cfg.referrer; } return url; } /** * retrieves installer name * @private */ function _getInstaller() { return cfg.baseDownloadUrl + (ua.win ? _getWinInstall() : _getOSXInstall() ); } /** * sets plugin status * @private */ function _setPluginStatus(status, type, data, url) { if (status === kMissing){ wasMissing = true; } // debug('setPluginStatus() status:', status, 'type:', type, 'data:', data, 'url:', url); // only report to analytics the first time a status occurs. if ( jQuery.inArray(status, pluginStatusHistory) === -1 ) { //Only send analytics for plugins installs. Do not send if plugin is already installed. if (wasMissing) { _an.send(status, type, data, url); } pluginStatusHistory.push(status); } pluginStatus = status; } /** * Contains browser and platform properties * @private */ var ua = function () { var a = nav.userAgent, p = nav.platform; var chrome = /chrome/i.test(a); var ua = { w3 : typeof doc.getElementById != "undefined" && typeof doc.getElementsByTagName != "undefined" && typeof doc.createElement != "undefined", win : p ? /win/i.test(p) : /win/i.test(a), mac : p ? /mac/i.test(p) : /mac/i.test(a), ie : /msie/i.test(a) ? parseFloat(a.replace(/^.*msie ([0-9]+(\.[0-9]+)?).*$/i, "$1")) : false, ff : /firefox/i.test(a), op : /opera/i.test(a), ch : chrome, ch_v : /chrome/i.test(a) ? parseFloat(a.replace(/^.*chrome\/(\d+(\.\d+)?).*$/i, "$1")) : false, sf : /safari/i.test(a) && !chrome, wk : /webkit/i.test(a) ? parseFloat(a.replace(/^.*webkit\/(\d+(\.\d+)?).*$/i, "$1")) : false, x64 : /win64/i.test(a) && /x64/i.test(a), moz : /mozilla/i.test(a) ? parseFloat(a.replace(/^.*mozilla\/([0-9]+(\.[0-9]+)?).*$/i, "$1")) : 0, mobile: /ipad/i.test(p) || /iphone/i.test(p) || /ipod/i.test(p) || /android/i.test(a) || /windows phone/i.test(a) }; ua.clientBrand = ua.ch ? 'ch' : ua.ff ? 'ff' : ua.sf ? 'sf' : ua.ie ? 'ie' : ua.op ? 'op' : '??'; ua.clientPlatform = ua.win ? 'win' : ua.mac ? 'mac' : '???'; // get base url var s = doc.getElementsByTagName("script"); for (var i = 0; i < s.length; ++i) { var m = s[i].src.match(/^(.*)3\.0\/uo\/UnityObject2\.js$/i); if (m) { cfg.baseDownloadUrl = m[1]; break; } } /** * compares two versions * @private */ function _compareVersions(v1, v2) { for (var i = 0; i < Math.max(v1.length, v2.length); ++i) { var n1 = (i < v1.length) && v1[i] ? new Number(v1[i]) : 0; var n2 = (i < v2.length) && v2[i] ? new Number(v2[i]) : 0; if (n1 < n2) return -1; if (n1 > n2) return 1; } return 0; }; /** * detect java */ ua.java = function () { if (nav.javaEnabled()) { var wj = (ua.win && ua.ff); var mj = false;//(ua.mac && (ua.ff || ua.ch || ua.sf)); if (wj || mj) { if (typeof nav.mimeTypes != "undefined") { var rv = wj ? [1, 6, 0, 12] : [1, 4, 2, 0]; for (var i = 0; i < nav.mimeTypes.length; ++i) { if (nav.mimeTypes[i].enabledPlugin) { var m = nav.mimeTypes[i].type.match(/^application\/x-java-applet;(?:jpi-)?version=(\d+)(?:\.(\d+)(?:\.(\d+)(?:_(\d+))?)?)?$/); if (m != null) { if (_compareVersions(rv, m.slice(1)) <= 0) { return true; } } } } } } else if (ua.win && ua.ie) { if (typeof ActiveXObject != "undefined") { /** * ActiveX Test */ function _axTest(v) { try { return new ActiveXObject("JavaWebStart.isInstalled." + v + ".0") != null; } catch (ex) { return false; } } /** * ActiveX Test 2 */ function _axTest2(v) { try { return new ActiveXObject("JavaPlugin.160_" + v) != null; } catch (ex) { return false; } } if (_axTest("1.7.0")) { return true; } if (ua.ie >= 8) { if (_axTest("1.6.0")) { // make sure it's 1.6.0.12 or newer. increment 50 to a larger value if 1.6.0.50 is released for (var i = 12; i <= 50; ++i) { if (_axTest2(i)) { if (ua.ie == 9 && ua.moz == 5 && i < 24) { // when IE9 is not in compatibility mode require at least // Java 1.6.0.24: http://support.microsoft.com/kb/2506617 continue; } else { return true; } } } return false; } } else { return _axTest("1.6.0") || _axTest("1.5.0") || _axTest("1.4.2"); } } } } return false; }(); // detect clickonce ua.co = function () { if (ua.win && ua.ie) { var av = a.match(/(\.NET CLR [0-9.]+)|(\.NET[0-9.]+)/g); if (av != null) { var rv = [3, 5, 0]; for (var i = 0; i < av.length; ++i) { var versionNumbers = av[i].match(/[0-9.]{2,}/g)[0].split("."); if (_compareVersions(rv, versionNumbers) <= 0) { return true; } } } } return false; }(); return ua; }(); /** * analytics * @private */ var _an = function () { var uid = function () { var now = new Date(); var utc = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDay(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds()); return utc.toString(16) + _getRandomInt().toString(16); }(); var seq = 0; var _ugaq = window["_gaq"] = ( window["_gaq"] || [] ); _setUpAnalytics(); /** * generates random integer number * @private */ function _getRandomInt() { return Math.floor(Math.random() * 2147483647); } /** * Checks if there is a need to load analytics, by checking the existance of a _gaq object */ function _setUpAnalytics() { var gaUrl = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var ss = doc.getElementsByTagName("script"); var googleAnalyticsLoaded = false; for (var i = 0; i < ss.length; ++i) { if (ss[i].src && ss[i].src.toLowerCase() == gaUrl.toLowerCase()) { googleAnalyticsLoaded = true; break; } } if (!googleAnalyticsLoaded) { var ga = doc.createElement("script"); ga.type = "text/javascript"; ga.async = true; ga.src = gaUrl; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s); } var gaAccount = (cfg.debugLevel === 0) ? 'UA-16068464-16' : 'UA-16068464-17'; _ugaq.push(["unity._setDomainName", "none"]); _ugaq.push(["unity._setAllowLinker", true]); _ugaq.push(["unity._setReferrerOverride", ' '+this.location.toString()]); _ugaq.push(["unity._setAccount", gaAccount]); // $(GoogleRevisionPlaceholder) } /** * sends analytics data to unity * @private */ function _sendUnityAnalytics(event, type, data, callback) { if (!cfg.enableUnityAnalytics) { if (callback) { callback(); } return; } var url = "http://unityanalyticscapture.appspot.com/event?u=" + encodeURIComponent(uid) + "&s=" + encodeURIComponent(seq) + "&e=" + encodeURIComponent(event); // $(UnityRevisionPlaceholder) if (cfg.referrer !== null) { url += "?r=" + cfg.referrer; } if (type) { url += "&t=" + encodeURIComponent(type); } if (data) { url += "&d=" + encodeURIComponent(data); } var img = new Image(); if (callback) { img.onload = img.onerror = callback; } img.src = url; } /** * sends analytics data to google * @private */ function _sendGoogleAnalytics(event, type, data, callback) { if (!cfg.enableGoogleAnalytics) { if (callback) { callback(); } return; } var url = "/webplayer/install/" + event; var join = "?"; if (type) { url += join + "t=" + encodeURIComponent(type); join = "&"; } if (data) { url += join + "d=" + encodeURIComponent(data); join = "&"; } if (callback) { _ugaq.push(function () { setTimeout(callback,1000); //this.googleAnalyticsCallback = callback; }); } //try to shorten the URL to fit into customVariable //it will try to replace the early directories to .. var gameUrl = cfg.src; if (gameUrl.length > 40) { gameUrl = gameUrl.replace("http://",""); var paths = gameUrl.split("/"); var gameUrlFirst = paths.shift(); var gameUrlLast = paths.pop(); gameUrl = gameUrlFirst + "/../"+ gameUrlLast; while(gameUrl.length < 40 && paths.length > 0) { var nextpath = paths.pop(); if(gameUrl.length + nextpath.length + 5 < 40) { gameUrlLast = nextpath + "/" + gameUrlLast; } else { gameUrlLast = "../" + gameUrlLast; } gameUrl = gameUrlFirst + "/../"+ gameUrlLast; } } _ugaq.push(['unity._setCustomVar', 2, // This custom var is set to slot #1. Required parameter. 'GameURL', // The name acts as a kind of category for the user activity. Required parameter. gameUrl, // This value of the custom variable. Required parameter. 3 // Sets the scope to page-level. Optional parameter. ]); _ugaq.push(['unity._setCustomVar', 1, // This custom var is set to slot #1. Required parameter. 'UnityObjectVersion', // The name acts as a kind of category for the user activity. Required parameter. "2", // This value of the custom variable. Required parameter. 3 // Sets the scope to page-level. Optional parameter. ]); if (type) { _ugaq.push(['unity._setCustomVar', 3, // This custom var is set to slot #1. Required parameter. 'installMethod', // The name acts as a kind of category for the user activity. Required parameter. type, // This value of the custom variable. Required parameter. 3 // Sets the scope to page-level. Optional parameter. ]); } _ugaq.push(["unity._trackPageview", url]); } return { /** * sends analytics data. optionally opens url once data has been sent * @public */ send : function (event, type, data, url) { if (cfg.enableUnityAnalytics || cfg.enableGoogleAnalytics) { debug('Analytics SEND', event, type, data, url); } ++seq; var count = 2; var callback = function () { if (0 == --count) { googleAnalyticsCallback = null; window.location = url; } } if (data === null || data === undefined) { data = ""; } _sendUnityAnalytics(event, type, data, url ? callback : null); _sendGoogleAnalytics(event, type, data, url ? callback : null); } }; }(); /* Java Install - BEGIN */ /** * @private */ function _createObjectElement(attributes, params, elementToReplace) { var i, at, pt, ue, pe; if (ua.win && ua.ie) { at = ""; for (i in attributes) { at += ' ' + i + '="' + attributes[i] + '"'; } pt = ""; for (i in params) { pt += '<param name="' + i + '" value="' + params[i] + '" />'; } elementToReplace.outerHTML = '<object' + at + '>' + pt + '</object>'; } else { ue = doc.createElement("object"); for (i in attributes) { ue.setAttribute(i, attributes[i]); } for (i in params) { pe = doc.createElement("param"); pe.name = i; pe.value = params[i]; ue.appendChild(pe); } elementToReplace.parentNode.replaceChild(ue, elementToReplace); } } /** * @private */ function _checkImage(img) { // img element not in the DOM yet if (typeof img == "undefined") { return false; } if (!img.complete) { return false; } // some browsers always return true in img.complete, for those // we can check naturalWidth if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { return false; } // no other way of checking, assuming it is ok return true; } /** * @private */ function _preloadJVMWhenReady(id) { var needToWait = false; for (var i = 0; i < imagesToWaitFor.length; i++) { if (!imagesToWaitFor[i]) { continue; } var img = doc.images[imagesToWaitFor[i]]; if (!_checkImage(img)) { needToWait = true; } else { imagesToWaitFor[i] = null; } } if (needToWait) { // check again in 100ms setTimeout(arguments.callee, 100); } else { // preload after a small delay, to make sure // the images have actually rendered setTimeout(function () { _preloadJVM(id); }, 100); } } /** * preloads the JVM and the Java Plug-in * @private */ function _preloadJVM(id) { var re = doc.getElementById(id); if (!re) { re = doc.createElement("div"); var lastBodyElem = doc.body.lastChild; doc.body.insertBefore(re, lastBodyElem.nextSibling); } var codebase = cfg.baseDownloadUrl + "3.0/jws/"; var a = { id : id, type : "application/x-java-applet", code : "JVMPreloader", width : 1, height : 1, name : "JVM Preloader" }; var p = { context : id, codebase : codebase, classloader_cache : false, scriptable : true, mayscript : true }; _createObjectElement(a, p, re); jQuery('#' + id).show(); //setVisibility(id, true); } /** * launches java installer * @private */ function _doJavaInstall(id) { triedJavaInstall = true; _setSessionCookie(triedJavaCookie, triedJavaInstall); var re = doc.getElementById(id); var appletID = id + "_applet_" + instanceNumber; applets[appletID] = { attributes : cfg.attributes, params : cfg.params, callback : cfg.callback, broken : cfg.broken }; var applet = applets[appletID]; var a = { id : appletID, type : "application/x-java-applet", archive : cfg.baseDownloadUrl + "3.0/jws/UnityWebPlayer.jar", code : "UnityWebPlayer", width : 1, height : 1, name : "Unity Web Player" }; if (ua.win && ua.ff) { a["style"] = "visibility: hidden;"; } var p = { context : appletID, jnlp_href : cfg.baseDownloadUrl + "3.0/jws/UnityWebPlayer.jnlp", classloader_cache : false, installer : _getInstaller(), image : baseDomain + "installation/unitylogo.png", centerimage : true, boxborder : false, scriptable : true, mayscript : true }; for (var i in applet.params) { if (i == "src") { continue; } if (applet.params[i] != Object.prototype[i]) { p[i] = applet.params[i]; if (i.toLowerCase() == "logoimage") { p["image"] = applet.params[i]; } else if (i.toLowerCase() == "backgroundcolor") { p["boxbgcolor"] = "#" + applet.params[i]; } else if (i.toLowerCase() == "bordercolor") { // there's no way to specify border color p["boxborder"] = true; } else if (i.toLowerCase() == "textcolor") { p["boxfgcolor"] = "#" + applet.params[i]; } } } // Create a dummy div element in the unityPlayer div // so that it can be replaced with the 1x1 px applet. // The applet will be resized when it has fully loaded, // see appletStarted(). var divToBeReplacedWithApplet = doc.createElement("div"); re.appendChild(divToBeReplacedWithApplet); _createObjectElement(a, p, divToBeReplacedWithApplet); jQuery('#' + id).show(); //setVisibility(appletID, true); } /** * @private */ function _jvmPreloaded(id) { // timeout prevents crash on ie setTimeout(function () { var re = doc.getElementById(id); if (re) { re.parentNode.removeChild(re); } }, 0); } /** * @private */ function _appletStarted(id) { // set the size of the applet to the one from cloned attributes var applet = applets[id], appletElement = doc.getElementById(id), childNode; // the applet might have already finished by now if (!appletElement) { return; } appletElement.width = applet.attributes["width"] || 600; appletElement.height = applet.attributes["height"] || 450; // remove all the siblings of the applet var parentNode = appletElement.parentNode; var childNodeList = parentNode.childNodes; for (var i = 0; i < childNodeList.length; i++) { childNode = childNodeList[i]; // Compare the child node with our applet element only if // it has the same type. Doing the comparison in other cases just // jumps out of the loop. if (childNode.nodeType == 1 && childNode != appletElement) { parentNode.removeChild(childNode); } } } // java installation callback function _javaInstallDoneCallback(id, success, errormessage) { debug('_javaInstallDoneCallback', id, success, errormessage); //console.log('javaInstallDoneCallback', id, success, errormessage); if (!success) { //var applet = applets[id]; _setPluginStatus(kError, kJava, errormessage); //createMissingUnity(id, applet.attributes, applet.params, applet.callback, applet.broken, kJava, errormessage); } } /* Java Install - END */ /** * @private */ function log() { logHistory.push(arguments); if ( cfg.debugLevel > 0 && window.console && window.console.log ) { console.log(Array.prototype.slice.call(arguments)); //console.log.apply(console, Array.prototype.slice.call(arguments)); } } /** * @private */ function debug() { logHistory.push(arguments); if ( cfg.debugLevel > 1 && window.console && window.console.log ) { console.log(Array.prototype.slice.call(arguments)); //console.log.apply(console, Array.prototype.slice.call(arguments)); } } /** * appends px to the value if it's a plain number * @private */ function _appendPX(value) { if (/^[-+]?[0-9]+$/.test(value)) { value += "px"; } return value; } var publicAPI = /** @lends UnityObject2.prototype */ { /** * Get Debug Level (0=Disabled) * @public * @return {Number} Debug Level */ getLogHistory: function () { return logHistory; // JSON.stringify() }, /** * Get configuration object * @public * @return {Object} cfg */ getConfig: function () { return cfg; // JSON.stringify() }, /** * @public * @return {Object} detailed info about OS and Browser. */ getPlatformInfo: function () { return ua; }, /** * Initialize plugin config and proceed with attempting to start the webplayer. * @public */ initPlugin: function (targetEl, src) { cfg.targetEl = targetEl; cfg.src = src; debug('ua:', ua); //console.debug('initPlugin this:', this); this.detectUnity(this.handlePluginStatus); }, /** * detects unity web player. * @public * callback - accepts two parameters. * first one contains "installed", "missing", "broken" or "unsupported" value. * second one returns requested unity versions. plugin version is included as well. * versions - optional array of unity versions to detect. */ detectUnity: function (callback, versions) { // console.debug('detectUnity this:', this); var self = this; var status = kMissing; var data; nav.plugins.refresh(); if (ua.clientBrand === "??" || ua.clientPlatform === "???" || ua.mobile ) { status = kUnsupported; } else if (ua.op && ua.mac) { // Opera on MAC is unsupported status = kUnsupported; data = "OPERA-MAC"; } else if ( typeof nav.plugins != "undefined" && nav.plugins[cfg.pluginName] && typeof nav.mimeTypes != "undefined" && nav.mimeTypes[cfg.pluginMimeType] && nav.mimeTypes[cfg.pluginMimeType].enabledPlugin ) { status = kInstalled; // make sure web player is compatible with 64-bit safari if (ua.sf && /Mac OS X 10_6/.test(nav.appVersion)) { _getPluginVersion(function (version) { if (!version || !version.plugin) { status = kBroken; data = "OSX10.6-SFx64"; } _setPluginStatus(status, lastType, data); callback.call(self, status, version); }, versions); return; } else if (ua.mac && ua.ch) { // older versions have issues on chrome _getPluginVersion(function (version) { if (version && (_getNumericUnityVersion(version.plugin) <= _getNumericUnityVersion("2.6.1f3"))) { status = kBroken; data = "OSX-CH-U<=2.6.1f3"; } _setPluginStatus(status, lastType, data); callback.call(self, status, version); }, versions); return; } else if (versions) { _getPluginVersion(function (version) { _setPluginStatus(status, lastType, data); callback.call(self, status, version); }, versions); return; } } else if (typeof win.ActiveXObject != "undefined") { try { var uo = new ActiveXObject("UnityWebPlayer.UnityWebPlayer.1"); var pv = uo.GetPluginVersion(); if (versions) { var v = {}; for (var i = 0; i < versions.length; ++i) { v[versions[i]] = uo.GetUnityVersion(versions[i]); } v.plugin = pv; } status = kInstalled; // 2.5.0 auto update has issues on vista and later if (pv == "2.5.0f5") { var m = /Windows NT \d+\.\d+/.exec(nav.userAgent); if (m && m.length > 0) { var wv = parseFloat(m[0].split(' ')[2]); if (wv >= 6) { status = kBroken; data = "WIN-U2.5.0f5"; } } } } catch (ex) { if (ua.win && ua.ie && ua.x64) { status = kUnsupported; data = "WIN-IEx64"; } } } _setPluginStatus(status, lastType, data); callback.call(self, status, v); }, /** * @public * @return {Object} with info about Unity WebPlayer plugin status (not installed, loading, running etc..) */ handlePluginStatus: function (status, versions) { // Store targetEl in the closure, to be able to get it back if setTimeout calls again. var targetEl = cfg.targetEl; var $targetEl = jQuery(targetEl); switch(status) { case kInstalled: // @todo add support for alternate custom handlers. this.notifyProgress($targetEl); this.embedPlugin($targetEl, cfg.callback); break; case kMissing: this.notifyProgress($targetEl); //this.installPlugin($targetEl); var self = this; var delayTime = (cfg.debugLevel === 0) ? 1000 : 8000; // Do a delay and re-check for plugin setTimeout(function () { cfg.targetEl = targetEl; self.detectUnity(self.handlePluginStatus); }, delayTime); break; case kBroken: // Browser needs to restart after install this.notifyProgress($targetEl); break; case kUnsupported: this.notifyProgress($targetEl); break; } }, /** * @public * @return {Object} with detailed plugin info, version number and other info that can be retrieved from the plugin. */ /*getPluginInfo: function () { },*/ /** * @public */ getPluginURL: function () { var url = "http://unity3d.com/webplayer/"; if (ua.win) { url = cfg.baseDownloadUrl + _getWinInstall(); } else if (nav.platform == "MacIntel") { url = cfg.baseDownloadUrl + (cfg.fullInstall ? "webplayer-i386.dmg" : "webplayer-mini.dmg"); if (cfg.referrer !== null) { url += "?referrer=" + cfg.referrer; } } else if (nav.platform == "MacPPC") { url = cfg.baseDownloadUrl + (cfg.fullInstall ? "webplayer-ppc.dmg" : "webplayer-mini.dmg"); if (cfg.referrer !== null) { url += "?referrer=" + cfg.referrer; } } return url; }, /** * @public */ getClickOnceURL: function () { return cfg.baseDownloadUrl + "3.0/co/UnityWebPlayer.application?installer=" + encodeURIComponent(cfg.baseDownloadUrl + _getWinInstall()); }, /** * Embed the plugin into the DOM. * @public */ embedPlugin: function (targetEl, callback) { targetEl = jQuery(targetEl).empty(); var src = cfg.src; //targetEl.data('src'), var width = cfg.width || "100%"; //TODO: extract those hardcoded values var height = cfg.height || "100%"; var self = this; if (ua.win && ua.ie) { // ie, dom and object element do not mix & match var at = ""; for (var i in cfg.attributes) { if (cfg.attributes[i] != Object.prototype[i]) { if (i.toLowerCase() == "styleclass") { at += ' class="' + cfg.attributes[i] + '"'; } else if (i.toLowerCase() != "classid") { at += ' ' + i + '="' + cfg.attributes[i] + '"'; } } } var pt = ""; // we manually add SRC here, because its now defined on the target element. pt += '<param name="src" value="' + src + '" />'; pt += '<param name="firstFrameCallback" value="UnityObject2.instances[' + instanceNumber + '].firstFrameCallback();" />'; for (var i in cfg.params) { if (cfg.params[i] != Object.prototype[i]) { if (i.toLowerCase() != "classid") { pt += '<param name="' + i + '" value="' + cfg.params[i] + '" />'; } } } //var tmpHtml = '<div id="' + targetEl.attr('id') + '" style="width: ' + _appendPX(width) + '; height: ' + _appendPX(height) + ';"><object classid="clsid:444785F1-DE89-4295-863A-D46C3A781394" style="display: block; width: 100%; height: 100%;"' + at + '>' + pt + '</object></div>'; var tmpHtml = '<object classid="clsid:444785F1-DE89-4295-863A-D46C3A781394" style="display: block; width: ' + _appendPX(width) + '; height: ' + _appendPX(height) + ';"' + at + '>' + pt + '</object>'; var $object = jQuery(tmpHtml); targetEl.append( $object ); embeddedObjects.push( targetEl.attr('id') ); unityObject = $object[0]; } else { // Create and append embed element into DOM. var $embed = jQuery('<embed/>') .attr({ src: src, type: cfg.pluginMimeType, width: width, height: height, firstFrameCallback: 'UnityObject2.instances[' + instanceNumber + '].firstFrameCallback();' }) .attr(cfg.attributes) .attr(cfg.params) .css({ display: 'block', width: _appendPX(width), height: _appendPX(height) }) .appendTo( targetEl ); unityObject = $embed[0]; } //Auto focus the new object/embed, so players dont have to click it before using it. //setTimeout is here to workaround a chrome bug. //we should not invoke focus on safari on mac. it causes some Input bugs. if (!ua.sf || !ua.mac) { setTimeout(function() { unityObject.focus(); }, 100); } if (callback) { callback(); } }, /** * Determine which installation method to use on the current platform, and return an array with their identifiers (i.e. 'ClickOnceIE', 'JavaInstall', 'Manual') * Take into account which previous methods might have been attempted (and failed) and skip to next best method. * @public * @return {String} */ getBestInstallMethod: function () { // Always fall back to good old manual (download) install. var method = 'Manual'; // Is Java available and not yet attempted? if (cfg.enableJava && ua.java && triedJavaInstall === false) { method = 'JavaInstall'; } // Is ClickOnce available and not yet attempted? else if (cfg.enableClickOnce && ua.co && triedClickOnce === false) { method = 'ClickOnceIE'; } return method; }, /** * Tries to install the plugin using the specified method. * If no method is passed, it will try to use this.getBestInstallMethod() * @public * @param {String} method The desired install method */ installPlugin: function(method) { if (method == null || method == undefined) { method = this.getBestInstallMethod(); } var urlToOpen = null; switch(method) { case "JavaInstall": this.doJavaInstall(cfg.targetEl.id); break; case "ClickOnceIE": //urlToOpen = this.getClickOnceURL(); //window.location = urlToOpen; var $iframe = jQuery("<iframe src='" + this.getClickOnceURL() + "' style='display:none;' />"); jQuery(cfg.targetEl).append($iframe); break; default: case "Manual": //doc.location = this.getPluginURL(); //urlToOpen = this.getPluginURL(); var $iframe = jQuery("<iframe src='" + this.getPluginURL() + "' style='display:none;' />"); jQuery(cfg.targetEl).append($iframe); break; } lastType = method; _an.send(kStart, method, null, null); }, /** * Trigger event using jQuery(document).trigger() * @public */ //TODO: verify its use. trigger: function (event, params) { if (params) { debug('trigger("' + event + '")', params); } else { debug('trigger("' + event + '")'); } jQuery(document).trigger(event, params); }, /** * Notify observers about onProgress event * @public */ notifyProgress: function (targetEl) { //debug('*** notifyProgress ***') if (typeof progressCallback !== "undefined" && typeof progressCallback === "function") { var payload = { ua: ua, pluginStatus: pluginStatus, bestMethod: null, lastType: lastType, targetEl: cfg.targetEl, unityObj: this }; if (pluginStatus === kMissing) { payload.bestMethod = this.getBestInstallMethod(); } if (latestStatus !== pluginStatus) { //Execute only on state change latestStatus = pluginStatus; progressCallback(payload); } } }, /** * Subscribe to onProgress notification * @public */ observeProgress: function (callback) { progressCallback = callback; }, /** * Callback made by the WebPlayer plugin when the first frame is rendered. * @public */ firstFrameCallback : function () { debug('*** firstFrameCallback (' + instanceNumber + ') ***'); pluginStatus = kFirst; this.notifyProgress(); /* // What? if (status == kFirst) { if (pluginStatus == null) { return; } } */ //Webplayer was already installed. //Should only log firstframes if it happened after a install. if (wasMissing === true) { _an.send(pluginStatus, lastType); } //setRunStatus(kFirst, lastType); }, /** * Get a string from a session cookie or SessionStorage * @public * @return {String} */ /*getSessionString: function (key) { },*/ /** * Set a string via a session cookie or SessionStorage * @public */ /* setSessionString: function (key, value) { },*/ /** * Get a string from a persistent cookie * @public * @return {String} */ /*getCookie: function (key) { },*/ /** * Set a string to a persistent cookie * @public */ /*setCookie: function (key, value, expiryDate) { },*/ /** * Exposed private function * @public */ setPluginStatus: function (status, type, data, url) { _setPluginStatus(status, type, data, url); }, /** * Exposed private function * @public */ doJavaInstall : function (id) { _doJavaInstall(id); }, /** * Exposed private function * @public */ jvmPreloaded : function (id) { _jvmPreloaded(id); }, /** * Exposed private function * @public */ appletStarted : function (id) { _appletStarted(id); }, /** * Exposed private function * @public */ javaInstallDoneCallback : function (id, success, errormessage) { _javaInstallDoneCallback(id, success, errormessage); }, getUnity: function() { return unityObject; } } // Internal store of each instance. instanceNumber = UnityObject2.instances.length; UnityObject2.instances.push(publicAPI); return publicAPI; }; /** * @static **/ UnityObject2.instances = [];