Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / gonacl_appengine / static / lua / naclterm.js
blob51eaac904bce172122d9ad308eef9ee076daf4bf
1 /*
2  * Copyright (c) 2013 The Chromium Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
7 'use strict';
9 lib.rtdep('lib.f',
10           'hterm');
12 // CSP means that we can't kick off the initialization from the html file,
13 // so we do it like this instead.
14 window.onload = function() {
15   lib.init(function() {
16     NaClTerm.init();
17   });
20 /**
21  * The hterm-powered terminal command.
22  *
23  * This class defines a command that can be run in an hterm.Terminal instance.
24  *
25  * @param {Object} argv The argument object passed in from the Terminal.
26  */
27 function NaClTerm(argv) {
28   this.io = argv.io.push();
29   this.argv_ = argv;
32 var ansiCyan = '\x1b[36m';
33 var ansiReset = '\x1b[0m';
35 /**
36  * Static initialier called from index.html.
37  *
38  * This constructs a new Terminal instance and instructs it to run the NaClTerm
39  * command.
40  */
41 NaClTerm.init = function() {
42   var profileName = lib.f.parseQuery(document.location.search)['profile'];
43   var terminal = new hterm.Terminal(profileName);
44   terminal.decorate(document.querySelector('#terminal'));
46   // Useful for console debugging.
47   window.term_ = terminal;
49   terminal.runCommandClass(NaClTerm, document.location.hash.substr(1));
50   return true;
53 NaClTerm.prototype.updateStatus = function(message) {
54   document.getElementById('statusField').textContent = message;
55   this.io.print(message + '\n');
58 /**
59  * Handle messages sent to us from NaCl.
60  *
61  * @private
62  */
63 NaClTerm.prototype.handleMessage_ = function(e) {
64   if (e.data.indexOf(NaClTerm.prefix) == 0) {
65     var msg = e.data.substring(NaClTerm.prefix.length);
66     if (!this.loaded) {
67       this.bufferedOutput += msg;
68     } else {
69       this.io.print(msg);
70     }
71   } else if (e.data.indexOf('exited') == 0) {
72     var exitCode = e.data.split(':', 2)[1]
73     if (exitCode === undefined)
74       exitCode = 0;
75     this.exit(exitCode);
76   } else {
77     console.log('unexpected message: ' + e.data);
78     return;
79   }
82 /**
83  * Handle load error event from NaCl.
84  */
85 NaClTerm.prototype.handleLoadAbort_ = function(e) {
86   this.updateStatus('Load aborted.');
89 /**
90  * Handle load abort event from NaCl.
91  */
92 NaClTerm.prototype.handleLoadError_ = function(e) {
93   this.updateStatus(embed.lastError);
96 NaClTerm.prototype.doneLoadingUrl = function() {
97   var width = this.io.terminal_.screenSize.width;
98   this.io.print('\r' + Array(width+1).join(' '));
99   var message = '\rLoaded ' + this.lastUrl;
100   if (this.lastTotal) {
101     var kbsize = Math.round(this.lastTotal/1024)
102     message += ' ['+ kbsize + ' KiB]';
103   }
104   this.io.print(message.slice(0, width) + '\n')
108  * Handle load end event from NaCl.
109  */
110 NaClTerm.prototype.handleLoad_ = function(e) {
111   if (this.lastUrl)
112     this.doneLoadingUrl();
113   else
114     this.io.print('Loaded.\n');
115   delete this.lastUrl
117   document.getElementById('loading-cover').style.display = 'none';
119   this.io.print(ansiReset);
121   // Now that have completed loading and displaying
122   // loading messages we output any messages from the
123   // NaCl module that were buffered up unto this point
124   this.loaded = true;
125   this.io.print(this.bufferedOutput);
126   this.sendMessage(this.bufferedInput);
127   this.bufferedOutput = ''
128   this.bufferedInput = ''
132  * Handle load progress event from NaCl.
133  */
134 NaClTerm.prototype.handleProgress_ = function(e) {
135   var url = e.url.substring(e.url.lastIndexOf('/') + 1);
137   if (this.lastUrl && this.lastUrl != url)
138     this.doneLoadingUrl()
140   if (!url)
141     return;
143   var percent = 10;
144   var message = 'Loading ' + url;
146   if (e.lengthComputable && e.total > 0) {
147     percent = Math.round(e.loaded * 100 / e.total);
148     var kbloaded = Math.round(e.loaded / 1024);
149     var kbtotal = Math.round(e.total / 1024);
150     message += ' [' + kbloaded + ' KiB/' + kbtotal + ' KiB ' + percent + '%]';
151   }
153   document.getElementById('progress-bar').style.width = percent + "%";
155   var width = this.io.terminal_.screenSize.width;
156   this.io.print('\r' + message.slice(-width));
157   this.lastUrl = url;
158   this.lastTotal = e.total;
162  * Handle crash event from NaCl.
163  */
164 NaClTerm.prototype.handleCrash_ = function(e) {
165  this.exit(this.embed.exitStatus);
169  * Exit the command.
170  */
171 NaClTerm.prototype.exit = function(code) {
172  this.io.print(ansiCyan)
173  if (code == -1) {
174    this.io.print('Program crashed (exit status -1)\n')
175  } else {
176    this.io.print('Program exited (status=' + code + ')\n');
178  this.loaded = false;
181 NaClTerm.prototype.restartNaCl = function() {
182   if (this.embed !== undefined) {
183     document.getElementById("listener").removeChild(this.embed);
184     delete this.embed;
185   }
186   this.io.terminal_.reset();
187   this.startCommand();
188   this.createEmbed(this.io.terminal_.screenSize.width, this.io.terminal_.screenSize.height);
192  * Create the NaCl embed element.
193  * We delay this until the first terminal resize event so that we start
194  * with the correct size.
195  */
196 NaClTerm.prototype.createEmbed = function(width, height) {
197   var mimetype = 'application/x-pnacl';
198   if (navigator.mimeTypes[mimetype] === undefined) {
199     if (mimetype.indexOf('pnacl') != -1)
200       this.updateStatus('Browser does not support PNaCl or PNaCl is disabled');
201     else
202       this.updateStatus('Browser does not support NaCl or NaCl is disabled');
203     return;
204   }
206   var embed = document.createElement('object');
207   embed.width = 0;
208   embed.height = 0;
209   embed.data = NaClTerm.nmf;
210   embed.type = mimetype;
211   embed.addEventListener('message', this.handleMessage_.bind(this));
212   embed.addEventListener('progress', this.handleProgress_.bind(this));
213   embed.addEventListener('load', this.handleLoad_.bind(this));
214   embed.addEventListener('error', this.handleLoadError_.bind(this));
215   embed.addEventListener('abort', this.handleLoadAbort_.bind(this));
216   embed.addEventListener('crash', this.handleCrash_.bind(this));
218   function addParam(name, value) {
219     var param = document.createElement('param');
220     param.name = name;
221     param.value = value;
222     embed.appendChild(param);
223   }
225   addParam('PS_TTY_PREFIX', NaClTerm.prefix);
226   addParam('PS_TTY_RESIZE', 'tty_resize');
227   addParam('PS_TTY_COLS', width);
228   addParam('PS_TTY_ROWS', height);
229   addParam('PS_STDIN', '/dev/tty');
230   addParam('PS_STDOUT', '/dev/tty');
231   addParam('PS_STDERR', '/dev/tty');
232   addParam('PS_VERBOSITY', '2');
233   addParam('PS_EXIT_MESSAGE', 'exited');
234   addParam('TERM', 'xterm-256color');
235   addParam('LUA_DATA_URL', 'http://storage.googleapis.com/gonacl/demos/publish/234230_dev/lua');
237   // Add ARGV arguments from query parameters.
238   var args = lib.f.parseQuery(document.location.search);
239   for (var argname in args) {
240     addParam(argname, args[argname]);
241   }
243   // If the application has set NaClTerm.argv and there were
244   // no arguments set in the query parameters then add the default
245   // NaClTerm.argv arguments.
246   if (args['arg1'] === undefined && NaClTerm.argv) {
247     var argn = 1
248     NaClTerm.argv.forEach(function(arg) {
249       var argname = 'arg' + argn;
250       addParam(argname, arg);
251       argn = argn + 1
252     })
253   }
255   this.updateStatus('Loading...');
256   this.io.print('Loading NaCl module.\n')
257   document.getElementById("listener").appendChild(embed);
258   this.embed = embed;
261 NaClTerm.prototype.onTerminalResize_ = function(width, height) {
262   if (this.embed === undefined)
263     this.createEmbed(width, height);
264   else
265     this.embed.postMessage({'tty_resize': [ width, height ]});
267   // Require at least 80 columns, otherwise some of the demos look
268   // very wrong.
269   var width = this.io.terminal_.scrollPort_.characterSize.width * 80;
270   document.getElementById("terminal").style.minWidth = width + 'px';
273 NaClTerm.prototype.sendMessage = function(msg) {
274   if (!this.loaded) {
275     this.bufferedInput += msg;
276     return;
277   }
278   var message = {};
279   message[NaClTerm.prefix] = msg;
280   this.embed.postMessage(message);
283 NaClTerm.prototype.onVTKeystroke_ = function(str) {
284   this.sendMessage(str)
287 NaClTerm.prototype.startCommand = function() {
288   // We don't properly support the hterm bell sound, so we need to disable it.
289   this.io.terminal_.prefs_.definePreference('audible-bell-sound', '');
290   this.io.terminal_.setAutoCarriageReturn(true);
291   this.io.terminal_.setCursorPosition(0, 0);
292   this.io.terminal_.setCursorVisible(true);
294   this.bufferedOutput = '';
295   this.bufferedInput = '';
296   this.loaded = false;
297   this.io.print(ansiCyan);
301  * This is invoked by the terminal as a result of terminal.runCommandClass().
302  */
303 NaClTerm.prototype.run = function() {
304   this.startCommand();
305   this.io.onVTKeystroke = this.onVTKeystroke_.bind(this);
306   this.io.onTerminalResize = this.onTerminalResize_.bind(this);