Home | History | Annotate | Download | only in static
      1 // Copyright 2007 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 /**
     16  * @fileoverview
     17  * Javascript code for the interactive AJAX shell.
     18  *
     19  * Part of http://code.google.com/p/google-app-engine-samples/.
     20  *
     21  * Includes a function (shell.runStatement) that sends the current python
     22  * statement in the shell prompt text box to the server, and a callback
     23  * (shell.done) that displays the results when the XmlHttpRequest returns.
     24  *
     25  * Also includes cross-browser code (shell.getXmlHttpRequest) to get an
     26  * XmlHttpRequest.
     27  */
     28 
     29 /**
     30  * Shell namespace.
     31  * @type {Object}
     32  */
     33 var shell = {}
     34 
     35 /**
     36  * The shell history. history is an array of strings, ordered oldest to
     37  * newest. historyCursor is the current history element that the user is on.
     38  *
     39  * The last history element is the statement that the user is currently
     40  * typing. When a statement is run, it's frozen in the history, a new history
     41  * element is added to the end of the array for the new statement, and
     42  * historyCursor is updated to point to the new element.
     43  *
     44  * @type {Array}
     45  */
     46 shell.history = [''];
     47 
     48 /**
     49  * See {shell.history}
     50  * @type {number}
     51  */
     52 shell.historyCursor = 0;
     53 
     54 /**
     55  * A constant for the XmlHttpRequest 'done' state.
     56  * @type Number
     57  */
     58 shell.DONE_STATE = 4;
     59 
     60 /**
     61  * A cross-browser function to get an XmlHttpRequest object.
     62  *
     63  * @return {XmlHttpRequest?} a new XmlHttpRequest
     64  */
     65 shell.getXmlHttpRequest = function() {
     66   if (window.XMLHttpRequest) {
     67     return new XMLHttpRequest();
     68   } else if (window.ActiveXObject) {
     69     try {
     70       return new ActiveXObject('Msxml2.XMLHTTP');
     71     } catch(e) {
     72       return new ActiveXObject('Microsoft.XMLHTTP');
     73     }
     74   }
     75 
     76   return null;
     77 };
     78 
     79 /**
     80  * This is the prompt textarea's onkeypress handler. Depending on the key that
     81  * was pressed, it will run the statement, navigate the history, or update the
     82  * current statement in the history.
     83  *
     84  * @param {Event} event the keypress event
     85  * @return {Boolean} false to tell the browser not to submit the form.
     86  */
     87 shell.onPromptKeyPress = function(event) {
     88   var statement = document.getElementById('statement');
     89 
     90   if (this.historyCursor == this.history.length - 1) {
     91     // we're on the current statement. update it in the history before doing
     92     // anything.
     93     this.history[this.historyCursor] = statement.value;
     94   }
     95 
     96   // should we pull something from the history?
     97   if (event.ctrlKey && event.keyCode == 38 /* up arrow */) {
     98     if (this.historyCursor > 0) {
     99       statement.value = this.history[--this.historyCursor];
    100     }
    101     return false;
    102   } else if (event.ctrlKey && event.keyCode == 40 /* down arrow */) {
    103     if (this.historyCursor < this.history.length - 1) {
    104       statement.value = this.history[++this.historyCursor];
    105     }
    106     return false;
    107   } else if (!event.altKey) {
    108     // probably changing the statement. update it in the history.
    109     this.historyCursor = this.history.length - 1;
    110     this.history[this.historyCursor] = statement.value;
    111   }
    112 
    113   // should we submit?
    114   var ctrlEnter = (document.getElementById('submit_key').value == 'ctrl-enter');
    115   if (event.keyCode == 13 /* enter */ && !event.altKey && !event.shiftKey &&
    116       event.ctrlKey == ctrlEnter) {
    117     return this.runStatement();
    118   }
    119 };
    120 
    121 /**
    122  * The XmlHttpRequest callback. If the request succeeds, it adds the command
    123  * and its resulting output to the shell history div.
    124  *
    125  * @param {XmlHttpRequest} req the XmlHttpRequest we used to send the current
    126  *     statement to the server
    127  */
    128 shell.done = function(req) {
    129   if (req.readyState == this.DONE_STATE) {
    130     var statement = document.getElementById('statement')
    131     statement.className = 'prompt';
    132 
    133     // add the command to the shell output
    134     var output = document.getElementById('output');
    135 
    136     output.value += '\n>>> ' + statement.value;
    137     statement.value = '';
    138 
    139     // add a new history element
    140     this.history.push('');
    141     this.historyCursor = this.history.length - 1;
    142 
    143     // add the command's result
    144     var result = req.responseText.replace(/^\s*|\s*$/g, '');  // trim whitespace
    145     if (result != '')
    146       output.value += '\n' + result;
    147 
    148     // scroll to the bottom
    149     output.scrollTop = output.scrollHeight;
    150     if (output.createTextRange) {
    151       var range = output.createTextRange();
    152       range.collapse(false);
    153       range.select();
    154     }
    155   }
    156 };
    157 
    158 /**
    159  * This is the form's onsubmit handler. It sends the python statement to the
    160  * server, and registers shell.done() as the callback to run when it returns.
    161  *
    162  * @return {Boolean} false to tell the browser not to submit the form.
    163  */
    164 shell.runStatement = function() {
    165   var form = document.getElementById('form');
    166 
    167   // build a XmlHttpRequest
    168   var req = this.getXmlHttpRequest();
    169   if (!req) {
    170     document.getElementById('ajax-status').innerHTML =
    171         "<span class='error'>Your browser doesn't support AJAX. :(</span>";
    172     return false;
    173   }
    174 
    175   req.onreadystatechange = function() { shell.done(req); };
    176 
    177   // build the query parameter string
    178   var params = '';
    179   for (i = 0; i < form.elements.length; i++) {
    180     var elem = form.elements[i];
    181     if (elem.type != 'submit' && elem.type != 'button' && elem.id != 'caret') {
    182       var value = escape(elem.value).replace(/\+/g, '%2B'); // escape ignores +
    183       params += '&' + elem.name + '=' + value;
    184     }
    185   }
    186 
    187   // send the request and tell the user.
    188   document.getElementById('statement').className = 'prompt processing';
    189   req.open(form.method, form.action + '?' + params, true);
    190   req.setRequestHeader('Content-type',
    191                        'application/x-www-form-urlencoded;charset=UTF-8');
    192   req.send(null);
    193 
    194   return false;
    195 };
    196