Skip to content

Core Features

StephenCote edited this page Sep 27, 2013 · 4 revisions

Namespaces and Libraries

The Hemi JavaScript Framework uses an anonymous closure to load javascript statements (Hash/JSON format). The core framework file includes the foundational components to handle namespaces, importing other scripts, XML/AJAX/JSON, the Object Registry, and the Message Service.

Hemi libraries uses the namespace utility to load itself into the framework, and use the include utility to identify dependencies. The compiler utility, PackageManager, includes a linker that removes the dependency statements if they have been included in the compiled file.

External scripts may be loaded using the include method. The include method assumes that the file ends with .js and that the contents include javascript. Otherwise, no expectation or framework conformance is expected or required. The include method caches the requested script, and may be instructed to not evaluate it. For example, most framework libraries evaluate the imported script, while [Modules] and [Tests] include the raw script but do not evaluate it at the time of the request.

Any script may be loaded in this fashion, such as:

(function(){
   /// Your script here.
}());

Scripts may use the include and namespace methods to define requirements and context:

(function () {
   Hemi.include("demo.dependency");
   Hemi.namespace("demo.namespace", HemiEngine, {
      /// Hash format
   });
}());

API: Hemi

Services

The Hemi JavaScript Framework comprises a set of services and utilities. The framework namespace feature includes special handling for libraries that follow the service model. An instance of a service with the serviceImpl constructor are automatically instantiated as service.

The following is an example of a generic service.

(function () {
   HemiEngine.include("hemi.object");
   HemiEngine.include("hemi.util.logger");
   HemiEngine.namespace("demo.namespace", HemiEngine, {
      service:null,
      serviceImpl:function(){
         // Setup the object as a framework object
         //
         HemiEngine.prepareObject("service_name", "version", true, this, true);
         // Add logging capabilities to this object
         //
         HemiEngine.util.logger.addLogger(this, "Service", "The Service", "001");
         // Add 'custom' object array, with named accessor methods (eg: getCustom)
         HemiEngine.object.addObjectAccessor(this, "custom");
     
         // If not registering the object via prepareObject, 
         // this object may be registered with the following statement
         // 
         //Hemi.transaction.service.register(this, 1);
      }
   });
}());

Objects

Framework objects must conform to the Hemi Framework API.

Base Object

The Base Framework Object API can be added to existing objects via the prepareObject method, or new objects created with the newObject method will include this API.

  • object_id : A guid value.
  • getObjectId() : Public method to return object_id.
  • object_version : The version of the object.
  • getObjectVersion() : Public method to return object_version.
  • object_type : The type of the object.
  • getObjectType() : Public method to return object_type.
  • ready_state : Integer value representing the state of the object, with 1 being uninitialized, 4 being ready, and 5 being destroyed.
  • getReadyState() : Public method to return the ready_state.
  • properties : A hash for storing configuration values.
  • getProperties() : Public method to return the properties hash.
  • objects : A hash for storing object pointers.
  • getObjects() : Public method to return the objects hash.

Preparing Objects

The prepare method is used to instrument an existing object for the framework. The following example prepares an existing object for the framework and registers it with the Object Registry.

var oSomeObject = ...;
Hemi.prepareObject("custom", "1.0", true, oSomeObject);

New Objects

New objects are created with a similar statement. The following example instruments an object deconstructor and includes optional call back examples for when an object is prepared, created, and destroyed.

var oNewObject = Hemi.newObject("custom","1.0",true,true,{
  object_prepare:function(){
  },
  object_created:function(){
  },
  object_destroy:function(){
  }
});

API: Hemi

Object Registry

The Object Registry is a central repository for all Hemi framework objects. Objects must conform to the framework API to be added (via prepareObject or newObject), and are stored against a unique or specified object id.

Registration Example

var oNewObject = Hemi.newObject("custom","1.0",true);
var sId = oNewObject.getObjectId();
// retrieve the new object by its id.
//
var oLookup = Hemi.registry.service.getObject(sId);

Destruction

The Object Registry service includes two methods for managing object destruction, sendSigterm and sendDestroyTo. When creating or preparing a framework object, the option to add the destruction call back is provided through the API: Hemi.object addObjectDeconstructor decorator. This method injects the sigterm and destroy methods onto an object instance, raises the virtual object_destroy method, removes the object from the registry, and nullifies object pointers and properties. Therefore, when the Registry Service sends a destroy or termination signal to an object, the decorated object cleans itself up.

API: Hemi.object

Destroy

