1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5'use strict'; 6 7var naclModule = null; 8 9/** 10 * A helper function to abbreviate getElementById. 11 * 12 * @param {string} elementId The id to get. 13 * @return {Element} 14 */ 15function $(elementId) { 16 return document.getElementById(elementId); 17} 18 19/** 20 * MIME type for PNaCl 21 * 22 * @return {string} MIME type 23 */ 24function PNaClmimeType() { 25 return 'application/x-pnacl'; 26} 27 28/** 29 * Check if the browser supports PNaCl. 30 * 31 * @return {bool} 32 */ 33function browserSupportsPNaCl() { 34 var mimetype = PNaClmimeType(); 35 return navigator.mimeTypes[mimetype] !== undefined; 36} 37 38/** 39 * Get the URL for Google Cloud Storage. 40 * 41 * @param {string} name The relative path to the file. 42 * @return {string} 43 */ 44function getDataURL(name) { 45 var revision = '236779'; 46 var baseUrl = '//storage.googleapis.com/gonacl/demos/publish/'; 47 return baseUrl + revision + '/life/' + name; 48} 49 50/** 51 * Create the Native Client <embed> element as a child of the DOM element 52 * named "listener". 53 * 54 * @param {string} name The name of the example. 55 * @param {number} width The width to create the plugin. 56 * @param {number} height The height to create the plugin. 57 * @param {Object} attrs Dictionary of attributes to set on the module. 58 */ 59function createNaClModule(name, width, height, attrs) { 60 var moduleEl = document.createElement('embed'); 61 moduleEl.setAttribute('name', 'nacl_module'); 62 moduleEl.setAttribute('id', 'nacl_module'); 63 moduleEl.setAttribute('width', width); 64 moduleEl.setAttribute('height', height); 65 moduleEl.setAttribute('path', ''); 66 moduleEl.setAttribute('src', getDataURL(name + '.nmf')); 67 moduleEl.setAttribute('type', PNaClmimeType()); 68 69 // Add any optional arguments 70 if (attrs) { 71 for (var key in attrs) { 72 moduleEl.setAttribute(key, attrs[key]); 73 } 74 } 75 76 // The <EMBED> element is wrapped inside a <DIV>, which has both a 'load' 77 // and a 'message' event listener attached. This wrapping method is used 78 // instead of attaching the event listeners directly to the <EMBED> element 79 // to ensure that the listeners are active before the NaCl module 'load' 80 // event fires. 81 var listenerDiv = $('listener'); 82 listenerDiv.appendChild(moduleEl); 83} 84 85/** 86 * Add the default event listeners to the element with id "listener". 87 */ 88function attachDefaultListeners() { 89 var listenerDiv = $('listener'); 90 listenerDiv.addEventListener('load', moduleDidLoad, true); 91 listenerDiv.addEventListener('error', moduleLoadError, true); 92 listenerDiv.addEventListener('progress', moduleLoadProgress, true); 93 listenerDiv.addEventListener('crash', handleCrash, true); 94 listenerDiv.addEventListener('message', handleMessage, true); 95} 96 97/** 98 * Called when the Browser can not communicate with the Module 99 * 100 * This event listener is registered in attachDefaultListeners above. 101 * 102 * @param {Object} event 103 */ 104function handleCrash(event) { 105 if (naclModule.exitStatus == -1) { 106 updateStatus('CRASHED'); 107 } else { 108 updateStatus('EXITED [' + naclModule.exitStatus + ']'); 109 } 110} 111 112/** 113 * Handle a message coming from the NaCl module. 114 * @param {Object} message_event 115 */ 116function handleMessage(message_event) { 117 // Assume value is the current fps, sent as a float. 118 $('fps').textContent = message_event.data.toFixed(1); 119} 120 121/** 122 * Called when the NaCl module is loaded. 123 * 124 * This event listener is registered in attachDefaultListeners above. 125 */ 126function moduleDidLoad() { 127 var bar = $('progress-bar'); 128 bar.style.width = 100; 129 naclModule = $('nacl_module'); 130 hideStatus(); 131} 132 133/** 134 * Hide the status field and progress bar. 135 */ 136function hideStatus() { 137 $('loading-cover').style.display = 'none'; 138} 139 140/** 141 * Called when the plugin fails to load. 142 * 143 * @param {Object} event 144 */ 145function moduleLoadError(event) { 146 updateStatus('Load failed.'); 147} 148 149/** 150 * Called when the plugin reports progress events. 151 * 152 * @param {Object} event 153 */ 154function moduleLoadProgress(event) { 155 $('progress').style.display = 'block'; 156 157 var loadPercent = 0.0; 158 var bar = $('progress-bar'); 159 160 if (event.lengthComputable && event.total > 0) { 161 loadPercent = event.loaded / event.total * 100.0; 162 } else { 163 // The total length is not yet known. 164 loadPercent = 10; 165 } 166 bar.style.width = loadPercent + "%"; 167} 168/** 169 * If the element with id 'statusField' exists, then set its HTML to the status 170 * message as well. 171 * 172 * @param {string} opt_message The message to set. 173 */ 174function updateStatus(opt_message) { 175 var statusField = $('statusField'); 176 if (statusField) { 177 statusField.style.display = 'block'; 178 statusField.textContent = opt_message; 179 } 180} 181 182/** 183 * Listen for the DOM content to be loaded. This event is fired when parsing of 184 * the page's document has finished. 185 */ 186document.addEventListener('DOMContentLoaded', function() { 187 updateStatus('Loading...'); 188 if (!browserSupportsPNaCl()) { 189 updateStatus('Browser does not support PNaCl or PNaCl is disabled'); 190 } else if (naclModule == null) { 191 createNaClModule('life', '100%', '100%'); 192 attachDefaultListeners(); 193 } else { 194 // It's possible that the Native Client module onload event fired 195 // before the page's onload event. In this case, the status message 196 // will reflect 'SUCCESS', but won't be displayed. This call will 197 // display the current message. 198 updateStatus('Waiting.'); 199 } 200}); 201