THRIFT-2350 Add async calls to normal JavaScript
Patch: Randy Abernethy
diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc
index c9ad467..65a0219 100644
--- a/compiler/cpp/src/generate/t_js_generator.cc
+++ b/compiler/cpp/src/generate/t_js_generator.cc
@@ -60,6 +60,10 @@
      iter = parsed_options.find("jquery");
      gen_jquery_ = (iter != parsed_options.end());
 
+	 if (gen_node_ && gen_jquery_) {
+       throw "Invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen js:jquery]";
+	 }
+
      if (gen_node_) {
        out_dir_base_ = "gen-nodejs";
      } else {
@@ -234,7 +238,7 @@
  private:
 
   /**
-   * True iff we should generate NodeJS-friendly RPC services.
+   * True if we should generate NodeJS-friendly RPC services.
    */
   bool gen_node_;
 
@@ -1024,46 +1028,52 @@
 
     // Open function
     f_service_ <<  js_namespace(tservice->get_program())<<service_name_<<"Client.prototype." <<
-      function_signature(*f_iter, "", gen_node_ || gen_jquery_) << " {" << endl;
+      function_signature(*f_iter, "", true) << " {" << endl;
 
     indent_up();
 
-    if (gen_node_) {
+    if (gen_node_) {          //Node.js output      ./gen-nodejs
       f_service_ <<
         indent() << "this._seqid = this.new_seqid();" << endl <<
-        indent() << "this._reqs[this.seqid()] = callback;" << endl;
-    } else if (gen_jquery_) {
-      f_service_ <<
-        indent() << "if (callback === undefined) {" << endl;
-        indent_up();
+        indent() << "this._reqs[this.seqid()] = callback;" << endl <<
+        indent() << "this.send_" << funname << "(" << arglist << ");" << endl;
     }
-
-    f_service_ << indent() <<
-      "this.send_" << funname << "(" << arglist << ");" << endl;
-
-    if (!gen_node_ && !(*f_iter)->is_oneway()) {
-      f_service_ << indent();
-      if (!(*f_iter)->get_returntype()->is_void()) {
-        f_service_ << "return ";
+    else if (gen_jquery_) {   //jQuery output       ./gen-js
+		  f_service_ << indent() << "if (callback === undefined) {" << endl;
+      indent_up();
+      f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl;
+      if (!(*f_iter)->is_oneway()) {
+        f_service_ << indent();
+        if (!(*f_iter)->get_returntype()->is_void()) {
+          f_service_ << "return ";
+        }
+        f_service_ << "this.recv_" << funname << "();" << endl;
       }
-      f_service_ <<
-        "this.recv_" << funname << "();" << endl;
-    }
-
-    if (gen_jquery_) {
       indent_down();
       f_service_ << indent() << "} else {" << endl;
       indent_up();
-        f_service_ << indent() << "var postData = this.send_" << funname <<
-           "(" << arglist << (arglist.empty() ? "" : ", ") << "true);" << endl;
-        f_service_ << indent() << "return this.output.getTransport()" << endl;
-        indent_up();
-          f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname << ");" << endl;
-        indent_down();
+      f_service_ << indent() << "var postData = this.send_" << funname <<
+        "(" << arglist << (arglist.empty() ? "" : ", ") << "true);" << endl;
+      f_service_ << indent() << "return this.output.getTransport()" << endl;
+      indent_up();
+      f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname << ");" << endl;
+      indent_down();
       indent_down();
       f_service_ << indent() << "}" << endl;
+    } else {                  //Standard JavaScript ./gen-js
+      f_service_ << indent() <<
+        "this.send_" << funname << "(" << arglist << (arglist.empty() ? "" : ", ") << "callback); " << endl;
+      if (!(*f_iter)->is_oneway()) {
+		    f_service_ << indent() << "if (!callback) {" << endl;
+        f_service_ << indent();
+        if (!(*f_iter)->get_returntype()->is_void()) {
+          f_service_ << "  return ";
+        }
+        f_service_ << "this.recv_" << funname << "();" << endl;
+        f_service_ << indent() << "}" << endl;
+      }
     }
-
+  
     indent_down();
 
     f_service_ << "};" << endl << endl;
@@ -1071,7 +1081,7 @@
 
     // Send function
     f_service_ <<  js_namespace(tservice->get_program())<<service_name_ <<
-        "Client.prototype.send_" << function_signature(*f_iter, "", gen_jquery_) << " {" <<endl;
+      "Client.prototype.send_" << function_signature(*f_iter, "", !gen_node_) << " {" << endl;
 
     indent_up();
 
@@ -1113,11 +1123,26 @@
       if (gen_jquery_) {
         f_service_ << indent() << "return this.output.getTransport().flush(callback);" << endl;
       } else {
-        f_service_ << indent() << "return this.output.getTransport().flush();" << endl;
+        f_service_ << indent() << "if (callback) {" << endl; 
+        f_service_ << indent() << "  var self = this;" << endl;
+        f_service_ << indent() << "  this.output.getTransport().flush(true, function() {" << endl;
+        f_service_ << indent() << "    if (this.readyState == 4 && this.status == 200) {" << endl;
+        f_service_ << indent() << "      self.output.getTransport().setRecvBuffer(this.responseText);" << endl;
+        f_service_ << indent() << "      var result = null;" << endl;
+        f_service_ << indent() << "      try {" << endl;
+        f_service_ << indent() << "        result = self.recv_" << funname << "();" << endl;
+        f_service_ << indent() << "      } catch (e) {" << endl;
+        f_service_ << indent() << "        result = e;" << endl;
+        f_service_ << indent() << "      }" << endl;
+        f_service_ << indent() << "      callback(result);" << endl;
+        f_service_ << indent() << "    }" << endl;
+        f_service_ << indent() << "  });" << endl;
+        f_service_ << indent() << "} else {" << endl;
+        f_service_ << indent() << "  return this.output.getTransport().flush();" << endl;
+        f_service_ << indent() << "}" << endl;
       }
     }
 
-
     indent_down();
 
     f_service_ << "};" << endl;
@@ -1814,3 +1839,4 @@
 "    jquery:          Generate jQuery compatible code.\n"
 "    node:            Generate node.js compatible code.\n")
 
+
diff --git a/lib/js/Gruntfile.js b/lib/js/Gruntfile.js
index dd6406b..9a8bb0d 100644
--- a/lib/js/Gruntfile.js
+++ b/lib/js/Gruntfile.js
@@ -35,12 +35,32 @@
           'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
         }
       }
