ContextAwareness API

The ContextAwareness API of Infusion provides a powerful suite of grades and utilities that allow a component to be responsive to aspects of its context in a flexible and open-ended way. A component can name particular kinds of adaptations (dimensions) that it is capable of, and for each of these adaptations, specify a list of environmental checks to be made in a particular order, which will result in particular adaptations being chosen. These adaptations are expressed through the addition of extra grade names to the component during its construction.

When and how to apply fluid.contextAware

For lightweight cases of context adaptation, the distributeOptions scheme suffices, in that it enables a component to be responsive to broadcasts "advising" it from around the component tree in a free-form and controllable away. However, when a component needs to take a clearer role in responding to potentially many kinds of requests for adaptation, where these can be organised around two or more "axes" or "dimensions" of adaptation that can be clearly named and identified, it should derive from the fluid.contextAware grade and advertise some of its dimensions of adaptations in its own options. These dimensions themselves can always be extended by further contributions to the component's options from all the usual sources - direct options, subcomponent options, and options distributions from elsewhere in the tree.

Any component derived from fluid.contextAware will advertise an area of its options named contextAwareness which organises the rules for its adaptation in a hierarchical way - at the top level by "adaptation" and then at the nested level by "checks".

Structure of this API

This page describes how various features of the framework and the ContextAwareness API cooperate together. These consist of:

  • The fluid.contextAware grade and the contextAwareness area of options that it responds to to produce adaptations
  • The fluid.contextAware.makeChecks API for converting aspects of the actual context or environment (e.g. capabilities of the browser, user's requirements or purpose of the application) into context names which contextAwareness can respond to
  • The fluid.contextAware.forgetChecks API for eliminating checks created by fluid.contextAware.makeChecks
  • The fluid.contextAware.makeAdaptation API which can be used by 3rd parties to broadcast contextAwareness records into implementation components that they which to make (more) adaptable

Simple example - speech API-aware component

This simple example invokes the fluid.textToSpeech.isSupported feature detector, which returns true if the current browser supports the HTML5 Speech API. We use the fluid.contextAware.makeChecks function to assign the result of this feature detection to a context named fluid.supportsTTS. We can then use this context to conditionally switch in an extra grade name, examples.myComponent.speechAware into the examples.myComponent component:

fluid.contextAware.makeChecks({
    "fluid.supportsTTS": "fluid.textToSpeech.isSupported"
});

fluid.defaults("examples.myComponent", {
    gradeNames: ["fluid.component", "fluid.contextAware"],
    contextAwareness: {
        speechAware: {
            checks: {
                speechAware: {
                    contextValue: "{fluid.supportsTTS}",
                    gradeNames: "examples.myComponent.speechAware"
                }
            },
            defaultGradeNames: "examples.myComponent.nonSpeechAware"
        }
    }
});

The options within contextAwareness could be contributed by any integrator, not necessarily the component's original author, and have a layout that makes it easy to target and override parts of the existing structure with updated options.

adaptationRecord members in a contextAwareness record

Each component implementing fluid.contextAware accepts a top-level record in its options at the path named contextAwareness.

The top level structure of the contextAwareness record consists of a free hash of adaptationName strings to adaptationRecord structures:

{
    <adaptationName> : <adaptationRecord>,
    <adaptationName> : <adaptationRecord>,
    ...
}

The adaptationName strings are considered as namespaces for the purposes of priority resolution (see the priority entry in the following table). The elements of the adaptationRecord are described in the following table:

Members of an adaptationRecord entry within the contextAwareness block of a fluid.contextAware component< /th>
Member Type Description
checks (optional) Hash of checkNamespace to checkRecord A free hash of namespace names for checks, to entries describing how a context check is to be made, and what gradeNames should result if the check is successful. (See Structure of members in a checkRecord below for details.)
defaultGradeNames (optional) String or Array of String One or more gradeNames to be the result if none of the checks entries matches
priority (optional) Priority value - see Priorities for a full explanation The priority (if any) that the gradeNames resulting from this dimension should have over those resulting from any other dimension. This should be an entry of the form before:adaptationName or after:adaptationName for one of the other dimensions attached to this component within contextAwareness

The result of the contextAwareness record is that a number of the elements within checks will be evaluated in the visible context, and result in a number of gradeNames which will then be contributed into the gradeNames of the instantiating component in a particular order. This order is governed by both the priority entry at the adaptation level as well as at the check level.

checkRecord members in an adaptationRecord

The checkRecord structure which is used in the first row of the table above is described now:

Members of a checkRecord entry within a adaptationRecord block of a fluid.contextAware component
Member Type Description
contextValue String IoC reference A standard IoC reference, either consisting of a bare context reference such as {contextName} or to a precise value such as {contextName}.further.path which should be sought by this check. If a bare context name is supplied, the checked path will default to options.value. If the context name does not match, the expression will evaluate to undefined
equals (optional) String, Number or Boolean A value with which the context value fetched from contextValue should be checked. If omitted, this defaults to true. The check will pass if the context value referenced in contextValue can be found, and matches any value supplied in equals (or the default of true).
gradeNames (optional) String or Array of String One or more gradeNames that will be returned out to the contextAwareness system if this check passes.
priority (optional) Priority value - see Priorities for a full explanation The priority (if any) that the gradeNames resulting from this dimension should have over those resulting from any other dimension. This should be an entry of the form before:adaptationName or after:adaptationName for one of the other dimensions attached to this component within contextAwareness

Example contextAwareness record

The most adaptable component in the framework is currently the Uploader which currently can respond to three "dimensions" of adaptation. Two of these, technology and liveness, are advertised in its own contextAwareness record:

fluid.defaults("fluid.uploader", {
    gradeNames: ["fluid.viewComponent", "fluid.contextAware"],
    contextAwareness: {
        technology: {
            defaultGradeNames: "fluid.uploader.singleFile"
        },
        liveness: {
            priority: "before:technology",
            checks: {
                localDemoOption: {
                    contextValue: "{uploader}.options.demo",
                    gradeNames: "fluid.uploader.demo"
                }
            },
            defaultGradeNames: "fluid.uploader.live"
        }
    }
});

technology refers to the implementation technology of the uploader. Although all technologies other than a modern HTML5 engine have been removed from the current framework image, the basic architecture to support other engines still exists and could be contributed to in future. The liveness adaptation relates to the mocking infrastructure for the Uploader which exists at two levels. Firstly, there is the "demo uploader" which mocks all of the engine-side implementation, and secondly the uploader can be run in various styles of integration tests which only mock the transport level which actually performs the file upload.

Example of dynamically broadcasting a fresh adaptation

Finally, a third dimension of adaptation is supported by the Uploader's capability to be configured in a way that it will respond to previous instances of its own API - in particular that delivered for Infusion 1.2, released in April 2010, and Infusion 1.3, released in November 2010. This is implemented by allowing a dynamic contribution of a fresh dimension to the uploader's contextAwareness record from separate implementation files:

fluid.defaults("fluid.uploader.compatibility.distributor.1_3", {
    distributeOptions: {
        record: {
            "1_2": {
                contextValue: "{fluid.uploader.requiredApi}.options.value",
                equals: "fluid_1_2",
                gradeNames: "fluid.uploader.compatibility.1_2"
            }
        },
        target: "{/ fluid.uploader}.options.contextAwareness.apiCompatibility.checks"
    }
});

// Actually construct a component instance performing the options broadcast into all uploaders
fluid.constructSingle([], {
    singleRootType: "fluid.uploader.compatibility.distributor",
    type: "fluid.uploader.compatibility.distributor.1_3"
});

// The grade that contextAwareness ends up contributing into the uploader, if the rule is activated
fluid.defaults("fluid.uploader.compatibility.1_2", {
    transformOptions: {
        transformer: "fluid.model.transformWithRules",
        config: fluid.compat.fluid_1_2.uploader.optionsRules
    }
});

The defaults block fluid.uploader.compatibility.distributor.1_3 contains an options distribution which causes a third dimension to be allocated in the uploader's contextAwareness, named apiCompatibility — this can be done simply by arranging to broadcast the appropriate options into it. Once we have defined this options distribution, we actually need to construct a component instance which holds and operates them — this is done via the fluid.constructSingle line. This utility automatically arranges for a singleton instance, uniquified at the component tree's top level by the type singleRootType which has a very similar function to the option of the same name consumed by the fluid.resolveRootSingle grade described in the documentation on Contexts.

Having shown the basic operation of the receiver of contextual information, we'll now describe the group of utilities, including fluid.constructSingle that we just met, which can be used by integrators and implementors to coordinate the visibility of context names and distributions from them.

Note that the combined effect of the first two defaults blocks shown in this example can be achieved "all-in-one" by a single call to the dedicated utility fluid.contextAware.makeAdaptation.

Making contexts visible and removing them with fluid.contextAware.makeChecks and fluid.contextAware.forgetChecks

The checkRecord structures described in the table above, by default reference context paths which hold values at an option named value (by default holding the boolean true) which are compared against a value held equals (also defaulting to true). The ContextAwareness API includes two helper functions to assist integrators to construct components matching contexts of this form, and removing them when they are no longer required.

These contexts can be issued with a call of the form:

fluid.contextAware.makeChecks(checkStructure);

The checkStructure argument holds a hash of contextName strings to checkEntry records, of the form

{
<contextName> : <checkEntry>,
<contextName> : <checkEntry>,
...
}

which is described in the following table:

Members of a checkEntry record supplied as an argument to fluid.contextAware.makeChecks
Member Type Description
func/funcName String IoC reference or global function name A function name or IoC reference to a function to be evaluated to produce the context value
value String, Number or Boolean The value to be supplied at the value path in the options tructure of the constructed context

You must supply exactly one of func, funcName or value.

Example of fluid.contextAware.makeChecks

fluid.contextAware.makeChecks({
    "fluid.browser.supportsBinaryXHR": {
        funcName: "fluid.enhance.supportsBinaryXHR"
    },
    "fluid.browser.supportsFormData": {
        funcName: "fluid.enhance.supportsFormData"
    }
});

In the above example, the two global functions fluid.enhance.supportsBinaryXHR and fluid.enhance.supportsFormData will be executed, and their return values added into contexts with the names fluid.browser.supportsBinaryXHR and fluid.browser.supportsFormData.

The contexts registered by fluid.contextAware.makeChecks can be erased from the system by the use of the call

fluid.contextAware.forgetChecks(<contextNames>);

Here, contextNames is can hold either a String or Array of String holding the keys from the structures previously supplied to fluid.contextAware.makeChecks

Example of fluid.contextAware.forgetChecks

For example, the checks registered in the above example fluid.contextAware.makeChecks call could be erased by a call to

fluid.contextAware.forgetChecks(["fluid.browser.supportsBinaryXHR",
    "fluid.browser.supportsFormData"]);

Defining and broadcasting a fresh adaptation in one operation with fluid.contextAware.makeAdaptation

A very common use case is to define an adaptation (that is, a distributeOptions block which targets the contextAwareness area of a collection of components in the tree), and then to create an instance of a single, well-known component which actually broadcasts the adaptation. This was what we did in two steps (fluid.defaults plus fluid.constructSingle) in the above example contextAwareness broadcast

  • this can be done in a single step using the fluid.contextAware.makeAdaptation API.
fluid.contextAware.makeAdaptation(<adaptationRecord>);

This accepts a single structure adaptationRecord with a number of required fields:

Members of a adaptationRecord record supplied as an argument to fluid.contextAware.makeAdaptation
Member Type Description
distributionName String A grade name — the name to be given to the fabricated grade which performs the broadcast
targetName String A grade name — the name of the grade to receive the adaptation
adaptationName String The name of the contextAwareness record (the top-level adaptationRecord) to receive the broadcast record — this will be a simple string
checkName String The name of the checkRecord record (within the adaptationRecord) to receive the broadcast record — this will be a simple string
record Object (checkRecord) The checkRecord which is to be broadcast — containing fields contextValue, gradeNames etc. as described above

Example of calling fluid.contextAware.makeAdaptation

For example, the pair of calls in the above example contextAwareness broadcast could be achieved by the following single call to fluid.contextAware.makeAdaptation:

fluid.contextAware.makeAdaptation({
    distributionName: "fluid.uploader.compatibility.distributor.1_3",
    targetName: "fluid.uploader",
    adaptationName: "apiCompatibility",
    checkName: "1_2",
    record: {
        contextValue: "{fluid.uploader.requiredApi}.options.value",
        equals: "fluid_1_2",
        gradeNames: "fluid.uploader.compatibility.1_2"
    }
});