diff --git a/app/controllers/wikilibras/signs_controller.rb b/app/controllers/wikilibras/signs_controller.rb new file mode 100644 index 0000000..e53666e --- /dev/null +++ b/app/controllers/wikilibras/signs_controller.rb @@ -0,0 +1,12 @@ +class Wikilibras::SignsController < InheritedResources::Base + actions :index, :new + + before_filter :authenticate_user! + + def create + controller = Wikilibras::Controller.new(params[:sign]) + controller.process() + + redirect_to @sign, notice: 'Edição criada com sucesso.' + end +end diff --git a/app/models/wikilibras.rb b/app/models/wikilibras.rb new file mode 100644 index 0000000..d3c4c22 --- /dev/null +++ b/app/models/wikilibras.rb @@ -0,0 +1,5 @@ +module Wikilibras + def self.table_name_prefix + 'wikilibras_' + end +end diff --git a/app/models/wikilibras/sign.rb b/app/models/wikilibras/sign.rb new file mode 100644 index 0000000..187ede5 --- /dev/null +++ b/app/models/wikilibras/sign.rb @@ -0,0 +1,15 @@ +# == Schema Information +# +# Table name: wikilibras_signs +# +# id :integer not null, primary key +# name :string(255) +# video_filename :string(255) +# owner_id :integer +# created_at :datetime +# updated_at :datetime +# + +class Wikilibras::Sign < ActiveRecord::Base + +end diff --git a/app/views/wikilibras/signs/new.haml b/app/views/wikilibras/signs/new.haml new file mode 100644 index 0000000..35a835c --- /dev/null +++ b/app/views/wikilibras/signs/new.haml @@ -0,0 +1,10 @@ +- content_for :js do + = javascript_include_tag "UnityObject2.js" + = javascript_include_tag "handlebars.runtime-v1.3.0" + +- content_for :css_code do + = stylesheet_link_tag "wikilibras" + += javascript_include_tag "wikilibras" + + diff --git a/config/application.rb b/config/application.rb index 8798f61..c404b00 100644 --- a/config/application.rb +++ b/config/application.rb @@ -19,6 +19,8 @@ module Vlibras # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' + config.assets.paths << Rails.root.join("vendor", "assets", "flash") + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.enforce_available_locales = false diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 7e1075f..bfd18ac 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,5 +5,11 @@ Rails.application.config.assets.precompile += %w( v_libras/requests/workflow.js Rails.application.config.assets.precompile += %w( v_libras/videos/index.js ) Rails.application.config.assets.precompile += %w( jquery.steps.js ) +Rails.application.config.assets.precompile += %w( UnityObject2.js ) +Rails.application.config.assets.precompile += %w( handlebars.runtime-v1.3.0.js ) +Rails.application.config.assets.precompile += %w( wikilibras.js ) +Rails.application.config.assets.precompile += %w( wikilibras.css ) + Rails.application.config.assets.precompile += %w( v_libras/requests.css ) -Rails.application.config.assets.precompile += %w( jquery.steps.css ) \ No newline at end of file +Rails.application.config.assets.precompile += %w( jquery.steps.css ) +Rails.application.config.assets.precompile += %w( video-js.swf ) diff --git a/config/routes.rb b/config/routes.rb index 1214e8e..3104153 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,8 +18,14 @@ Rails.application.routes.draw do end resources :videos, :only => [ :index, :show, :destroy ] + end - + namespace :wikilibras do + resources :signs do + # Wikilibras plugins makes a POST request to itself. This redirects it to create method + post 'new', on: :collection, to: 'signs#create' + end end + end diff --git a/db/migrate/20140811143409_create_wikilibras_signs.rb b/db/migrate/20140811143409_create_wikilibras_signs.rb new file mode 100644 index 0000000..684b720 --- /dev/null +++ b/db/migrate/20140811143409_create_wikilibras_signs.rb @@ -0,0 +1,12 @@ +class CreateWikilibrasSigns < ActiveRecord::Migration + def change + create_table :wikilibras_signs do |t| + t.string :name + t.string :video_filename + + t.references :owner + + t.timestamps + end + end +end diff --git a/lib/wikilibras/controller.rb b/lib/wikilibras/controller.rb new file mode 100644 index 0000000..b84ef87 --- /dev/null +++ b/lib/wikilibras/controller.rb @@ -0,0 +1,10 @@ +class Wikilibras::Controller + def initialize(params) + @params = params + end + + def process + puts "*" * 50 + puts @params.to_json + end +end \ No newline at end of file diff --git a/vendor/assets/flash/video-js.swf b/vendor/assets/flash/video-js.swf new file mode 100644 index 0000000..ed18e3d Binary files /dev/null and b/vendor/assets/flash/video-js.swf differ diff --git a/vendor/assets/javascripts/UnityObject2.js b/vendor/assets/javascripts/UnityObject2.js new file mode 100644 index 0000000..bbb81fa --- /dev/null +++ b/vendor/assets/javascripts/UnityObject2.js @@ -0,0 +1,1693 @@ +/** + * @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 or 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 = ""; + + if (ua.x64) { + url = cfg.fullInstall ? "UnityWebPlayerFull64.exe" : "UnityWebPlayer64.exe"; + } else { + 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); + + //starting from IE 11, IE is using a different UserAgent. + var ie = false; + if (/msie/i.test(a)){ + ie = parseFloat(a.replace(/^.*msie ([0-9]+(\.[0-9]+)?).*$/i, "$1")); + } else if (/Trident/i.test(a)) { + ie = parseFloat(a.replace(/^.*rv:([0-9]+(\.[0-9]+)?).*$/i, "$1")); + } + 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 : ie, + 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 += ''; + } + + elementToReplace.outerHTML = '' + pt + ''; + + } 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 (ua.ie) { + var activeXSupported = false; + try { + if (ActiveXObject.prototype != null) { + activeXSupported = true; + } + } catch(e) {} + + if (!activeXSupported) { + status = kUnsupported; + data = "ActiveXFailed"; + } else { + status = kMissing; + 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(e) {} + } + } + + _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 += ''; + pt += ''; + + for (var i in cfg.params) { + + if (cfg.params[i] != Object.prototype[i]) { + + if (i.toLowerCase() != "classid") { + + pt += ''; + } + } + } + + //var tmpHtml = '
' + pt + '
'; + var tmpHtml = '' + pt + ''; + 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('') + .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'; + + //We only have manual install for 64bit plugin so far. + if (ua.x64) + return method; + + // 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": + triedClickOnce = true; + _setSessionCookie(triedClickOnceCookie, triedClickOnce); + var $iframe = jQuery("