The sendDestroyTo method only invokes an object's destroy method. In the previous example, the new oNewObject framework object was created with a deconstructor to handle the registry's destroy invocation. This object may then be destroyed and removed from the registry via one of two ways:

   /// Send destroy via Registry call:
   Hemi.registry.service.sendDestroyTo(oNewObject);
   /// oNewObject will remove itself from the registry because it was created with a deconstructor.
   /// Destroy directly:
   oNewObject.destroy();

API: Hemi.object

Signal Termination

The purpose of the sendSigterm method is to signal the framework that a complete tear-down is in effect. The method sends a notification, by invoking a virtual sigterm method, to all registered objects. After the notifications, all objects are removed from the registry.

When an object is created with a deconstructor, the decorated sigterm method invokes the decorated destroy method.

The Driver Service registers browser window event handlers and notifies the Registry Service as well as sending a destroy publication when the window unloads.

API: Hemi | API: Hemi.object | API: Hemi.driver

Evaluation Statements

The registry includes two utility methods for creating evaluation statements that return a registered object instance, and an apply statement for a named function on a registered object instance. This is useful when the JavaScript execution scope shifts between global and a closure or instance.

For example, given some object instance:

oSomeObject = {
   SomeFunction : function(){
   }
}
Hemi.prepareObject("custom", "1.0", true, oSomeObject);

The manner in which SomeFunction is exposed to other objects necessitates restoration of the execution scope prior to it being invoked. In situations where a string representation of the method is desired (such as an inline anonymous event handler), the function may not be executed in the instance scope as expected. The getApplyStatement method returns an evaluation statement to restore that scope.

var sFPointer = Hemi.registry.getApplyStatement(oSomeObject, "SomeFunction");
/// yields:
Hemi.registry.service.getObject("...").SomeFunction.apply(
   Hemi.registry.service.getObject("...")
);

API: Hemi | API: Hemi.event

Messaging

The message service provides a message subscription and publication model for framework objects, and an encoded notification engine. The service uses an optional notification delay to throttle intra-framework communication.

Notification

The Message Service can send general notifications via the sendMessage method. Notifications use an encoded numeric syntax to identify origin, threshold (like a log threshold), and context. Notifications are published to onsendmessage subscribers and default to the 200 block (refer Encoded Discriminator).

The API: Hemi.util.logger utility uses the Message Service notification and publication to instrument logging capabilities for a given object.

Encoded Discriminator

Notifications use an encoded discriminator in the format: block.level.context.condition. Typically, only the block or block.level values are needed.

  • block : The block is used to identify the general origin. The message service defines 200 as the default block. The block is restricted between 1 and 999. A block must be defined before it is used. Otherwise, the nearest defined block will be used. The easiest way to define a new block is to use the hemi.util.logger utility.
  • level: The level defines the importance of the notification, and must be one of the following numeric values: ** 0 (ALL) ** 1 (DEBUG) ** 2 (ADVISORY) ** 3 (NORMAL) ** 4 (WARNING) ** 5 (ERROR) ** 6 (FATAL) ** 7 (NONE)
  • context and condition are reserved for implementations to define where the notification originated. By default, these values are 0.

Whether or not a notification is published depends on the Message Service report threshold. By default, this is set to NORMAL, and can be changed with the setReportThreshold method.

Notification Example

The following example sends a general notification to the default block and level (200.3):

Hemi.message.service.sendMessage("Some message");

The following example sends a warning notification to the default block (200.4):

Hemi.message.service.sendMessage("Some message","200.4");

Subscriptions and Publications

The Message Service includes a publication and subscription model, which when coupled with the delivery delay feature can be very powerful for leveling application behavior. Any object or global function may be registered as a subscriber.

The following example demonstrates a global subscriber. When using sendMessage and onsendmessage, the second argument, oMessage, is a Message object.

function HandleMessage(sName, oMessage){
   alert(vData.message);
}
Hemi.message.service.subscribe(0, "onsendmessage", HandleMessage);
Hemi.message.service.sendMessage("Test message");

The following example demonstrates a registered framework object subscribing to a message, sending a message, and then unsubscribing. The second parameter is whatever was included in the publication. In this case, the string value some payload.

var oObject = Hemi.newObject("custom","1.0",true,false,{
   handle_some_publication : function(sName, vData){
   }
});
Hemi.message.service.subscribe(oObject, "some_publication", handle_some_publication);
// ...
Hemi.message.service.publish("some_publication","some payload");

// Cleanup
Hemi.message.service.unsubscribe(oObject, "some_publication", handle_some_publication);

API: Hemi

