- add sources.
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / gonacl_appengine / static / pnacl-demo-earth / example.js
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
7 var naclModule = null;
8
9 /**
10  * A helper function to abbreviate getElementById.
11  *
12  * @param {string} elementId The id to get.
13  * @return {Element}
14  */
15 function $(elementId) {
16   return document.getElementById(elementId);
17 }
18
19 /**
20  * MIME type for PNaCl
21  *
22  * @return {string} MIME type
23  */
24 function PNaClmimeType() {
25   return 'application/x-pnacl';
26 }
27
28 /**
29  * Check if the browser supports PNaCl.
30  *
31  * @return {bool}
32  */
33 function 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  */
44 function getDataURL(name) {
45   var revision = 229855;
46   var baseUrl = 'http://commondatastorage.googleapis.com/gonacl/demos/publish/';
47   return baseUrl + revision + '/earth/' + 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  */
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());
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  */
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);
95   attachListeners();
96 }
97
98 /**
99  * Called when the Browser can not communicate with the Module
100  *
101  * This event listener is registered in attachDefaultListeners above.
102  *
103  * @param {Object} event
104  */
105 function handleCrash(event) {
106   if (naclModule.exitStatus == -1) {
107     updateStatus('CRASHED');
108   } else {
109     updateStatus('EXITED [' + naclModule.exitStatus + ']');
110   }
111 }
112
113 /**
114  * Called when the NaCl module is loaded.
115  *
116  * This event listener is registered in attachDefaultListeners above.
117  */
118 function moduleDidLoad() {
119   var bar = $('progress');
120   bar.value = 100;
121   bar.max = 100;
122   naclModule = $('nacl_module');
123   hideStatus();
124   setThreadCount();
125 }
126
127 /**
128  * Hide the status field and progress bar.
129  */
130 function hideStatus() {
131   $('statusField').style.display = 'none';
132   $('progress').style.display = 'none';
133 }
134
135 /**
136  * Called when the plugin fails to load.
137  *
138  * @param {Object} event
139  */
140 function moduleLoadError(event) {
141   updateStatus('Load failed.');
142 }
143
144 /**
145  * Called when the plugin reports progress events.
146  *
147  * @param {Object} event
148  */
149 function moduleLoadProgress(event) {
150   $('progress').style.display = 'block';
151
152   var loadPercent = 0.0;
153   var bar = $('progress');
154   bar.max = 100;
155   if (event.lengthComputable && event.total > 0) {
156     loadPercent = event.loaded / event.total * 100.0;
157   } else {
158     // The total length is not yet known.
159     loadPercent = -1.0;
160   }
161   bar.value = loadPercent;
162 }
163
164 /**
165  * If the element with id 'statusField' exists, then set its HTML to the status
166  * message as well.
167  *
168  * @param {string} opt_message The message to set.
169  */
170 function updateStatus(opt_message) {
171   var statusField = $('statusField');
172   if (statusField) {
173     statusField.style.display = 'block';
174     statusField.textContent = opt_message;
175   }
176 }
177
178 /**
179  * Send the current value of the element threadCount to the NaCl module.
180  *
181  * @param {number} threads The number of threads to use to render.
182  */
183 function setThreadCount(threads) {
184   var value = parseInt($('threadCount').value);
185   naclModule.postMessage({'message': 'set_threads',
186                           'value': value});
187 }
188
189 /**
190  * Add event listeners after the NaCl module has loaded.  These listeners will
191  * forward messages to the NaCl module via postMessage()
192  */
193 function attachListeners() {
194   $('threadCount').addEventListener('change', setThreadCount);
195   $('zoomRange').addEventListener('change',
196     function() {
197       var value = parseFloat($('zoomRange').value);
198       naclModule.postMessage({'message' : 'set_zoom',
199                               'value' : value});
200     });
201   $('lightRange').addEventListener('change',
202     function() {
203       var value = parseFloat($('lightRange').value);
204       naclModule.postMessage({'message' : 'set_light',
205                               'value' : value});
206     });
207 }
208
209 /**
210  * Load a texture and send pixel data down to NaCl module.
211  * @param {string} name
212  */
213 function loadTexture(name) {
214   // Load image from jpg, decompress into canvas.
215   var img = new Image();
216   img.onload = function() {
217     var graph = document.createElement('canvas');
218     graph.width = img.width;
219     graph.height = img.height;
220     var context = graph.getContext('2d');
221     context.drawImage(img, 0, 0);
222     var imageData = context.getImageData(0, 0, img.width, img.height);
223     // Send NaCl module the raw image data obtained from canvas.
224     naclModule.postMessage({'message' : 'texture',
225                             'name' : name,
226                             'width' : img.width,
227                             'height' : img.height,
228                             'data' : imageData.data.buffer});
229   };
230   // A cross-origin request to an image is "tainted", and cannot be read into a
231   // canvas without specifying this. See
232   // https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
233   img.crossOrigin = 'Anonymous';
234   img.src = getDataURL(name);
235 }
236
237 /**
238  * Handle a message coming from the NaCl module.
239  * @param {Object} message_event
240  */
241 function handleMessage(message_event) {
242   if (message_event.data['message'] == 'set_zoom') {
243     // zoom slider
244     var zoom = message_event.data['value'];
245     $('zoomRange').value = zoom;
246   } else if (message_event.data['message'] == 'set_light') {
247     // light slider
248     var light = message_event.data['value'];
249     $('lightRange').value = light;
250   } else if (message_event.data['message'] == 'request_textures') {
251     // NaCl module is requesting a set of textures.
252     var names = message_event.data['names'];
253     for (var i = 0; i < names.length; i++)
254       loadTexture(names[i]);
255   }
256 }
257
258 /**
259  * Listen for the DOM content to be loaded. This event is fired when parsing of
260  * the page's document has finished.
261  */
262 document.addEventListener('DOMContentLoaded', function() {
263   updateStatus('Loading...');
264   if (!browserSupportsPNaCl()) {
265     updateStatus('Browser does not support PNaCl or PNaCl is disabled');
266   } else if (naclModule == null) {
267     createNaClModule('earth', 480, 480);
268     attachDefaultListeners();
269   } else {
270     // It's possible that the Native Client module onload event fired
271     // before the page's onload event.  In this case, the status message
272     // will reflect 'SUCCESS', but won't be displayed.  This call will
273     // display the current message.
274     updateStatus('Waiting.');
275   }
276 });