mvcct-enhancer/advanced topics

Here you find information to configure yourself the mvcct-enhancer module that is usually configured automatically during Mvc Controls Toolkit installation.

Moreover, you find also information on how to write modules to be orchestrated by mvcct-enhancer, and how to write your own custom Html5 input fallback module,

Since this is an advanced topics section, before proceeding, please, read mvcct-enhancer basics.

Html Enhancer with pure synchronous javascript loading

<script type="text/javascript" src="mvcct.enhancer.js" />
<script type="text/javascript" src="lib1.js" />
<script type="text/javascript" src="lib2.js" />
 
 
<!--in actual applications the code below should be enclosed into another .js
file at the end of the body
-->
<script type="text/javascript">
        mvcct.enhancer.register(lib1Transform,
            true,
            lib1ProcessOptions,
            "lb1DebugName",
            lib1OptionalPreProcessOptions);
        mvcct.enhancer.register(lib2Transform,
            true,
            lib2ProcessOptions,
            "lb1DebugName",
            lib2OptionalPreProcessOptions);
        var myOptions{
            //MyOptions definition
        };
        mvcct.enhancer.init(myOptions);
</script>

Then each time you create new dynamic html nodes just call mvcct.enhancer.transform(node) on the root node(s).

Transformations are applied in the order they are registred, so we may decide once and for all their order in the .js file that defines all registrations and options.

After the .init method is called a three stages processing occurs:

  1. Pre-processing stage. If available the PreProcessOption function of each module is called. In this stage each transformation module has the opportunity to offer services to other modules by setting the property of the option object used by that nodules.
  2. Processing options stage. Each module configures itself based on the sections of options object destined to it.
  3. Transformation stage. All transformations are applied to the initial content of the page.

lib1Transform, lib2Transform define the actual transformations to be applied. They receive two arguments: the root node of the chunk to transform and a boolean that is true for the initial static page transformation and false for dynamic Html transformations.

When the second argument is set to true the transformation is applied to both the initial static Html, and to dynamic content. When it is set to falsethe transformation is applied just to dynamic content. This option maybe usefull for modules that automatically apply their transformation on the document ready event. IMPORTANT! This technique interferes with the main purpose of centralizing the place where to define the order of application of all transformations. Therefore, as a default, the jQuery document ready event is intercepted and "blocked". This block may be removed by setting the runReady property of the options object to true. In any case all functions registred on the document raedy event will be executed only after the option processing stage, during the transformation stage.

lib1ProcessOptions, lib1ProcessOptions are the functions that define the option processing of each module. They receive the options object as their unique argument.

lb1DebugName, lb2DebugName, are names used in error messages when a javascript error occurs during a transformation.

lib1PreProcessOptions, lib2PreProcessOptions, are the functions that define the pre-processing of each module. They receive the options object as their unique argument.

Html Enhancer with mixed synchronous and asynchronous javascript loading

<script type="text/javascript" src="mvcct.enhancer.js" />
<script type="text/javascript" src="lib1.js" />
<script type="text/javascript" src="lib2.js" />
 
<script data-main="scripts/main" src="scripts/require.js"></script>
 
<!--in actual applications the code below should be enclosed into another .js -->
<script type="text/javascript">
        mvcct.enhancer.register(lib1Transform,
            true,
            lib1ProcessOptions,
            "lb1DebugName",
            lib1OptionalPreProcessOptions);
        mvcct.enhancer.register(asynclib1Transform,
            true,
            asynclib1ProcessOptions,
            "asynclb1DebugName",
            asynclib1OptionalPreProcessOptions);
        mvcct.enhancer.register(asynclib2Transform,
            true,
            asynclib2ProcessOptions,
            "asynclb1DebugName",
            asynclib2OptionalPreProcessOptions);
        mvcct.enhancer.register(lib2Transform,
            true,
            lib2ProcessOptions,
            "lb1DebugName",
            lib2OptionalPreProcessOptions);
        var myOptions{
            //MyOptions definition
        };
        mvcct.enhancer.waitAsync(myOptions);
</script>

waitAsync delays initialization till async loading has been completed. Async modules are registred together with sync modules, and also in this case transformations are applied in the order they are registred. All functions used in the registration of async modules are on-line wrappers around the actual functions that are being loaded asynchronously. For eaxample asynclib1Transform will be something like:


function(options, init){
    allAsyncs.asynclib1.transform(options, init);
}
        

In the main.js we have something like:


define(["..../asynclib1", ...],
    function(asynclib1, ...) {
        var allAsyncs=window.allAsyncs={};
        allAsyncs.asynclib1=asynclib1;
        ....
        mvcct.enhancer.asyncReady();
        ...
    }
);
        