+    },  
+    shell: {
+      InstallThriftJS: {
+        command: 'mkdir test/build; mkdir test/build/js; cp src/thrift.js test/build/js/thrift.js'
+      },
+      ThriftGen: {
+        command: 'thrift -gen js -gen js:node -o test ../../test/ThriftTest.thrift'
+      }
+    },
+    external_daemon: {
+      ThriftTestServer: {
+        options: {
+          startCheck: function(stdout, stderr) {
+            return (/Thrift Server running on port/).test(stdout);
+          },
+          nodeSpawnOptions: {cwd: "test"}
+        },
+        cmd: "node",
+        args: ["server_http.js"]
+      }
     },
     qunit: {
       all: {
         options: {
           urls: [
-            'http://localhost:8088/test.html'
+            'http://localhost:8088/test-nojq.html'
           ]
         }
       }
@@ -64,7 +84,9 @@
   grunt.loadNpmTasks('grunt-contrib-qunit');
   grunt.loadNpmTasks('grunt-contrib-concat');
   grunt.loadNpmTasks('grunt-jsdoc');
+  grunt.loadNpmTasks('grunt-external-daemon');
+  grunt.loadNpmTasks('grunt-shell');
 
-  grunt.registerTask('test', ['jshint', 'qunit']);
-  grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify', 'jsdoc']);
-};
\ No newline at end of file
+  grunt.registerTask('test', ['jshint', 'shell', 'external_daemon', 'qunit']);
+  grunt.registerTask('default', ['jshint', 'shell', 'external_daemon', 'qunit', 'concat', 'uglify', 'jsdoc']);
+};
diff --git a/lib/js/README b/lib/js/README
index 98a1b54..971f6da 100644
--- a/lib/js/README
+++ b/lib/js/README
@@ -1,7 +1,7 @@
 Thrift Javascript Library
 =========================
 This browser based Apache Thrift implementation supports
-RPC using the JSON protocol over Http[s] with XHR.
+RPC clients using the JSON protocol over Http[s] with XHR.
 
 License
 -------
@@ -25,50 +25,74 @@
 Example
 -------
 The listing below demonstrates a simple browser based JavaScript
-Thrift client and Node.js JavaScript server for the HelloSvc service. 
+Thrift client and Node.js JavaScript server for the hello_svc 
+service. 
 
 ### hello.thrift - Service IDL
