Table of Contents

Hop

This module contains utilities for getting, controlling, and using the Hop server. The module defines functions to craft service responses, and a broadcast function that lets a server send events to registered remote clients.

The module also defines an API to invoke third party WebServices.

For ease of use, hop is defined as a global object and can be used directly without require.

Server Information

The server properties defined below are read-only.

hop.port

The port number of the running Hop server. To set the port and protocol for the Hop server, see config.

console.log( "port:", hop.port );

hop.hostname

The host name of the running Hop server.

console.log( "hostname:", hop.hostname );

hop.version

The Hop version.

console.log( "Hop version:", hop.version );

Responses

Service result values are transformed into Hop responses before being sent to the clients.

hop.HTTPResponseHop( obj, [option] )

This class is used to respond values to client requests.

service getObj() {
  return hop.HTTPResponseHop( { key: "foo", value: [ 1,2 3 ] } );

Note: In normal situation, it is not necessary to explicitly build the HTTPResponseHop object as the runtime system automatically constructs one when the response of a service is a compound JavaScript object.

hop.HTTPResponseXml( obj, [option] )

This class is used to deliver XML documents to client.

service getXml() {
  return hop.HTTPResponseXml( <div>a div</div> );

Note: In normal situation, it is not necessary to explicitly build the HTTPResponseXml object as the runtime system automatically constructs one when the response of a service is an XML fragment.

hop.HTTPResponseString( string, [option] )

This class is used to deliver plain character strings to client.

service getXml() {
  return hop.HTTPResponseString(
    "This resource does not exist here!",
    { startLine: "HTTP/1.0 404 File not found" } ) 

hop.HTTPREsponseJson( object )

This convenience function returns an [application/json] value from a JavaScript object. It is the same as:

hop.HTTPResponseString( JSON.stringify( obj ), { contentType: 'application/json' } )

hop.HTTPResponseFile( path, [option] )

This class is used to respond files to clients. The argument path is the full path of a existing file.

Example

This example shows the most efficient way to deliver content file to client. Using a HTTPResponseFile object deliver much better performance than reading the file content first and then seding a buffer or a string the client.

In this example, the file replied to the client is to be interpreted as a plain text file alhgouth it is a JavaScript program. To tell the Web browser not to interpret the file, the mime type text/plain is specified in the response option.

The charset encoding of the file is also provided using the charset attribute.

file/file.js

service file() {
   var pre = <pre/>;

   return <html>
      ~{
         var entityMap = {
            "&": "&amp;",
            "<": "&lt;",
            ">": "&gt;",
            '"': '&quot;',
            "'": '&#39;',
            "/": '&#x2F;'
         };
         
         function escapeHTML( string ) {
            return String( string ).replace(
                  /[&<>"'\/]/g,
               function ( s ) {
                  return entityMap[s];
               } );
         }
      }
      <button onclick=~{
            var file = ${fileGet.resource( "file.js" )};

            ${fileGet}( file )
            .post( function( txt ) {
               ${pre}.innerHTML = escapeHTML( txt )
            } );
      }>
         click me
      </button>
     ${pre}
   </html>;
}

service fileGet( path ) {
   return hop.HTTPResponseFile( path,
                                { contentType: "text/plain",
                                  charset: hop.locale } );
}
                                          
console.log( "Go to \"http://%s:%d/hop/file\"", hop.hostname, hop.port );

Note: HTTPResponseFile is a much faster way to send a file to a client, althought, the same behaviour can also be implemented combining standard fs operations and HTTPResponseString values.

hop.HTTPResponseAuthentication( msg, [option] )

This class is used to respond HTTP 401 Unauthorized response to Web client.

Note: the class hop.HTTPResponseAuthentication is a convenience class. The same behavior can be implemented using hop.HTTPResponseString and passing a tagstLine value in the optional argument.

Example

This example shows how to use HTTPResponseAuthentication to request Web browser authentication.

The example counts the number of request (the variable count). Each request decrements the counter but only passes through when it reaches 0.

authentication/authentication.js

var count = 2;

service authenticationAccept() {
   switch( count-- ) {
      case 2:
        return hop.HTTPResponseAuthentication( "I don't know you", this );

      case 1:
        return hop.HTTPResponseAuthentication( "Do you really insist?", this );
      
      case 0:
        count = 2;
        return "Ok for this time";
   }
}

service authentication() {
   var console = <div/>;

   return <html>
      <div>
        Click 3 times the "click me" button.
        Permission granted on the third request.
      </div>
      <button onclick=~{
        ${authenticationAccept}()
          .post( function( v ) { ${console}.innerHTML = v },
                 { fail: function( v ) { ; } } ) }>
        click me
      </button>
      ${console}
   </html>
}

console.log( "Go to \"http://%s:%d/hop/authentication\"", hop.hostname, hop.port );

hop.HTTPResponseError( obj, [option] )

Respond an error value to the client, which either invokes the fail callback of the post service call, or raises an exception.

hop.HTTPResponseAsync( sender, req )

Asynchronous responses are used when a service cannot returns instantly a value to a client because it relies on an asynchronous computation. In that situation, the service must produce a hop.HTTPResponseAsync which is interpreted by the builtin server as a delayed reply.

Example

This example illustraed service declaration with fixed number of argument and asynchronous responses.

The service foo must call the service bar on the same host. Calling the serving synchronously would result in a dead lock as only one thread is in charge of handling services. The call to bar must then be asynchronous. This is acheived by applying the post method of the frame computed with bar( x + 1 ).

The service foo relies on a asynchronous computation. It then cannot respond immediately to the client. It will be in position to reply only when the invocation of bar has completed. This is implemented using a hop.HTTPResponseAsync object. The argument sendReponse is a function automatically created by the runtime system. When invoked with sendReponse( e ) the result of the service bar is replied to the cilent which has called foo.

svc3/svc3.js

service svc3() {
   return <html>
      <button onclick=~{
         ${foo}( 1 )
            .post( function( r ) {
               document.body.appendChild( r );
            } )
      }>click</button>
    </html>;
}

service foo( x ) {
   console.log( "in foo x=", x );
   return hop.HTTPResponseAsync(
      function( sendResponse ) {
         bar( x + 1 ).post( function( e ) {
            sendResponse( e );
         } )
      }, this );
}

service bar( x ) {
   console.log( "in bar x=", x );
   return <div>${ x + 1 }</div>;
}

console.log( "Go to \"http://%s:%d/hop/svc3\"", hop.hostname, hop.port );

Note: the class hop.HTTPResponseAsync is the base class for implementing asynchronous reponses. Returning Promise objects as a similar behavior and is encouraged. The service foo defined above can be implemented as:

service foo( x ) {
   console.log( "in foo x=", x );
   return new Promise( function( resolve, reject ) {
      bar( x + 1 ).post( resolve );
   }
}

Invoking the resolve function actually sends the responds to the client. Invoking the reject as the same effect of responding a HHTPResponseError value.

hop.HTTPResponseProxy( obj, [option] )

The hop.HTTPResponseProxy objects are to be used when a remote resource can be access othwerwise. For instance, these situations arise because of the security enforcement of the Web browsers. Some resources have to be downloaded from the origin server. Using a hop.HTTPResponseProxy object enables the web page to only use local URLs, that are proxied to the actual remote resources by the server.

Example

This example illustrates standard client image manipulations and use of hop.HTTPResponseProxy objects.

Security enforcement of Web browsers prevent manipulation image pixels whose origin differs from the origin of the main page. To workaround this problem, the manipulated image is proxied by the Hop server. The Web browser only sees relative local URLs.

image/image.js

var img_default = "http://t1.gstatic.com/images?q=tbn:ANd9GcRUADxj_7NEl8RAFNM-s6x3Wgp1QIg81QHRMVeOuMHBklr1JWddmQ";

var colors_default = [
   {red: 252, green: 27, blue: 0},
   {red: 125, green: 167, blue: 129},
   {red: 255, green: 122, blue: 10}
];

service imgProxy( url ) {
   return hop.HTTPResponseProxy( url );
}

service image( o ) {
   var url = o && "url" in o ? o.url : img_default;
   
   return <html>
     ~{
        function drawImage( url, colors ) {
           var img = new Image();
           var src = document.getElementById( "src" );
           var dst = document.getElementById( "dst" );
           var ctxsrc = src.getContext( "2d" );
           var ctxdst = dst.getContext( "2d" );

           img.onload = function( e ) {
              src.width = img.width;
              src.height = img.height;
              dst.width = img.width;
              dst.height = img.height;

              ctxsrc.drawImage( img, 0, 0 );
              var f = ctxsrc.getImageData( 0, 0, img.width, img.height );
              ctxdst.putImageData( colorize( f, colors ), 0, 0 );
           }

           img.src = url;
        }

        function colorize( frame, colorset ) {
           var data = frame.data;
           var l = data.length / 4;

           for( var i = 0; i < l; i++ ) {
              var r = data[ i*4 ];
              var g = data[ i*4 + 1 ];
              var b = data[ i*4 + 2 ];
              var range = Math.floor( (0.65*r + 0.22*g + 0.13*b) / 86 );

              var cs = colorset[ range ];
              
              data[ i*4 ] = 0.7*cs.red + 0.3*r;
              data[ i*4 + 1 ] = 0.7*cs.green + 0.3*g;
              data[ i*4 + 2 ] = 0.7*cs.blue + 0.3*b;
           }

           return frame;
        }
        
        window.addEventListener( "load", function( e ) {
           drawImage( ${imgProxy( url )}, ${colors_default} )
        } )
     }
     <img src=${url}/>
     <canvas id="src" style="display: none"/>
     <canvas id="dst"/>
   </html>
}
   
console.log( "Go to \"http://%s:%d/hop/image\"", hop.hostname, hop.port );

Broadcast

Broadcast is an abstraction on top of webSockets to let a Hop server send events to connected clients (either web browsers or Hop client processes). Connections originate from the client to the server, so broadcast can be used even in the asymetric web topology where clients most often lie behind a NAT router or firewall and would not accept a connection from a remote server (forbidding the remote server to invoke services running on the client process).

hop.broadcast( eventName, value )

Generates an event of type eventName with payload value. The event is broadcast over the network to all registered clients. eventName is cast into a String, valuecan be any serializable object, including JavaScript objects, Hop.js services, and xml-elements. Clients register to specific broadcast events with the addEventListenermethod.

hop.broadcast( 'refreshScore', 14 );

hop.signal()

This function is similar to broadcast but only one receiver will be notified of the message.

Server.addEventListener( eventName, handler [, options] )

Use this method on the client side to register to the eventName server event. The effect of this method is to establish a persistent connection with the Hop server, register the client for the given event type, and trigger the handler whenever the event is received by the client. handler takes one argument, the event. The transmitted value can be retrieved in the value property of the event.

When used within a web browser, connection is established with the Hop server serving the current page, the exact syntax is server.addEventListener( eventName, handler ) where server denotes the current server (the runtime system automatically binds the server variable to the current server).

server.addEventListener( 'refreshScore', function( event ) {
  var score = event.value;
  var scoreElement = this.document.getElementById( 'score' );
  // update GUI element with new score

On the server side, server objects are instances of the Server class.

new hop.Server( [ hostname [, port [, authorization [, ssl ] ] ] )

the arguments are as follows:

var srv = new hop.Server( "localhost", 9999 );

srv.addEventListener( 'refreshScore', function( event ) {
   var score = event.value;
   ...
} )

service getScore();
getScore.call( srv, "jean dupont" ).post( v => ... );

WebService

WebService is a set of API that let you invoke third party WebServices the same way you invoke Hop services.

var hop = require( 'hop' );
var mymemory = hop.webService( "http://mymemory.translated.net/api/get" );
mymemory( {q: 'My tailor is rich.', langpair: 'en|fr' } ).post( function( result ) {
   console.log( result.responseData );
   }, { fail: function( error ) {
   console.log( 'failure' );
} });

hop.webService( url )

Use this method to declare a remote WebService,that can later be invoked with named arguments. urlis the url of the WebService. Call the returned function with an object argument containing the named arguments you want to send to the WebService. The returned value is a WebServiceFrame (very similar in use to Service Frames).

WebServiceFrame.post([ success [, fail-or-options]] )

Invokes asynchronously the webService. The optional successargument, when provided, must be a function of one argument, which is set the the value returned by the WebService.

if the optional argument fail-or-options is a procedure, it is invoked if an error occurs during the WebService invocation. If fail-or-options is an object, it contains optional parameters to the WebService invocation.

WebServiceFrame.postSync([ success [, fail-or-option]] )

The synchronous version of post. Returns the value returned by the service. Since postSync blocks the execution of the client process until the service returns a value, it is strongly advised to use the asynchronous post when applicable.

Miscellaneous

hop.charsetConvert( text, source, target )

Converts the text string from charset source into charset target.

url/url.js

"use hopscript";

var mymemory = hop.webService( "http://mymemory.translated.net/api/get" );

function translateText( text, lang = "en|fr" ) {
   var o = mymemory( { q: text, langpair: lang } ).postSync();
   
   if( o.responseStatus === 200 ) {
      var t = o.responseData.translatedText;
      
      return hop.charsetConvert( unescape( t ), "UTF-8" );
   }
}

service url() {
   var output = <div/>;
   var input = <input value="toto n'est pas content"/>;
   var select = <select>
     <option label="fr->en" value="fr|en">fr-&gt;en</option>
     <option label="en->fr" value="en|fr">en-&gt;fr</option>
   </select>
      
   var translate = service( text, langpair ) {
      return translateText( text, langpair );
   };
      
   return <html>
     <div>
       ${select}
       ${input}
       <button onclick=~{
          ${translate}( ${input}.value, ${select}.value )
             .post( function( v ) { ${output}.innerHTML = v; } )}>
         translate
       </button>
       ${output}
     </div>
   </html>;
}

console.log( "Go to \"http://%s:%d/hop/url\"", hop.hostname, hop.port );

hop.encodeURIComponent( string )

Encodes a string into a valid URI component.

hop.encodeURIComponent( 'jean dupont' );
// "jean%20dupont"

hop.Cons()

This function is a constructor to create native (Bigloo) objects.

hop.List()

This function is a constructor to create native (Bigloo) objects.

hop.md5sum( string )

Computes the md5sum of a string.

hop.md5sum( 'jean dupont' );
// "b38bed581de7b86dd6fc8355c73cebf2"

hop.sha1sum( string )

Computes the sha1 sum of a string.

hop.sha1sum( 'jean dupont' );
// "7461340811509ec24dd1c1a32504a01e24423768"

hop.compileXML( node [, ofile] [, backend] )

Compile a XML node into HTML. If no output file is specified, the product of the compilation is returned in a buffer. The optional backend argument is a string denoting the HTML version to be used for the compilation.

var node = <html><div onclick=~{alert( "clicked" )}>click me</div></html>
console.log( hop.compileXML( node, false, "html5" ) );

Note: explicit compilation to HTML using hop.compileXML is unncessary for service responses. Services can directly return XML objects in response to HTTP requests.

Sub Modules

The following properties lead to sub modules that can be loaded using the require function.

var hop = require( 'hop' );
var config = require( hop.config );

hop.config

See config.

hop.fontifier

hop.markdown

See markdown.

hop.notepad

hop.security

hop.spage

See spage.

hop.tree

See tree.

hop.user

See user.

hop.wiki