There, we first define the actual functions invoked in the registration, and then we inform the enhancer module async loading has been completed by calling asyncReady.

Html Enhancer with mixed synchronous loading and Globalize library

This is a particular case of mixed sinchronous/asynchronous loading since Globalization jSon data are usually loaded asynchronously. In this case the async loading part is:

@using System.Globalization
<script src="~/lib/globalize/globalize.min.js"></script>
<script type="text/javascript">
    @{var culture = CultureInfo.CurrentCulture.Name; }
    (function () {
 
        $.when(
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/supplemental/likelysubtags.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/supplemental/numberingSystems.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/supplemental/timeData.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/supplemental/weekData.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/supplemental/currencyData.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/currencies.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/numbers.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/ca-gregorian.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/timeZoneNames.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/dateFields.json")"),
          $.getJSON("@Url.Content("~/lib/globalize/cldr-data/main/" + culture + "/layout.json")")
 
        ).then(function () {
            // Normalize $.get results, we only need the JSON, not the request statuses.
            return [].slice.apply(arguments, [0]).map(function (result) {
                return result[0];
            });
        })
         .then(Globalize.load).then(function () {
             Globalize.locale("@culture");
             mvcct.enhancer.asyncReady();
 
         });
 
    })(jQuery);
</script>

Html Enhancer with asynchronous javascript loading

define(["..../asynclib1""..../asynclib2", ..., ".../mvcct.enhancer, ..."],
    function(asynclib1, asynclib2, ..., enhancer, ... ...) {
        enhancer.register(asynclib1.Transform,
            true,
            asynclib1.ProcessOptions,
            "asynclb1DebugName",
            asynclib1.PreProcessOptions);
        enhancer.register(asynclib2.Transform,
            true,
            asynclib2.ProcessOptions,
            "asynclb1DebugName",
            asynclib2.PreProcessOptions);
        ....
        var myOptions{
            //MyOptions definition
        };
        document.addEventListener('DOMContentLoaded'function(){
            enhancer.init(myOptions);
        }, false);
        ...
    }
);

The avove code may be inserted either directly in the main.js or in a specfific transformations registration module. Important:: our examples used just AMD style async loading, but mvcct.enhancer supports also CommonJs/Node.Js loading.

Defining a custom Html5 fallback module

The bootstrap-html5-fallback acts by simply adding js widgets after not supported Html5 inputs have been falled back by the mvcct.enhancer.input.basic.js module (see mvcct-enhancer basics for more infos on mvcct.enhancer.input.basic.js). You may implement a similar mvcct-enhancer module by filling the browserSupport.handlers.enhance property of the option object with an object like the one below, during your module pre-processing options stage:


         {
            number: function(node){.... return;},
            range: function(node){.... return;},
            date: function(node){.... return;},
            month: function(node){.... return;},
            week: function(node){.... return;},
            time: function(node){.... return;},
            datetime: function(node){.... return;},
            email: function(node){.... return;},
            search: function(node){.... return;},
            tel: function(node){.... return;},
            url: function(node){.... return;},
            color: function(node){.... return;},
        }
        

Each of the above functions will be automatically invoked on the corresponding input type after fallback, and can be used to enhance the node with a widget. For instance, we may add a datepicker:


...
date: function(node){
    $(node).datepicker(options.browserSupport.dateOptions);
},
...
        

It is good practice to take widgets options from the overall mvcct.enhancer options object. See the bootstrap-html5-fallback documentation for an example of how to place widgets options in the overallmvcct.enhancer options object.

Other sub-properties of the browserSupport.handlers property allow more advanced customizations of the fallback process, Namely:

  • browserSupport.handlers.replace=function(type,support).... It receives as input the original input type and the same object returned by getSupport().Html5InputSupport and returns the input type to fallback to. If the function returns the same type received as input no further processing is done (fallback is aborted). The default function always returns "text", but in the case the input type is "range" and "number" is supported where it returns "number", so that the "range" is substituted with a "number".
  • browserSupport.handlers.translateVal=function(value, type, node).... It is invoked after a fallback in order to translate the original content of the input into a value appropriate to the way the fallback works. It receives as input, the value to transform (a string), the original input type, and the new input type, and returns the new value(a string). The default implementation furnished by the mvcct.enhancer.input.basic module uses the Globalize library to transform all date/time/numbers related input type values from the international ISO format into the current locale format, while leaving unchanged all other input types. This transformation is performed only if the new input type is "text".
  • browserSupport.handlers.fullReplace=function(input).... It allows a complete customization. It takes the input node as input and transform the html around it in a custom way. No other automatic action is performed when this function is provided.


Fork me on GitHub