.. _pages/core/environment#environment: Environment *********** Introduction ============ The environment of an application is a set of values that can be queried through a well-defined interface. Values are referenced through unique keys. You can think of this set as a global key:value store for the application. Values are write-once, read-many, but there are also read-only values. If a value for a certain key can be set, it can be set in various ways, e.g. by code, through build configuration, etc., usually during startup of the application, and not changed later. Other environment values are automatically discovered when they are queried at run time, such as the name of the current browser, or the number of allowed server connections. This way, the environment API also implements browser feature detection, and these values cannot be arbitrarily set. Environment settings are also used in the framework, among other things to add debug code in the form of additional tests and logging, to provide browser-specific implementations of certain methods, asf. Certain settable environment keys are pre-defined by qooxdoo, the values of which can be overridden by the application. Applications are also free to define their own environment keys and query their values at run time. So for the application developer, the environment represents a set of global values that mirrors the general parameters under which the application runs. It can be used to both *detect* (e.g. browser features) as well as *inject* such parameters (e.g. through build configuration). For global values that are *not* derived from the outside world in some way, just use e.g. a static application class. .. _pages/core/environment#motivation: Motivation ========== Environment settings address various needs around JavaScript applications: * Control initial settings of the framework, before the custom classes are loaded. * Pass values from outside to the application. * Trigger the creation of multiple build files. * Query features of the platform at run time (browser engine, HTML5 support, etc.) * Create builds optimized for a specific target environment, i.e. feature-based builds. As there are a number of pre-defined settings in the framework, you can make use of them right away by querying their values in your application code. The next section deals with that. Afterwards, you learn how to override default values or define your own environment settings. .. _pages/core/environment#querying: Querying Environment Settings ============================= In general, there are two different kinds of settings, **synchronous** and **asynchronous**. The asynchronous settings are especially for feature checks where the check itself is asynchronous, like checking for data: URL support. Both kinds have two query methods at the qx.core.Environment class, *.get()* and *select()* for synchronous, and *.getAsync()* and *.selectAsync()* for asynchronous settings. Synchronous ----------- Let's first take a look at the synchronous API and the two possibilities of accessing the data: :: qx.core.Environment.get("myapp.key"); The ``get`` method is likely the most important one. It returns the value for the given key, ``myapp.key`` in this example. :: qx.core.Environment.select("myapp.key", { "value1" : resvalue1, "value2" : resvalue2, "default" : catchAllValue } The ``select`` method is a way to select a value from a given map. This offers a convenient way to select an expression for a given key value. It also allows you to specify the special map key **"default"**, that will be used if the current value of the environment key does not match any of the other map keys. This is very handy when only one of the expected values needs a special case treatment. In the example above, the ``resvalue(s)`` could be a function or any other valid JavaScript expression. Asynchronous ------------ The asynchronous methods are a direct mapping of their synchronous counterparts. :: qx.core.Environment.getAsync("myapp.key", function(result) { // callback }, context); As the *.getAsync* does not return a result immediately, you have to specify a callback method which will be executed as soon as the value for the environment key is available. Your callback will be passed this value as the single argument, and the callback is responsible to integrate the result with your application. This principle carries over to the corresponding select call: :: qx.core.Environment.selectAsync("myapp.key", { "value" : function(result) { // callback value 1 }, "default" : function(result) { // catch all callback } }, context) In case of an asynchronous select the type of the values has to be a function, which will be called as soon as the key value is available. Again, you can provide a *"default"* case. As with the callbacks used for *.getAsync*, the callbacks used with *.selectAsync* will also get the key value as parameter, which might come handy especially in the *"default"* case. .. _pages/core/environment#caching: Caching ------- It sure happens in the live cycle of an application that some key get queried quite often, like the engine name. The environment system caches every value to ensure the best possible performance on expensive feature tests. But in some edge cases, it might happen that you want to redo the test. For such use cases you can invalidate the cache for a given key, to force a re-calculation on the next query: :: qx.core.Environment.invalidateCacheKey("myapp.key"} This example would clear a previously calculated value for ``myapp.key``. Removal of Code --------------- Usually, function calls like *qx.core.Environment.get()* are executed at run time and return the given value of the environment key. This is useful if such value is determined only at run time, or can change between runs. But if you want to pre-determine the value, you can set it in the generator config. The generator can then anticipate the outcome of a query and remove code that wouldn't be used at run time. For example, :: function foo(a, b) { if (qx.core.Environment.get("qx.debug") == true) { if ( (arguments.length != 2) || (typeof a != "string") ) { throw new Error("Bad arguments!"); } } return 3; } will be reduced in the case *qx.debug* is *false* to :: function foo(a, b) { return 3; } In the case of a *select* call, :: qx.core.Environment.select("myapp.key", { "value1" : resvalue1, "value2" : resvalue2 } will reduce if *myapp.key* has the value *value2* to just :: resvalue2 The :ref:`generator documentation ` has more details on optimization of *qx.core.Environment* calls. .. _pages/core/environment#pre_defined: Pre-defined Environment Keys ---------------------------- qooxdoo comes with a set of pre-defined environment settings. You can divide those into two big groups. One is a set of feature tests which cover browser features like support for certain CSS or HTML features. The second group are simple settings for the qooxdoo framework which drive the inner workings of the framework. For a complete list of predefined environment keys, take a look at the `API documentation of the qx.core.Environment class `__. .. _pages/core/environment#defining: Defining New Environment Settings ================================= Now to actually setting new or overriding existing environment settings. The value of an environment key can take one of two forms, as a concrete literal value, or as a function that returns a value at run time. The former can be achieve in various ways (see further), the latter only through application code. (An environment key with its current value is also referred to as an *environment setting*). We go through both ways now. .. _pages/core/environment#defining_as_value: As Literal Values ------------------ As already mentioned, there are various possibilities to assign a literal value, like ``"foo"``, ``3`` or ``true``, to an environment key. You can define the setting * in the class map * in method code * through inline `` .. _pages/core/environment#in_configuration: In the Generator Config ^^^^^^^^^^^^^^^^^^^^^^^ You can define a key and its value in the :ref:`environment ` key of the job with which you build the script files of your application (e.g. *source-script*, *build-script*): :: "myjob" : { [...] "environment" : { "myapp.key" : value } } Using the generator config adds a special meaning to the provided environment settings. Specifying a **list** of values for a key triggers the creation of multiple output files by the generator. E.g. replacing **value** with **[value1, value2]** in the above example, the generator will create two output files. See the :ref:`environment ` key for more information on multiple output generation. .. _pages/core/environment#in_url: Via URL parameter ^^^^^^^^^^^^^^^^^^^ Before using URL parameter to define environment settings, you have to specify another environment setting in the generator configuration which is named ``qx.allowUrlSettings``. If the application is generated with this config setting in place, you can then use URL parameter to add further key:value pairs. .. code-block:: html http://my.server.com/path/to/app/index.html?qxenv:myapp.key:value The pattern in the URL parameter is easy. It has three parts separated by colons. The first part is the constant ``qxenv``, the second part is the key of the environment setting and the last part is the value of the setting. So much for setting simple key:value pairs. Now for providing a check function as the value of an environment key. .. _pages/core/environment#defining_as_function: As a Check Function ------------------------------- As mentioned before, providing a function in place of a value can only be done in application code, so these environment settings are done at run time. The framework settings defined at run time are usually feature checks like checking for dedicated CSS or HTML features. The check function is executed and is responsible for returning a proper value when the environment key is queried later. These checks can be synchronous or asynchronous, and this corresponds to how they are queried. Synchronous checks are queried with the *.get()* and *.select()* methods, asynchronous checks with *.getAsync()* and *.selectAsync()* (see :ref:`Querying Environment Settings `). Synchronous ^^^^^^^^^^^ To add a synchronous check function, the standard *.add()* call is used: :: qx.core.Environment.add("group.feature", function() { return !!window.feature; }); This example shows the same API used for adding a key:value setting. The only difference is that you add a function as second parameter and not a simple value. This function is responsible for determining and returning the value for the given environment key. In this example, if ``window.feature`` is defined, the check will return ``true``. Asynchronous ^^^^^^^^^^^^ To add an asynchronous check, use *.addAsync()*: :: qx.core.Environment.addAsync("group.feature", function(callback) { window.setTimeout(function() { callback.call(null, true); }, 1000); }); This example shows how to add a asynchronous feature check. A timeout is used to get the asynchronous behavior in this simple example. That can be more complicated of course but the timeout is good enough to showcase the API. As you can see in the check function we are adding, it has one parameter called ``callback`` which is the callback passed by *.getAsync()* or *.selectAsync()* asynchronous queries. As before, the check function is responsible for computing the value of the environment key. But rather than just returning the value (as in the synchronous case), it calls the callback function and passes the value. The callback function is then responsible to integrate the result value with the querying layer. In this simple example, the check waits a second and calls the callback with the result ``true``.