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 thecontextAwareness
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 whichcontextAwareness
can respond to - The
fluid.contextAware.forgetChecks
API for eliminating checks created byfluid.contextAware.makeChecks
- The
fluid.contextAware.makeAdaptation
API which can be used by 3rd parties to broadcastcontextAwareness
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"
}
});