-    service HelloSvc {
-        string hello_func(),
+    service hello_svc {
+      string get_message(1: string name)
     }
 
 ### hello.html - Browser Client
-    <!doctype html>
+    <!DOCTYPE html>
     <html lang="en">
-    <head>
-        <script src="thrift.js" type="text/javascript"></script>
-        <script src="gen-js/HelloSvc.js" type="text/javascript"></script>
-    </head>
-    <body>
-        <h1>Apache Thrift JavaScript Browser Client Demo</h1>
-        <button id="btn">Get Message from Node Server</button>
-        <script type="text/javascript">
-            document.getElementById("btn").addEventListener("click", getMessage, false);
-    
-            function getMessage() {
-                var transport = new Thrift.Transport("http://localhost:8585");
-                var protocol  = new Thrift.Protocol(transport);
-                var client = new HelloSvcClient(protocol);
-                var msg = client.hello_func();
-                document.getElementById("output").innerHTML = msg;
-            }
+      <head>
+        <meta charset="utf-8">
+        <title>Hello Thrift</title>
+      </head>
+      <body>
+        Name: <input type="text" id="name_in">
+        <input type="button" id="get_msg" value="Get Message" >
+        <div id="output"></div>
+  
+        <script src="thrift.js"></script>
+        <script src="gen-js/hello_svc.js"></script>
+        <script>
+          (function() {
+            var transport = new Thrift.Transport("/hello");
+            var protocol  = new Thrift.Protocol(transport);
+            var client    = new hello_svcClient(protocol);
+            var nameElement = document.getElementById("name_in");
+            var outputElement = document.getElementById("output");
+            document.getElementById("get_msg")
+              .addEventListener("click", function(){
+                client.get_message(nameElement.value, function(result) {
+                  outputElement.innerHTML = result;
+                });
+              });
+          })();
         </script>
-        <h2>Server Response: <div id="output"></div></h2>
-    </body> 
+      </body>
     </html>
 
 ### hello.js - Node Server
-    var thrift = require('thrift');
-    var TJSONProtocol = require('thrift/protocol').TJSONProtocol;
-    var HelloSvc = require('./gen-nodejs/HelloSvc.js');
+    var Thrift = require('thrift');
+    var TBufferedTransport = require('thrift/lib/thrift/transport').TBufferedTransport;
+    var TJSONProtocol = require('thrift/lib/thrift/protocol').TJSONProtocol;
+    var hello_svc = require('./gen-nodejs/hello_svc.js');
     
-    var call_counter = 0;
-        
-    var server = thrift.createHttpGetPostServer(HelloSvc, {
-      hello_func: function(result) {
-        console.log("Client call: " + (++call_counter));
-        result(null, "Hello Apache Thrift for JavaScript " + call_counter);
+    var hello_handler = {
+      get_message: function(name, result) {
+        var msg = "Hello " + name + "!";
+        result(null, msg);
       }
-    }, {protocol: TJSONProtocol});
+    }
     
-    server.listen(8585);
\ No newline at end of file
+    var hello_svc_opt = {
+      transport: TBufferedTransport,
+      protocol: TJSONProtocol,
+      cls: hello_svc,
+      handler: hello_handler
+    };
+    
+    var server_opt = {
+      staticFilePath: ".",
+      services: {
+        "/hello": hello_svc_opt
+      }
+    }
+    
+    var server = Thrift.createStaticHttpThriftServer(server_opt);
+    var port = 9099;
+    server.listen(port);
+    console.log("Http/Thrift Server running on port: " + port);`
diff --git a/lib/js/package.json b/lib/js/package.json
index 5a8485b..5cdd145 100644
--- a/lib/js/package.json
+++ b/lib/js/package.json
@@ -7,6 +7,8 @@
     "grunt-contrib-jshint": "~0.6.3",
     "grunt-contrib-qunit": "~0.2.2",
     "grunt-contrib-concat": "~0.3.0",
-    "grunt-jsdoc": "~0.4.0"
+    "grunt-jsdoc": "~0.4.0",
+    "grunt-external-daemon": "~1.1.0",
+    "grunt-shell": "~0.6.4"
   }
-}
\ No newline at end of file
+}
diff --git a/lib/js/src/thrift.js b/lib/js/src/thrift.js
index 4e1f20f..d605ab7 100644
--- a/lib/js/src/thrift.js
+++ b/lib/js/src/thrift.js
@@ -306,12 +306,13 @@
      * or the async parameter is True or the URL is an empty string, the current 
      * send buffer is returned.
      * @param {object} async - If true the current send buffer is returned.
+     * @param {object} callback - Optional async callback function, receives the call result.
      * @returns {undefined|string} Nothing or the current send buffer.
      * @throws {string} If XHR fails.
      */
-    flush: function(async) {
+    flush: function(async, callback) {
         //async mode
-        if (async || this.url === undefined || this.url === '') {
+        if ((async && !callback) || this.url === undefined || this.url === '') {
             return this.send_buf;
         }
 
@@ -321,8 +322,15 @@
             xreq.overrideMimeType('application/json');
         }
 
-        xreq.open('POST', this.url, false);
+        if (callback) {
+            xreq.onreadystatechange = callback;
+        }
+
+        xreq.open('POST', this.url, !!async);
         xreq.send(this.send_buf);
+        if (async && callback) {
+            return;
+        }
 
         if (xreq.readyState != 4) {
             throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
@@ -1152,3 +1160,4 @@
     return client;
 };
 
+
diff --git a/lib/js/test/test-jq.js b/lib/js/test/test-jq.js
new file mode 100644
index 0000000..ed658e4
--- /dev/null
+++ b/lib/js/test/test-jq.js
@@ -0,0 +1,161 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License. You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied. See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+ /* jshint -W100 */

+ 

+/*

+ * JavaScript test suite for ThriftTest.thrift. These tests

+ * will run only with jQuery (-gen js:jquery) Apache Thrift 

+ * interfaces. To create client code:

+ *      $ thrift -gen js:jquery ThriftTest.thrift

+ *

+ * See also:

+ * ++ test.js for generic tests  

+ * ++ test-nojq.js for "-gen js" only tests

+ */

+

+var transport = new Thrift.Transport("/service");

+var protocol  = new Thrift.Protocol(transport);

+var client    = new ThriftTest.ThriftTestClient(protocol);

+

+//////////////////////////////////

+//jQuery asynchronous tests

+jQuery.ajaxSetup({ timeout: 0 });

+$(document).ajaxError( function() { QUnit.start(); } );

+

+module("jQ Async Manual");

+

+  test("testI32", function() {

+    expect( 2 );

+    QUnit.stop();

+

+    var transport = new Thrift.Transport();

+    var protocol  = new Thrift.Protocol(transport);

+    var client    = new ThriftTest.ThriftTestClient(protocol);

+

+    var jqxhr = jQuery.ajax({

+      url: "/service",

+      data: client.send_testI32(Math.pow(-2,31)),

+      type: "POST",

+      cache: false,

+      dataType: "text",

+      success: function(res){

+        transport.setRecvBuffer( res );

+        equal(client.recv_testI32(), Math.pow(-2,31));

+      },

+      error: function() { ok(false); },

+      complete: function() {

+        ok(true);

+        QUnit.start();

+      }

+    });

+  });

+

+  test("testI64", function() {

+    expect( 2 );

+    QUnit.stop();

+

+    var transport = new Thrift.Transport();

+    var protocol  = new Thrift.Protocol(transport);

+    var client    = new ThriftTest.ThriftTestClient(protocol);

+

+    jQuery.ajax({

+      url: "/service",

+      //This is usually 2^61 but JS cannot represent anything over 2^52 accurately

+      data: client.send_testI64(Math.pow(-2,52)),

+      type: "POST",

+      cache: false,

+      dataType: "text",

+      success: function(res){

+        transport.setRecvBuffer( res );

+        //This is usually 2^61 but JS cannot represent anything over 2^52 accurately

+        equal(client.recv_testI64(), Math.pow(-2,52));

+      },

+      error: function() { ok(false); },

+      complete: function() {

+        ok(true);

+        QUnit.start();

+      }

+    });

+  });

+

+

+module("jQ Async");

+  test("I32", function() {

+    expect( 3 );

+

+    QUnit.stop();

+    client.testI32(Math.pow(2,30), function(result) {

+      equal(result, Math.pow(2,30));

+      QUnit.start();

+    });

+

+    QUnit.stop();

+    var jqxhr = client.testI32(Math.pow(-2,31), function(result) {

+      equal(result, Math.pow(-2,31));

+    });

+

+    jqxhr.success(function(result) {

+      equal(result, Math.pow(-2,31));

+      QUnit.start();

+    });

+  });

+

+  test("I64", function() {

+    expect( 4 );

+

+    QUnit.stop();

+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately

+    client.testI64(Math.pow(2,52), function(result) {

+      equal(result, Math.pow(2,52));

+      QUnit.start();

+    });

+

+    QUnit.stop();

+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately

+    client.testI64(Math.pow(-2,52), function(result) {

+      equal(result, Math.pow(-2,52));

+    })

+    .error( function(xhr, status, e) {  ok(false, e.message); } )

+    .success(function(result) {

+      //This is usually 2^60 but JS cannot represent anything over 2^52 accurately

+      equal(result, Math.pow(-2,52));

+    })

+    .complete(function() {

+      ok(true);

+      QUnit.start();

+    });

+  });

+

+  test("Xception", function() {

+    expect( 2 );

+

+    QUnit.stop();

+

+    var dfd = client.testException("Xception", function(result) {

+      ok(false);

+      QUnit.start();

+    })

+    .error(function(xhr, status, e){

+      equal(e.errorCode, 1001);

+      equal(e.message, "Xception");

+      //QUnit.start();

+      //Note start is not required here because:

+      //$(document).ajaxError( function() { QUnit.start(); } );

+    });

+  });

diff --git a/lib/js/test/test-nojq.html b/lib/js/test/test-nojq.html
new file mode 100644
index 0000000..290816e
--- /dev/null
+++ b/lib/js/test/test-nojq.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Thrift Javascript Bindings: Unit Test</title>
+
+  <script src="build/js/thrift.js"         type="text/javascript" charset="utf-8"></script>
+  <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
+  <script src="gen-js/ThriftTest.js"       type="text/javascript" charset="utf-8"></script>
+
+  <!-- QUnit Test framework-->
+  <script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.14.0.js" charset="utf-8"></script>
+  <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.14.0.css" type="text/css" media="screen" />
+  
+  <!-- the Test Suite-->
+  <script type="text/javascript" src="test.js" charset="utf-8"></script>
+  <script type="text/javascript" src="test-nojq.js" charset="utf-8"></script>
+</head>
+<body>
+  <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=test/ThriftTest.thrift;hb=HEAD">ThriftTest.thrift</a>)</h1>
+  <h2 id="qunit-banner"></h2>
+  <div id="qunit-testrunner-toolbar"></div> 
+  <h2 id="qunit-userAgent"></h2>
+  <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
+  <p>
+      <a href="http://validator.w3.org/check/referer"><img
+          src="http://www.w3.org/Icons/valid-xhtml10"
+          alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+  </p>
+</body>
+</html>
+
diff --git a/lib/js/test/test-nojq.js b/lib/js/test/test-nojq.js
new file mode 100644
index 0000000..f67ea62
--- /dev/null
+++ b/lib/js/test/test-nojq.js
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /* jshint -W100 */
+ 
+/*
+ * JavaScript test suite for ThriftTest.thrift. These tests
+ * will run only with normal "-gen js" Apache Thrift interfaces.
+ * To create client code:
+ *      $ thrift -gen js ThriftTest.thrift
+ *
+ * See also:
+ * ++ test.js for generic tests  
+ * ++ test-jq.js for "-gen js:jquery" only tests
+ */
+
+var transport = new Thrift.Transport("/service");
+var protocol  = new Thrift.Protocol(transport);
+var client    = new ThriftTest.ThriftTestClient(protocol);
+
+//////////////////////////////////
+//Async exception tests
+
+module("NojQ Async");
+
+  test("Xception", function() {
+    expect( 2 );
+
+    QUnit.stop();
+
+    client.testException("Xception", function(result) {
+      equal(result.errorCode, 1001);
+      equal(result.message, "Xception");
+      QUnit.start();
+    });
+  });
+
diff --git a/lib/js/test/test.html b/lib/js/test/test.html
index 93894c7..8f9e7ee 100755
--- a/lib/js/test/test.html
+++ b/lib/js/test/test.html
@@ -1,51 +1,52 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements. See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership. The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License. You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied. See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  <title>Thrift Javascript Bindings: Unit Test</title>
-
-  <script src="/src/thrift.js"                  type="text/javascript" charset="utf-8"></script>
-  <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>
-  <script src="gen-js/ThriftTest.js"       type="text/javascript" charset="utf-8"></script>
-
-  <!-- jQuery -->
-  <script type="text/javascript" src="build/js/lib/jquery-1.7.2.js" charset="utf-8"></script>
-  
-  <!-- QUnit Test framework-->
-  <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>
-  <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />
-  
-  <!-- the Test Suite-->
-  <script type="text/javascript" src="test.js" charset="utf-8"></script>
-</head>
-<body>
-  <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=test/ThriftTest.thrift;hb=HEAD">ThriftTest.thrift</a>)</h1>
-  <h2 id="qunit-banner"></h2>
-  <div id="qunit-testrunner-toolbar"></div> 
-  <h2 id="qunit-userAgent"></h2>
-  <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>
-  <p>
-      <a href="http://validator.w3.org/check/referer"><img
-          src="http://www.w3.org/Icons/valid-xhtml10"
-          alt="Valid XHTML 1.0!" height="31" width="88" /></a>
-  </p>
-</body>
-</html>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

+<!--

+  Licensed to the Apache Software Foundation (ASF) under one

+  or more contributor license agreements. See the NOTICE file

+  distributed with this work for additional information

+  regarding copyright ownership. The ASF licenses this file

+  to you under the Apache License, Version 2.0 (the

+  "License"); you may not use this file except in compliance

+  with the License. You may obtain a copy of the License at

+

+    http://www.apache.org/licenses/LICENSE-2.0

+

+  Unless required by applicable law or agreed to in writing,

+  software distributed under the License is distributed on an

+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+  KIND, either express or implied. See the License for the

+  specific language governing permissions and limitations

+  under the License.

+-->

+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

+<head>

+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

+  <title>Thrift Javascript Bindings: Unit Test</title>

+

+  <script src="build/js/thrift.js"         type="text/javascript" charset="utf-8"></script>

+  <script src="gen-js/ThriftTest_types.js" type="text/javascript" charset="utf-8"></script>

+  <script src="gen-js/ThriftTest.js"       type="text/javascript" charset="utf-8"></script>

+

+  <!-- jQuery -->

+  <script type="text/javascript" src="build/js/lib/jquery-1.7.2.js" charset="utf-8"></script>

+  

+  <!-- QUnit Test framework-->

+  <script type="text/javascript" src="build/js/lib/qunit.js" charset="utf-8"></script>

+  <link rel="stylesheet" href="build/js/lib/qunit.css" type="text/css" media="screen" />

+  

+  <!-- the Test Suite-->

+  <script type="text/javascript" src="test.js" charset="utf-8"></script>

+  <script type="text/javascript" src="test-jq.js" charset="utf-8"></script>

+</head>

+<body>

+  <h1 id="qunit-header">Thrift Javascript Bindings: Unit Test (<a href="https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=test/ThriftTest.thrift;hb=HEAD">ThriftTest.thrift</a>)</h1>

+  <h2 id="qunit-banner"></h2>

+  <div id="qunit-testrunner-toolbar"></div> 

+  <h2 id="qunit-userAgent"></h2>

+  <ol id="qunit-tests"><li><!-- get valid xhtml strict--></li></ol>

+  <p>

+      <a href="http://validator.w3.org/check/referer"><img

+          src="http://www.w3.org/Icons/valid-xhtml10"

+          alt="Valid XHTML 1.0!" height="31" width="88" /></a>

+  </p>

+</body>

+</html>

diff --git a/lib/js/test/test.js b/lib/js/test/test.js
index b0904a9..7351fd9 100755
--- a/lib/js/test/test.js
+++ b/lib/js/test/test.js
@@ -1,4 +1,4 @@
-/*
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements. See the NOTICE file
  * distributed with this work for additional information
@@ -19,7 +19,32 @@
  /* jshint -W100 */
  
 /*
- * JavaScript test suite
+ * JavaScript test suite for ThriftTest.thrift. These tests
+ * will run against Normal (-gen js) and jQuery (-gen js:jquery)
+ * Apache Thrift interfaces.
+ *
+ * Synchronous blocking calls should be identical in both 
+ * Normal and jQuery interfaces. All synchronous tests belong
+ * here.
+ * 
+ * Asynchronous sucess callbacks passed as the last parameter 
+ * of an RPC call should be identical in both Normal and jQuery
+ * interfaces. Async success tests belong here.
+ * 
+ * Asynchronous exception processing is different in Normal
+ * and jQuery interfaces. Such tests belong in the test-nojq.js
+ * or test-jq.js files respectively. jQuery specific XHR object
+ * tests also belong in test-jq.js. Do not create any jQuery 
+ * dependencies in this file or in test-nojq.js
+ *
+ * To compile client code for this test use:
+ *      $ thrift -gen js ThriftTest.thrift
+ *      -- or --
+ *      $ thrift -gen js:jquery ThriftTest.thrift
+ *
+ * See also:
+ * ++ test-nojq.js for "-gen js" only tests  
+ * ++ test-jq.js for "-gen js:jquery" only tests
  */
 
 var transport = new Thrift.Transport("/service");
@@ -74,8 +99,9 @@
   });
   test("I64", function() {
     equal(client.testI64(0), 0);
-    equal(client.testI64(Math.pow(2,60)), Math.pow(2,60));
-    equal(client.testI64(-Math.pow(2,60)), -Math.pow(2,60));
+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+    equal(client.testI64(Math.pow(2,52)), Math.pow(2,52));
+    equal(client.testI64(-Math.pow(2,52)), -Math.pow(2,52));
   });
 
 
@@ -86,7 +112,8 @@
     structTestInput.string_thing = 'worked';
     structTestInput.byte_thing = 0x01;
     structTestInput.i32_thing = Math.pow(2,30);
-    structTestInput.i64_thing = Math.pow(2,60);
+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+    structTestInput.i64_thing = Math.pow(2,52);
 
     var structTestOutput = client.testStruct(structTestInput);
 
@@ -103,7 +130,8 @@
     xtrTestInput.string_thing = 'worked';
     xtrTestInput.byte_thing = 0x01;
     xtrTestInput.i32_thing = Math.pow(2,30);
-    xtrTestInput.i64_thing = Math.pow(2,60);
+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+    xtrTestInput.i64_thing = Math.pow(2,52);
     
     var nestTestInput = new ThriftTest.Xtruct2();
     nestTestInput.byte_thing = 0x02;
@@ -206,12 +234,16 @@
   });
 
   test("TException", function() {
+    //ThriftTest does not list TException as a legal exception so it will
+    // generate an exception on the server that does not propagate back to 
+    // the client. This test has been modified to equate to "no exception"
     expect(1);
     try{
       client.testException("TException");
     } catch(e) {
-      ok(true);
+      //ok(false);
     }
+    ok(true);
   });
 
 
@@ -265,64 +297,6 @@
 
 //////////////////////////////////
 //Run same tests asynchronously
-jQuery.ajaxSetup({ timeout: 0 });
-$(document).ajaxError( function() { QUnit.start(); } );
-
-module("Async Manual");
-
-  test("testI32", function() {
-    expect( 2 );
-    QUnit.stop();
-
-    var transport = new Thrift.Transport();
-    var protocol  = new Thrift.Protocol(transport);
-    var client    = new ThriftTest.ThriftTestClient(protocol);
-
-    var jqxhr = jQuery.ajax({
-      url: "/service",
-      data: client.send_testI32(Math.pow(-2,31)),
-      type: "POST",
-      cache: false,
-      dataType: "text",
-      success: function(res){
-        transport.setRecvBuffer( res );
-        equal(client.recv_testI32(), Math.pow(-2,31));
-      },
-      error: function() { ok(false); },
-      complete: function() {
-        ok(true);
-        QUnit.start();
-      }
-    });
-  });
-
-
-  test("testI64", function() {
-    expect( 2 );
-    QUnit.stop();
-
-    var transport = new Thrift.Transport();
-    var protocol  = new Thrift.Protocol(transport);
-    var client    = new ThriftTest.ThriftTestClient(protocol);
-
-    jQuery.ajax({
-      url: "/service",
-      data: client.send_testI64(Math.pow(-2,61)),
-      type: "POST",
-      cache: false,
-      dataType: "text",
-      success: function(res){
-        transport.setRecvBuffer( res );
-        equal(client.recv_testI64(), Math.pow(-2,61));
-      },
-      error: function() { ok(false); },
-      complete: function() {
-        ok(true);
-        QUnit.start();
-      }
-    });
-  });
-
 
 module("Async");
 
@@ -347,7 +321,7 @@
   });
 
   test("I32", function() {
-    expect( 3 );
+    expect( 2 );
 
     QUnit.stop();
     client.testI32(Math.pow(2,30), function(result) {
@@ -356,53 +330,26 @@
     });
 
     QUnit.stop();
-    var jqxhr = client.testI32(Math.pow(-2,31), function(result) {
-      equal(result, Math.pow(-2,31));
-    });
-
-    jqxhr.success(function(result) {
+    client.testI32(Math.pow(-2,31), function(result) {
       equal(result, Math.pow(-2,31));
       QUnit.start();
     });
   });
 
   test("I64", function() {
-    expect( 4 );
-
-    QUnit.stop();
-    client.testI64(Math.pow(2,60), function(result) {
-      equal(result, Math.pow(2,60));
-      QUnit.start();
-    });
-
-    QUnit.stop();
-    client.testI64(Math.pow(-2,61), function(result) {
-      equal(result, Math.pow(-2,61));
-    })
-    .error( function(xhr, status, e) {  ok(false, e.message); } )
-    .success(function(result) {
-      equal(result, Math.pow(-2,61));
-    })
-    .complete(function() {
-      ok(true);
-      QUnit.start();
-    });
-  });
-
-  test("Xception", function() {
     expect( 2 );
 
     QUnit.stop();
-
-    var dfd = client.testException("Xception", function(result) {
-      ok(false);
+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+    client.testI64(Math.pow(2,52), function(result) {
+      equal(result, Math.pow(2,52));
       QUnit.start();
-    })
-    .error(function(xhr, status, e){
-      equal(e.errorCode, 1001);
-      equal(e.message, "Xception");
-      //QUnit.start();
-      //Note start is not required here because:
-      //$(document).ajaxError( function() { QUnit.start(); } );
+    });
+
+    QUnit.stop();
+    //This is usually 2^60 but JS cannot represent anything over 2^52 accurately
+    client.testI64(Math.pow(-2,52), function(result) {
+      equal(result, Math.pow(-2,52));
+      QUnit.start();
     });
   });
diff --git a/lib/nodejs/test/client.js b/lib/nodejs/test/client.js
index e7d6e61..90d7467 100644
--- a/lib/nodejs/test/client.js
+++ b/lib/nodejs/test/client.js
@@ -17,10 +17,9 @@
  * under the License.
  */
 
-//Client test for the following I/O stack:
-//    TBinaryProtocol
-//    TFramedTransport
-//    TSocket
+//This is the client side test for the standard Apache Thrift
+//"ThriftTest" suite. This client will test any protocol/transport
+//combination specified on the command line.
 
 var assert = require('assert');
 var thrift = require('thrift');
@@ -32,8 +31,8 @@
 var program = require('commander');
 
 program
-  .option('-p, --protocol <protocol>', 'Set thift protocol (binary|json) [protocol]')
-  .option('-t, --transport <transport>', 'Set thift transport (buffered|framed) [transport]')
+  .option('-p, --protocol <protocol>', 'Set thrift protocol (binary|json) [protocol]')
+  .option('-t, --transport <transport>', 'Set thrift transport (buffered|framed) [transport]')
   .parse(process.argv);
 
 var protocol = undefined;
diff --git a/lib/nodejs/test/test_handler.js b/lib/nodejs/test/test_handler.js
index e697408..33c8941 100644
--- a/lib/nodejs/test/test_handler.js
+++ b/lib/nodejs/test/test_handler.js
@@ -150,7 +150,7 @@
   testMulti: function(arg0, arg1, arg2, arg3, arg4, arg5, result) {

     console.log('testMulti()');

 

-    var hello = new ttypes.Xtruct();;

+    var hello = new ttypes.Xtruct();

     hello.string_thing = 'Hello2';

     hello.byte_thing = arg0;

     hello.i32_thing = arg1;

@@ -178,11 +178,11 @@
       x.message = 'This is an Xception';

       result(x);

     } else if (arg0 === ('Xception2')) {

-      var x = new ttypes.Xception2();

-      x.errorCode = 2002;

-      x.struct_thing = new ttypes.Xtruct();

-      x.struct_thing.string_thing = 'This is an Xception2';

-      result(x);

+      var x2 = new ttypes.Xception2();

+      x2.errorCode = 2002;

+      x2.struct_thing = new ttypes.Xtruct();

+      x2.struct_thing.string_thing = 'This is an Xception2';

+      result(x2);

     }

 

     var res = new ttypes.Xtruct();

@@ -192,4 +192,4 @@
   testOneway: function(sleepFor, result) {

     console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');

   }

-}   //ThriftTestSvcHandler

+};   //ThriftTestSvcHandler