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.
10 * A helper function to abbreviate getElementById.
12 * @param {string} elementId The id to get.
15 function $(elementId
) {
16 return document
.getElementById(elementId
);
22 * @return {string} MIME type
24 function PNaClmimeType() {
25 return 'application/x-pnacl';
29 * Check if the browser supports PNaCl.
33 function browserSupportsPNaCl() {
34 var mimetype
= PNaClmimeType();
35 return navigator
.mimeTypes
[mimetype
] !== undefined;
39 * Get the URL for Google Cloud Storage.
41 * @param {string} name The relative path to the file.
44 function getDataURL(name
) {
45 var revision
= '236779';
46 var baseUrl
= '//storage.googleapis.com/gonacl/demos/publish/';
47 return baseUrl
+ revision
+ '/cube/' + name
;
51 * Create the Native Client <embed> element as a child of the DOM element
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.
59 function 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());
69 // Add any optional arguments
71 for (var key
in attrs
) {
72 moduleEl
.setAttribute(key
, attrs
[key
]);
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'
81 var listenerDiv
= $('listener');
82 listenerDiv
.appendChild(moduleEl
);
86 * Add the default event listeners to the element with id "listener".
88 function 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('message', handleMessage
, true);
94 listenerDiv
.addEventListener('crash', handleCrash
, true);
99 * Called when the Browser can not communicate with the Module
101 * This event listener is registered in attachDefaultListeners above.
103 * @param {Object} event
105 function handleCrash(event
) {
106 if (naclModule
.exitStatus
== -1) {
107 updateStatus('CRASHED');
109 updateStatus('EXITED [' + naclModule
.exitStatus
+ ']');
114 * Called when the NaCl module is loaded.
116 * This event listener is registered in attachDefaultListeners above.
118 function moduleDidLoad() {
119 var bar
= $('progress-bar');
120 bar
.style
.width
= 100;
121 naclModule
= $('nacl_module');
126 * Hide the status field and progress bar.
128 function hideStatus() {
129 $('loading-cover').style
.display
= 'none';
133 * Called when the plugin fails to load.
135 * @param {Object} event
137 function moduleLoadError(event
) {
138 updateStatus('Load failed.');
142 * Called when the plugin reports progress events.
144 * @param {Object} event
146 function moduleLoadProgress(event
) {
147 $('progress').style
.display
= 'block';
149 var loadPercent
= 0.0;
150 var bar
= $('progress-bar');
152 if (event
.lengthComputable
&& event
.total
> 0) {
153 loadPercent
= event
.loaded
/ event
.total
* 100.0;
155 // The total length is not yet known.
158 bar
.style
.width
= loadPercent
+ "%";
162 * If the element with id 'statusField' exists, then set its HTML to the status
165 * @param {string} opt_message The message to set.
167 function updateStatus(opt_message
) {
168 var statusField
= $('statusField');
170 statusField
.style
.display
= 'block';
171 statusField
.textContent
= opt_message
;
176 * Add event listeners after the NaCl module has loaded. These listeners will
177 * forward messages to the NaCl module via postMessage()
179 function attachListeners() {
180 $('xAngle').addEventListener('change', postAngleMessage
);
181 $('yAngle').addEventListener('change', postAngleMessage
);
182 $('animateOff').addEventListener('click', function() {
183 $('animateOn').checked
= '';
184 naclModule
.postMessage(false);
186 $('animateOn').addEventListener('click', function() {
187 $('animateOff').checked
= '';
188 naclModule
.postMessage(true);
192 function postAngleMessage() {
193 var xAngle
= parseFloat($('xAngle').value
);
194 var yAngle
= parseFloat($('yAngle').value
);
195 naclModule
.postMessage([xAngle
, yAngle
]);
199 * Load a texture and send pixel data down to NaCl module.
200 * @param {string} name
202 function loadTexture(name
) {
203 // Load image from jpg, decompress into canvas.
204 var img
= new Image();
205 img
.onload = function() {
206 var graph
= document
.createElement('canvas');
207 graph
.width
= img
.width
;
208 graph
.height
= img
.height
;
209 var context
= graph
.getContext('2d');
210 context
.drawImage(img
, 0, 0);
211 var imageData
= context
.getImageData(0, 0, img
.width
, img
.height
);
212 // Send NaCl module the raw image data obtained from canvas.
213 naclModule
.postMessage({'message' : 'texture',
216 'height' : img
.height
,
217 'data' : imageData
.data
.buffer
});
219 // A cross-origin request to an image is "tainted", and cannot be read into a
220 // canvas without specifying this. See
221 // https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
222 img
.crossOrigin
= 'Anonymous';
223 img
.src
= getDataURL(name
);
227 * Handle a message coming from the NaCl module.
228 * @param {Object} message_event
230 function handleMessage(event
) {
231 if (event
.data
instanceof Array
) {
232 if (event
.data
.length
!= 2)
234 var xAngle
= event
.data
[0];
235 var yAngle
= event
.data
[1];
236 $('xAngle').value
= xAngle
;
237 $('yAngle').value
= yAngle
;
238 } else if (typeof(event
.data
) === 'number') {
239 $('fps').textContent
= event
.data
.toFixed(1);
244 * Listen for the DOM content to be loaded. This event is fired when parsing of
245 * the page's document has finished.
247 document
.addEventListener('DOMContentLoaded', function() {
248 updateStatus('Loading...');
249 if (!browserSupportsPNaCl()) {
250 updateStatus('Browser does not support PNaCl or PNaCl is disabled');
251 } else if (naclModule
== null) {
252 createNaClModule('cube', '100%', '100%');
253 attachDefaultListeners();
255 // It's possible that the Native Client module onload event fired
256 // before the page's onload event. In this case, the status message
257 // will reflect 'SUCCESS', but won't be displayed. This call will
258 // display the current message.
259 updateStatus('Waiting.');