Restricted Subscription and Publication Scope

The scope of subscriptions and publications can be limited to a specific object. For example, an object may subscribe to a publication with the proviso that it only receive the publication made with a specific reference. This allows subscriptions and publications to be targeted as opposed to having to filter for specific object instances on a general publication broadcast.

API: Hemi

Logging Example

The API: Hemi.util.logger utility decorates an object with a logging facility, and registers a named notification block.

Refer to the Utilities documentation for an example.

Delayed Notification and Publication

The Message Service can throttle message publication and notification rates using the setDeliveryDelay method. Delayed notification is not supported for use in tandem with the convenience methods provided by the API: Hemi.app utilities as those methods are synchronous but rely on operations that become asynchronous with the delay enabled.

XML

Hemi uses a robust XML utility(formerly distributed as libXmlRequest), which supports synchronous and asynchronous get and post methods for XML, text, and JSON via a dynamically-allocated pool of request objects. The XML utility supports caching results, transforming with XSL, token evaluation, dynamic protocols, and moving node trees between XML and XHTML.

Configuration

The following configuration may be specified.

  • auto_content_type : Bit indicating that the content type will be set based on the type of data that is posted.
  • xml_content_type: When auto_content_type is set, the Content Type to set when posting XML data. Default value is text/xml.
  • form_content_type : When auto_content_type is set, the Content Type to set when posting text data via an XML request. Default value is application/x-www-form-urlencoded.
  • text_content_type: When auto_content_type is set, the Content Type to set when posting JSON data. Default value is text/plain.
  • ax_http_control : For Pre-IE7, the ActiveX control used for XMLHTTP Requests. Default value is MSXML2.XMLHTTP.
  • ax_dom_control : For Pre-IE8, or, for certain file-system instances, used for creating XML documents. Default value is ax_dom_control.
  • gadget_mode : (To be deprecated) For use when using Hemi within a Windows Sidebar Gadget.
  • gadget_xml_control : (To be deprecated) When in gadget mode, the ActiveX Control that handles XML Requests. Default value is Core.Gadget.GadgetXmlHttp.
  • gadget_base_path : (To be deprecated) When in gadget mode, the base path of the application context (which may not be the same as hemi_base).

XML Get

Synchronous request to an XML resource, and handling the response as XHTML.

// Get synchronous XML
var oXml = Hemi.xml.getXml("/path/to/data.xml");
// Assume it's valid, and copy the results into an HTML node.
Hemi.xml.setInnerXHTML(document.body, oXML.documentElement);

Asynchronous request to an XML resource.

// Get asynchronous XML and cache the response
function handle_xml_request(sEvent, vData){
   var oXml = vData.xdom;
}
Hemi.xml.getXml("/path/to/data.xml",handle_xml_request, true);

Cached Requests

The XML utility also includes a per-page caching mechanism for repeated requests to the same resource. The syntax is:

Hemi.xml.getXml(sPath, fHandler, bAsync, sId, bCached);

If sId is not specified, but bCache is set, the identifier is sPath. In the following example, the response is cached.

var oXml = Hemi.xml.getXml("/path/to/data.xml", 0, 0, "DataId", 1);

Subsequent requests for /path/to/data.xml with the id DataId will return the cached copy. If a different identifier is used, a new request is made.

XML Post

Given some XML structure:

Some Text

Which may be represented via the following script:

var oXml = Hemi.xml.newXmlDocument("Data");
var oNode = oXml.createElement("SomeData");
oNode.appendChild(oXml.createTextNode("Some Text"));
oXml.documentElement.appendChild(oNode);
/// add some data to the XML 

The document may be posted using the synchronous or asynchronous postXML methods.

/// Post synchronous XML
var oResponseXml = Hemi.xml.postXml("/path/to/application", oXml);
/// Handle the XML respose
... 

For asynchronous operations, a handler must be specified.

// Post asynchronous XML
function handle_xml_request(sEvent, vData){
   var oXml = vData.xdom;
}
Hemi.xml.postXml("/path/to/application", oXml, handle_xml_request);

Text and JSON

The XML utility includes get and post convenience methods for requesting text and JSON structures via the XML API. The method syntax, caching, and handling is identical to getXml and postXml.

For JSON requests, a JSON interpreter is expected. For older browsers that do not include a JSON interpreter, the separate JSON library must be included. The Hemi JavaScript Framework will not attempt to load this.

JSON Reviver

When using getJSON, the Hemi JavaScript Framework XML utility uses a JSON Reviver to restore date strings to date objects.

API: Hemi

Clone this wiki locally