To make it easier, there is a Javascript library.
First, I will describe the mechanism for simply sending and receiving Karoo Project messages. These messages are normally sent as UDP/IP datagrams, which are asynchronous in nature. The Javascript library is hindered by the fact that it must use HTTP, which is synchronous. Hence, the surf application implements a gateway, so that the Javascript client can poll the gateway and get any messages that have arrived. From your Javascript application's point of view, the messages do arrive asynchronously, though with some latency.., and your application does need to manage the polling (start it, set up the frequency, and stop it.)
See the online reference for the Javascript interface:
<script type="text/javascript" src="/surf/client.js"></script> <script type="text/javascript" src="/surf/cave.js"></script>
This is actually a "higher level" interface, because the Javascript function hides a lot of the internal workings of the code that communicates with the gateway.
The function is called "sendCaveQuery", and it takes 5 arguments:
var params = new Array(); params[0] = new caveParam("price", DB_DATA_TYPE_FLOAT, price_field.value); params[1] = new caveParam("bedrooms", DB_DATA_TYPE_INT_32, parseInt(bedrooms_field.value)); params[2] = new caveParam("bathrooms", DB_DATA_TYPE_INT_32, parseInt(bathrooms_field.value)); params[3] = new caveParam("ensuites", DB_DATA_TYPE_INT_32, parseInt(ensuites_field.value)); params[4] = new caveParam("garages", DB_DATA_TYPE_INT_32, parseInt(garages_field.value)); params[5] = new caveParam("carports", DB_DATA_TYPE_INT_32, parseInt(carports_field.value)); params[6] = new caveParam("id", DB_DATA_TYPE_INT_32, id); params[7] = new caveParam("sold", DB_DATA_TYPE_BOOL, sold_field.checked); params[8] = new caveParam("sessionid", DB_DATA_TYPE_INT_32, sessionid); sendCaveQuery("specs", "set-specs", params, doneSpecsCallback, "ke-cave");
This example matches up with the service which is defined in the cave configuration file:
<service name="set specifications" id="set-specs"> <sql transaction="commit"> select setspecs as success from setSpecs( <parameter name="id" type="integer"/>, <parameter name="price" type="float"/>, <parameter name="bedrooms" type="integer"/>, <parameter name="bathrooms" type="integer"/>, <parameter name="ensuites" type="integer"/>, <parameter name="garages" type="integer"/>, <parameter name="carports" type="integer"/>, <parameter name="sold" type="boolean"/>, <parameter name="sessionid" type="integer"/>); </sql> </service>
The sendCaveQuery() function includes a callback. That is important, because it is the only way you can get a reply. The communication is asynchronous, which means that the reply can't simply be returned by the sendCaveQuery() function.
In the example above, the doneSpecsCallback is specified as the callback. The callback function prototype must be as so:
function callbackFunction(trans)
... where the "trans" parameter is a caveTransaction object containing the results. The trans object is actually an array or arrays, of arrays. Before describing this, I'll first point out that there is a convenience method for dealing with 99% of the responses which will probably just contain a single row success/failure type of response:
E.g.
var params = new Array(); params[0] = new caveParam("success"); findParametersInTransaction(trans, params); if (params[0].value && parseBoolean(params[0].value)) { ... }
In this example, the findParametersInTransaction() will look for just one column named "success". You could of course pass many parameters for it to look for in the reply.
I.e.
var number_of_sqls = trans.sqls.length; for var i = 0; i < number_of_sqls; i++) { var sql = trans.sqls[i]; var number_of_rows = sql.length; for (var j = 0; j < number_of_rows; j++) { var row = sql[j]; var number_of_columns = row.length; for (var k = 0; k < number_of_columns; k++) { var col = row[k]; var column_name = col.name; var column_value = col.value; var column_type = col.type; switch (column_type) { case DB_DATA_TYPE_NULL: ... break; case DB_DATA_TYPE_UNSIGNED_8: ... break; case DB_DATA_TYPE_BOOL: ... break; case DB_DATA_TYPE_CHAR: ... break; case DB_DATA_TYPE_UNSIGNED_16: ... break; case DB_DATA_TYPE_UNSIGNED_32: ... break; case DB_DATA_TYPE_UNSIGNED_64: ... break; case DB_DATA_TYPE_INT_8: ... break; case DB_DATA_TYPE_INT_16: ... break; case DB_DATA_TYPE_INT_32: ... break; case DB_DATA_TYPE_INT_64: ... break; case DB_DATA_TYPE_FLOAT: ... break; case DB_DATA_TYPE_DOUBLE: ... break; case DB_DATA_TYPE_LONG_DOUBLE: ... break; case DB_DATA_TYPE_STRING: ... break; } } } }
<form action="/www.yourhost.com/" method="post" enctype="multipart/form-data" target="image-submit-output" id="new-picture-form" onsubmit="return prepareFileSend('image-button-hex', 'image', 'set-image', 'id', 2, 'fileDoneCallback1');"> <p> <input id="image-file-field" type="file" name="image" tabindex="1" /> <input type="hidden" name="type" value="11"/> <input id="image-button-hex" type="hidden" name="hex" value=""/> <input type="hidden" name="rock-type" value="cave"/> <input type="hidden" name="rock-name" value="ke-cave"/> <input type="submit" value="Save" id="new-picture-submit" /> </p> </form> <iframe name="image-submit-output" id="image-submit-output" src="/surf/foutput.html"> </iframe>
The method called by the submit button (as specified by the onsubmit attribute of the FORM) must set up a few things, and then return true. It must return true, because otherwise the form won't be submitted via an HTTP POST, as it needs to be.
Note that the destination host name must be the action string, enclosed in '/' slash characters.
The function in the above example is not a library function. You need to write your own. I've included this function as an example:
function prepareFileSend(hex_id, name, id, id_key, id_val, calback_name) { var callback; eval("callback = "+calback_name+";"); var trans = beginTransaction(name, callback); var params = new Array(); params[0] = new caveParam(id_key, DB_DATA_TYPE_INT_32, id_val); params[1] = new caveParam("sessionid", DB_DATA_TYPE_INT_32, sessionid); var hex = getHexOfPreparedCaveQuery(name, id, params, trans.seq); document.getElementById(hex_id).value = hex; cave_poller.poll(); return true; }
The function must create a transaction, by calling beginTransaction(). Then it must create the beginning of the message, hex encoded, by calling getHexOfPreparedCaveQuery(). (This hex encoded string must be set as the value of the hidden INPUT element named "hex" in the form.)
The getHexOfPreparedCaveQuery() function must be passed the following arguments:
See also
1.5.8