{"id":24464243,"url":"https://github.com/mvccontrolstoolkit/mvcct-enhancer","last_synced_at":"2025-10-04T10:45:27.286Z","repository":{"id":57305803,"uuid":"57302187","full_name":"MvcControlsToolkit/mvcct-enhancer","owner":"MvcControlsToolkit","description":"A javascript package to handle standard html enhancements and polyfill ","archived":false,"fork":false,"pushed_at":"2017-05-25T13:00:34.000Z","size":42,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-13T07:12:05.868Z","etag":null,"topics":["html-enhancement","html5-fallback","html5-inputs","html5-inputs-fallback"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MvcControlsToolkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-28T13:00:23.000Z","updated_at":"2019-04-30T16:05:18.000Z","dependencies_parsed_at":"2022-08-31T00:00:53.348Z","dependency_job_id":null,"html_url":"https://github.com/MvcControlsToolkit/mvcct-enhancer","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MvcControlsToolkit%2Fmvcct-enhancer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MvcControlsToolkit%2Fmvcct-enhancer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MvcControlsToolkit%2Fmvcct-enhancer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MvcControlsToolkit%2Fmvcct-enhancer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MvcControlsToolkit","download_url":"https://codeload.github.com/MvcControlsToolkit/mvcct-enhancer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675423,"owners_count":21143768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["html-enhancement","html5-fallback","html5-inputs","html5-inputs-fallback"],"created_at":"2025-01-21T05:14:54.311Z","updated_at":"2025-10-04T10:45:22.256Z","avatar_url":"https://github.com/MvcControlsToolkit.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# What is Html Enhancer?\nHtml Enhancer was conceived after years of frustrations spent trying to \nenrich my Html with widgets and plugins written by various authors and based on different technologies. \nI am speaking of:\n- problems caused by the wrong order plugins are applied to their target Html\n- panic when the same plugins must be applied also to the Html created dynamically or received by ajax calls. \nIn fact, putting everything in the jQuery.ready(...) is easy...but then we have to re-implement a similar code each time we\ncreate dynamic content! Several jQuery widgets automatically enhance our Html at the price of almost no programming effort, \nthanks to data- attributes and to a document parsing performed on the document ready event, but then all these tranformations are not automatically applied also to dynamic content!\n\nIt would be nice to decide once and for all the kind of transformations to apply to our html, and their order, \nand then having them applied automatically to all our pages, and dynamic content.\n\nIt would be nice to coordinate easily modules loaded asynchronously and modules loaded sinchronously,\nso that they apply in the right order!\n\nSo after years of frustrastions I decided to solve the problem once and for all, by creating an unique \ncentralized registry of all Html transformations/widgets that receives an unique options object that feeds all \nregistred modules.\n\nmvcct-enhancer has no dependency on other js libraries and comes with just a single transformation module that applies a minimal fallback for Html5 inputs that either are \nnot supported by the browser or that the developer would like to substitute with widgets. Adding other transformations is as easy as:\n1. writing simple wrappers around already existing modules\n2. registering the wrapped module by calling the `.register(...)` method.\n\n**mvcct-enhancer, is available both on bower, and npm.**\n\n## Html Enhancer with pure synchronous javascript loading\n```\n\n    \u003cscript type=\"text/javascript\" src=\"mvcct.enhancer.js\" /\u003e\n    \u003cscript type=\"text/javascript\" src=\"lib1.js\" /\u003e\n    \u003cscript type=\"text/javascript\" src=\"lib2.js\" /\u003e\n    \n    \n    \u003c!--in actual applications the code below should be enclosed into another .js \n    file at the end of the body\n    --\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n        mvcct.enhancer.register(lib1Transform, \n            true, \n            lib1ProcessOptions, \n            \"lb1DebugName\", \n            lib1OptionalPreProcessOptions);\n        mvcct.enhancer.register(lib2Transform, \n            true, \n            lib2ProcessOptions, \n            \"lb1DebugName\", \n            lib2OptionalPreProcessOptions);\n        var myOptions{\n            //MyOptions definition\n        };\n        mvcct.enhancer.init(myOptions);\n    \u003c/script\u003e   \n    \n   ```\nThen each time you create new dynamic html nodes just call \n`mvcct.enhancer.transform(node)` on the root node(s).\n\nTransformations are applied in the order they are registred, so we may decide once and \nfor all their order in the .js file that defines all registrations and options. \n\nAfter the .init method is called a three stages processing occurs:\n1. **Pre-processing stage**. If available the PreProcessOption function of each module\nis called. In this stage each transformation module has the opportunity to offer services to other \nmodules by setting the property of the option object used by that nodules.\n2. **Processing options stage**. Each module configures itself based on the sections of\noptions object destined to it.\n3. **Transformation stage**. All transformations are applied to the initial content of the page. \n\n`lib1Transform`, `lib2Transform` define the actual transformations \nto be applied. They receive two arguments: the root node of the chunk to transform and \na boolean that is `true` for the initial static page transformation and `false`\nfor dynamic Html transformations.\n\nWhen the second argument is set to `true` the transformation is applied to both the initial static Html, \nand to dynamic content. When it is set to `false`the transformation is applied just to\ndynamic content. \nThis option maybe usefull for modules that automatically apply their transformation\non the document ready event. **IMPORTANT!** This technique interferes with the main purpose \nof centralizing the place where to define the order of application of all transformations.\n**Therefore**, as a default, the jQuery document ready event is intercepted and \"blocked\".\nThis block may be removed by setting the `runReady` property of the options object to `true`.   \nIn any case all functions registred on the document raedy event will be executed only after the \noption processing stage, during the transformation stage.   \n\n`lib1ProcessOptions`, `lib1ProcessOptions` are the functions that define the option processing\nof each module. They receive the options object as their unique argument.\n\n`lb1DebugName`, `lb2DebugName`, are names used in error messages when a javascript error occurs \nduring a transformation.  \n\n`lib1PreProcessOptions`, `lib2PreProcessOptions`, are the functions that define the pre-processing\nof each module. They receive the options object as their unique argument.\n\n## Html Enhancer with mixed synchronous and asynchronous javascript loading\n\nWhen we have a foundamentally synchronous loading with just a few async loaded modules\nwe may adopt the following technique:\n\n```\n\u003cscript type=\"text/javascript\" src=\"mvcct.enhancer.js\" /\u003e\n    \u003cscript type=\"text/javascript\" src=\"lib1.js\" /\u003e\n    \u003cscript type=\"text/javascript\" src=\"lib2.js\" /\u003e\n    \n    \u003cscript data-main=\"scripts/main\" src=\"scripts/require.js\"\u003e\u003c/script\u003e\n    \n    \u003c!--in actual applications the code below should be enclosed into another .js --\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n        mvcct.enhancer.register(lib1Transform, \n            true, \n            lib1ProcessOptions, \n            \"lb1DebugName\", \n            lib1OptionalPreProcessOptions);\n        mvcct.enhancer.register(asynclib1Transform, \n            true, \n            asynclib1ProcessOptions, \n            \"asynclb1DebugName\", \n            asynclib1OptionalPreProcessOptions);\n        mvcct.enhancer.register(asynclib2Transform, \n            true, \n            asynclib2ProcessOptions, \n            \"asynclb1DebugName\", \n            asynclib2OptionalPreProcessOptions);\n        mvcct.enhancer.register(lib2Transform, \n            true, \n            lib2ProcessOptions, \n            \"lb1DebugName\", \n            lib2OptionalPreProcessOptions);\n        var myOptions{\n            //MyOptions definition\n        };\n        mvcct.enhancer.waitAsync(myOptions);\n    \u003c/script\u003e\n```\n`waitAsync` delays initialization till async loading has been completed.\n\nAsync modules are registred together with sync modules, and also in this case \ntransformations are applied in the order they are registred. All functions used in\nthe registration of async modules are on-line wrappers around the actual functions \nthat are being loaded asynchronously. For eaxample `asynclib1Transform` will be \nsomething like: \n\n```\nfunction(options, init){\n    allAsyncs.asynclib1.transform(options, init);\n}\n```\nIn the main.js we have something like:\n\n```\ndefine([\"..../asynclib1\", ...],\n    function(asynclib1, ...) {\n        var allAsyncs=window.allAsyncs={};\n        allAsyncs.asynclib1=asynclib1;\n        ....\n        mvcct.enhancer.asyncReady();\n        ...\n    }\n);\n```\nThere, we first define the actual functions invoked in the registration, and then\nwe inform the enhancer module async loading has been completed by calling `asyncReady`.\n\n## Html Enhancer with asynchronous javascript loading\n\n```\ndefine([\"..../asynclib1\", \"..../asynclib2\", ..., \".../mvcct.enhancer, ...\"],\n    function(asynclib1, asynclib2, ..., enhancer, ... ...) {\n        enhancer.register(asynclib1.Transform, \n            true, \n            asynclib1.ProcessOptions, \n            \"asynclb1DebugName\", \n            asynclib1.PreProcessOptions);\n        enhancer.register(asynclib2.Transform, \n            true, \n            asynclib2.ProcessOptions, \n            \"asynclb1DebugName\", \n            asynclib2.PreProcessOptions);\n        ....\n        var myOptions{\n            //MyOptions definition\n        };\n        document.addEventListener('DOMContentLoaded', function(){ \n            enhancer.init(myOptions);\n        }, false);        \n        ...\n    }\n);\n```\nThe avove code may be inserted either directly in the main.js or in a specfific \ntransformations registration module.\n\n**IMPORTANT!** Our examples used just AMD style async loading, but mvcct.enhancer supports\nalso CommonJs/Node.Js loading.\n\n## Selective enhancement\n\nVersion 1.1.0 introduced selective enhancements to allow several client side technologies to coexist in the same page and use mvcct-enhancer. The `register` method has a new optional `type` argument:\n\n```\nregister(\n            transform: (node: HTMLElement, isInitialize?: boolean) =\u003e void,\n            initialize: boolean,\n            processOptions: (options: Options) =\u003e void,\n            name: string,\n            preProcessOptions: (options: Options) =\u003e void,\n            type?: string): void;\n```\nWhen provided the `type` argument assigns a type to the module, so that one may enhance just modules of specific types. At moment built-in modules have either the 'html5-input' (for html5 inputs fallback) or the 'unobtrusive-validation' (for unobtrusive validation) types.\n\nThe `transform` function has an optional `types` argument:\n\n```\ntransform(node: HTMLElement, types?: any): void;\n```\n\nWhen provided it is an object whose properties (that must be set to `true`) are the module types to be transformed. Thus, for instance, if we would like to apply just htm5 fallback we must set the `types` argument to:\n\n```\n{\n    \"html5-input\": true\n}\n```\n\nIf no `types` argument is passed all registered modules are applied.\n\nif no type is specified when registering a module that module is always applied.\n\n## TypeScript support\nThe distribution contains the .d.ts header file needed to use mvcct enhamcer with TypeScript.  \n\n## Htm5 inputs support-detection and fallback module\nWhen the enhancer is loaded it automatically computes the browser support of all Html5 input fields. \nThis info may be obtained with the mvcct.enhancer call: `getSupport().Html5InputOriginalSupport`. \nThe `Html5InputOriginalSupport` property contains the object:\n\n```\n        {   \n            number: true/false;\n            range: true/false;\n            date: true/false;\n            month: true/false;\n            week: true/false;\n            time: true/false;\n            datetime: true/false;\n            email: true/false;\n            search: true/false;\n            tel: true/false;\n            url: true/false;\n            color: true/false;\n        }\n```\nWhere `true` means the input type is supported.\nAs a default no fallback attempt is performed. However, if the `mvcct.enhancer.input.basic.js` basic \nfallback module is loaded and registered by calling the mvcct.enhancer method `addBasicInput(Globalize)`, \nthen Html5 fallback is automatically turned on. and all Html5 inputs that are not supported are transformed \ninto text inputs and their content is converted into the current \"locale\" (this means, for instance, that dates are transformed \nfrom the date input ISO format into a format like mm/dd/yy). Moreover, if version \u003e= 1.0.2 the original input type\nis stored in the `data-original-type` attribute.\n\nAvailable, also a more complete fallback based on bootstrap widgets: [bootstrap-html5-fallback](https://github.com/MvcControlsToolkit/bootstrap-html5-fallback). \nWhen `bootstrap.html5.fallback.js` is loaded after `mvcct.enhancer.input.basic.js` the `addBasicInput(Globalize)`\nis updated to load also all bootstrap widgets.\n\nAs a default the following [Globalize](https://github.com/jquery/globalize) \"locale\" formats are used: \n\n```\n{\n     dateFormat: { \"date\": \"short\" },\n     timeFormat: { \"skeleton\": \"Hms\" },\n     timeFormat1: { \"skeleton\": \"Hms\" },\n     datetimeFormat: { \"datetime\": \"short\" },\n     datetimeFormat1\": { \"datetime\": \"short\" },\n     monthFormat: { \"date\": \"short\" },\n     weekFormat: { \"date\": \"short\" }\n};\n```\n\nHowever, they may be customized bay passing a similar object, containing just the property to customize in the `editFormats` options property.\n\nThe `addBasicInput` must be passed a [Globalize](https://github.com/jquery/globalize) object, that is needed for the above transformations.\nThe basic fallback module adds also some keyboard control to all numeric fields, so that the user may insert just digits, \ndecimal separators and +/- chars. The `min` attribute is used to verify if the number may be negative, and accordingly if \n+/- chars must be accepted. The developer may provide extra information about the number type with the `data-val-correcttype-type` attribute:\n1 positive integer, 2 integer, 3 float.\n\nHtml5 fallback may be turned on also manually by setting the `browserSupport.fallbackHtml5` options field to `true`, without registering any extra module, in which case \nthe only effect will be the conversion of all not supported inputs into text inputs wih no further processing.\n\n\n`getSupport().Html5InputSupport`, instead contains informations also on possible fallbacks provided:\n\n```\n        {\n            number: number;\n            range: number;\n            date: number;\n            month: number;\n            week: number;\n            time: number;\n            datetime: number;\n            email: number;\n            search: number;\n            tel: number;\n            url: number;\n            color: number;\n        }\n```\nFour integer values are used: 1 no support, 2 not ISO fallback, 3 ISO fallback, 4 native support.\n\nIso fallback means that numbers and dates/time are stored in the input field in exactly the same format of \nnative input type (international ISO format). not ISO fallback is the most common, since most of available widgets use\ncurrent \"locale\" formats(ie mm/dd/yy like dates).\n\nA value of, say 3 doesn't mean necessarely that the browser has no native support since the developer may force the \nusage of the available fallback.\nThe developer may declare the provided fallback by putting in the options object property `browserSupport.fallbacks` an object like the one below:\n\n```\n        {\n            number: \n                {\n                    force: true/false,\n                    type: 1..3\n                },\n            range?: ...,\n            date?: ...,\n            month?: ...,\n            week?: ...,\n            time?: ...,\n            datetime?: ...,\n            email?: ...,\n            search?: ...,\n            tel?: ...,\n            url?: ...,\n            color?: ...,\n        }\n```\nSetting `force` to true forces the usage of the available fallback, while type specifies the type of the  \nprovided fallback.\n\nAll well written fallback modules shoud automatically set the type in the pre-processing stage(The mvcct.enhancer.basic.input module do it!).\n## Time zone processing (starting from version 1.0.2)\nIf a `datetime-local` input has the attribute `data-is-utc=\"true\"`, then its value is assumed to be an UTC date/time. This value is converted \ninto the local time zone during the input processing stage. Moreover, it is assumed that the element after the `datetime-local` input is an input type \"hidden\"\nwhere to store the date/time time-zone offset (that in general depends on the specific date/time value). \nAccordingly, each time the input field is modified by the user, this associated input field is automatically \nupdated with the time zone offset of the newly inseted value. \n## Reading and setting input values\nSince input formats for native support and fallback might differ, support checks must be done \nwhen reading/setting input fields. In order to simplify input field access, when it is loaded, \nthe Htm5 inputs support-detection and fallback module adds two helper methods to the enhancer object, namely:\n\n```\n.format(type, value, [invariant]) \n.parse(type, stringValue, [invariant])\n\n```\nBoth methods takes the type of the original Html5 input as first argument(\"date\", \"time\", etc.). `format` \ntakes a javascript object (date or number depending on the input time) and transforms it in a properly formatted \nstring, while `parse` performs the inverse transformation. I case of fallback the formats specified in \n`editFormats`(or their defaults) are used. If the third optional argument is true parsing/formatting are \ndone using the invariant culture (the one used by native Html5 inputs), otherwise the right culture is auto-detected.\nForcing the invariant culture may be useful for some custom processing of the original input field (for instance \nprocessing min/max or step attributes to set some fallback widget options). \n\n## Adding widgets to Htm5 input fallback\nOne may also add easily widgets(date, datetime, time pickers, siders, etc) to the basic input fallback, \nby adding to the options property `browserSupport.handlers.enhance` an object like:\n\n```\n        {\n            number: function(node){.... return;},\n            range: function(node){.... return;},\n            date: function(node){.... return;},\n            month: function(node){.... return;},\n            week: function(node){.... return;},\n            time: function(node){.... return;},\n            datetime: function(node){.... return;},\n            email: function(node){.... return;},\n            search: function(node){.... return;},\n            tel: function(node){.... return;},\n            url: function(node){.... return;},\n            color: function(node){.... return;},\n        }\n```\nEach of the above functions will be automatically invoked on the corresponding input type after fallback, and can be used to enhance \nthe node with a widget. For instance, we may add a datepicker:\n\n```\n...\ndate: function(node){\n    $(node).datepicker(options.browserSupport.dateOptions);\n},\n...\n\n```\nIt is good practice to take widgets options from the overall mvcct.enhancer options object. \nOne may also define all widgets for a given javascript framework (such Bootstrap for instance), \nin a module that is registred in mvcct.enhancer. A similar module should contain just a pre-process \nfunction that populates the `browserSupport.handlers.enhance` of the options object.\n \n## Sending Htm5 inputs support information to the server\nmvcct.enhancer may package both the information contained in `getSupport().Html5InputSupport` and in `getSupport().Html5InputOriginalSupport` \nin a cookie and/or in an hidden field, so that they are automatically sent to the server that may use them \nto interpret all values returned by the input fields. Infact, the format of numbers and dates are returned changes \nin case of fallback of type 2.\nIn order to enable hidden field/cookie packaging it is enough to fill the options object properties `browserSupport.cookie` \nand/or `browserSupport.forms` with the name of the cookie/hidden field. In case the Html document contains several forms the hidden field is \nadded to all of them.\n\nBoth property data are inserted in the same array as key value pairs:\n```\n[\n    {Key: 'Html5InputSupport.number', Value: 2},\n    ....\n    {Key: 'Html5InputOriginalSupport.number', Value: false}\n    ....\n]\n    \n```\nThen, they are serialized into a jSon strings, and, just for the cookie, also url-encoded.\n\n\n\n## Dependencies propagation module\nIn order to help registred modules to enhance Html, mvcct.enhancer provides a simple dependency propagation engine.\nWe may declare that an input node input1 depends on input2, according to a function f(). After that each time input 2\nchanges (becuase of an user action) f() will be invoked on input1, then all inputs depending on input1 will have their f() invoked, and so on.\nInfinite loops are prevented by stopping the propagation when it returns on an already visited node.\n\nA dependency is declared with the following mvcct.enhancer method:\n\n```\ndependency(name,//string\n           sourceNode, //tracked node\n           targetNode, //dependent node\n           eventNames: //array containing the names of all \n                       //events that will trigger the propagation(ie ['blur', 'keypress'...], etc\n           action: // invoked function function(targetNode, sourceNode) =\u003e void); \n)\n```\n`name` identifies the type of dependency. If you want your dependencies interact globally with all others, please set **name=\"main\"**.\nWhen the value of an input is changed programmatically, dependency propagation\nmay be started by triggering the `\"_enhancer.dependency.\"+name` event.\n\n**IMPORTANT!**, since mvcct.enhancer doesnt depend on jQuery all above events are not defined with jQuery.\n\n## Defining a custom Html5 fallback module\nThe fallback process can be customized at varoius levels. We already described how to add widgets with the `browserSupport.handlers.enhance` \nproperty. More advanced customizations may be obtained by providing other properties in `browserSupport.handlers`, namely:\n\n`browserSupport.handlers.replace=function(type,support)`. It receives as input the original input type and the same object returned by\n`getSupport().Html5InputSupport` and returns the input type to fallback to. If the function returns the same type received as input no further \nprocessing is done (fallback is aborted). The default function always returns \"text\", but in the case the input type is \"range\" and \"number\"\nis supported where it returns \"number\", so that the \"range\" is substituted with a \"number\".\n\n`browserSupport.handlers.translateVal=function(value, type, node)`. It is invoked after a fallback in order to translate \nthe original content of the input into a value appropriate to the way the fallback works. It receives as input, the value to \ntransform (a string), the original input type, and the new input type, and returns the new value(a string).\nThe default implementation furnished by the mvcct.enhancer.input.basic module uses the Globalize library to transform all \ndate/time/numbers related input type values from the international ISO format into the current locale format, \nwhile leaving unchanged all other input types. This transformation is performed only if the new input type is \"text\".\n\n`browserSupport.handlers.fullReplace=function(input)`. It allows a complete customization. It takes the input node\nas input and transform the html around it in a custom way. No other automatic action is performed when this function\nis provided.\n\n \n\n\n\n\n\n  ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmvccontrolstoolkit%2Fmvcct-enhancer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmvccontrolstoolkit%2Fmvcct-enhancer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmvccontrolstoolkit%2Fmvcct-enhancer/lists"}