Config.js – a JavaScript cofiguration library

Description

Config.js allows developers to configure their applications in an XML block instead of hard-coding values inside their scripts or in JSON objects.  The XML can be embedded inside an HTML document or in a separate XML file.  The configuration block may contain strings, numbers, arrays and HTML.  Furthermore, variables can be inserted into these values, allowing developers to use the XML block as a simple templating system for their applications.

Usage

For the majority of cases, using the library consists of three simple steps.

  1. Include the EventHelpers.js and config.js script in the <head> of your document:
    <script type="text/javascript src="/path/to/EventHelpers.js"></script>
    <script type="text/javascript" src="/path/to/config-2.0.js"></script>
  2. The config block can be inserted into your HTML document by defining any HTML node with an id of config.  The only content inside this node should be an HTML comment with your configuration values inside.  Below is an example XML block that defines a configuration property formValidator.errors.mandatoryField:
    <div id=”config”>
    <!--
    <config>
      <formValidator>
        <errors>
          <mandatoryField>
            The above field is mandatory
          </mandatoryField>
        </errors>
      </formValidator>
    </config>
    -->
    </div>
  3. Accessing the data inside the configuration block can be done with JavaScript by using the config.getValue() function.  In the above example, the text inside the mandatoryField tag can be accessed inside JavaScript using the config.getValue() function like this:
    document.getElementById("message") =
      config.getValue("formValidator.errors.mandatoryField");

    Note: any call to config.getValue() must be made after config.js loads the XML block into memory. If you are initializing your own code with a window.onload, you should replace it with a call to config.addLoadEvent().

    For example, replace

    window.onload=myObject.init

    with:

    config.addLoadEvent(myObject.init)

You can put as many values inside your XML as you like.  For example the values inside the XML configuration block below can be retrieved using config.getValue('formValidator.errors.mandatoryField')config.getValue('formValidator.errors.postalCode') and config.getValue('formValidator.alerts.success').

<!--
<config>
  <formValidator>
    <errors>
      <mandatoryField>
        The above field is mandatory
      </mandatoryField>
      <postalCodeError>
        You did not format the postal code correctly.
      </postalCodeField>
     </errors>
     <alerts>
      <success>
        Your changes have been submitted.
      </success>
     </alerts>
  </formValidator>
</config>
-->
</div>

Note the object oriented “dot” notation of the configuration properties, which corresponds to the XML’s tree structure. The usual convention I follow is:

  1. I code all configuration values that have to do with the same script using the same top level object (in this case, the top-level object  is called formValidator since it is being accessed by a script called formValidator.js).
  2. I then create properties for that top level object that describe logical categories that I want to configure  (e.g. templates, errors, urls,  etc).
  3. I finally have the third level contain the actual configuration values.

While I consider this best practice (insomuch as it works well for me), developers are of course not stuck with this convention, and can format their XML as they please, as long as there are no duplicate property names within an object.

HTML Values

Note that a developer can insert HTML inside the XML string by placing the HTML content inside a CDATA node:

<config>
  <formValidator>
    <templates>
      <hasError><![CDATA[
        <div id="errorMessage">
        <p>There are errors with the information you are trying
        to submit.  Please check the form below, correct the
        errors and resubmit.</p>
        </div>
      ]]></hasError>
      <submissionAlert>
        If this was a real form, the information above would
        have been submitted.  Since this isn't, we won't submit
        any of the form information.
      </submissionAlert>
    </templates>
    <errors>
      <mandatoryField>
        The above field is mandatory
      </mandatoryField>
    </errors>
  </formValidator>
</config>

Note that the CDATA node has to immediately follow the containing tag (in the above example, you’ll see there are no spaces hasError tag). This restriction will probably be relaxed in a subsequent release.

Removing Extraneous Spaces

Let’s look at this example:

<config>
  <formValidator>
    <templates>
      <submissionAlert>
      If this was a real form, the information above would
      have been submitted.  Since this isn't, we won't submit
      any of the form information.
      </submissionAlert>
    </templates>
  </formValidator>
</config>

If we tried to execute window.alert("formValidator.templates.submissionError"), we would get an alert box that would look something like this:

This alert has bad spacing due to the way the XML is formatted.

This alert has bad spacing due to the way the XML is formatted.

Doesn’t that look ugly?  This is because the formatting of the text inside it’s configuration block is indented so that it is readable to the developer when viewing the code (Note that all carriage returns are ignored by config.js, but the spaces are left intact). In order to keep the pretty XML formatting while getting rid of the extra spacing inside the alert box, we can set the removeMultispaces attribute of it’s configuration tag to "true":

<config>
  <formValidator>
    <templates>
      <submissionAlert removeMultispaces="true">
      If this was a real form, the information above would
      have been submitted.  Since this isn't, we won't submit
      any of the form information.
      </submissionAlert>
    </templates>
  </formValidator>
</config>

This results in an alert box that is formatted as intended:

All the extraneous spacing has been removed, regardless of where it appears in the XML.

All the extraneous spacing has been removed, regardless of where it appears in the XML.

Dynamic Content:

Injecting variables into the contents of the configuration value can be achieved by using config.getScriptedValue().   To do so, you first insert the variables into your XML using placeholders surrounded by @-symbols, which we’ll call “at values”:

<config>
  <formValidator>

    <submissionAlert>
    If this was a real form, the information above would
    have been submitted.  Since this isn't, we won't submit
    any of the form information, including the @ccType@ Credit Card number
    @ccNum@.
    </submissionAlert>

  </formValidator>
</config>

In this example, @ccType@ and @ccExp@ are the at-values that will contain dynamic content. The following JavaScript creates an alert containing the configuration value of formValidator.templates.submissionAlert, complete with dynamic content.

var formToValidate = document.getElementById("formToValidate");
var alertString = config.getScriptedValue(
  "formValidator.templates.submissionAlert",
  {
    ccNum: formToValidate.ccNum.value,
    ccType: formToValidate.ccType.value
  });

This replaces the at-value placeholders @ccNum@ and @ccType@ with the values of the form fields ccNum and ccType, respectively.

External XML File

Instead of having the XML contained within an HTML comment, you can have the file loaded separately by defining the file name inside the config node like this:

<!--
xml=xml/config.xml
-->
</div>

Comments

If the XML is embedded in an HTML comment, then comments to the embedded XML must be put inside the XML block using JSP style <%-- --%> comment notation:

<config>
  <formValidator>
    <%--
    This section contains english text that will
    be inserted into the page
    --%>
    <templates>
    <hasError><![CDATA[
      <div id="errorMessage">
      <p>There are errors with the information you are trying
      to submit.  Please check the form below, correct the
      errors and resubmit.</p>
      </div>
    ]]></hasError>
    <submissionAlert removeMultispaces="true">
      If this was a real form, the information above would
      have been submitted.  Since this isn't, we won't submit
      any of the form information, including the @ccType@ Credit
      Card number @ccNum@.
    </submissionAlert>
  </templates>
</formValidator>
</config>

If the configuration XML is stored in an external file, use regular XML<!-- --!> comment notation:

<config>
  <formValidator>
    <!--
    This section contains english text that will
    be inserted into the page
    --!>
    <templates>
    <hasError><![CDATA[
      <div id="errorMessage">
      <p>There are errors with the information you are trying
      to submit.  Please check the form below, correct the
      errors and resubmit.</p>
      </div>
    ]]></hasError>
    <submissionAlert removeMultispaces="true">
      If this was a real form, the information above would
      have been submitted.  Since this isn't, we won't submit
      any of the form information, including the @ccType@ Credit
      Card number @ccNum@.
    </submissionAlert>
  </templates>
</formValidator>
</config>

Object Arrays

Developers can also define arrays inside configuration files as well, by using the datatype attribute inside a tag.  In the following example, the configuration property formValidator.creditCardForm.inputs is an array containing three objects:

<config>
  <formValidator>
    <creditCardForm>
      <inputs datatype="array">

       <ccType>
        <postalCode>
          <isMandatory>true</isMandatory>
          <%-- Note: We assume Canadian postal codes only - -%>
          <pattern>
            ^[a-zA-Z]\d[a-zA-Z]\s*\d[a-zA-Z]\d$
          </pattern>
          <unmatchError>
            The postal code must be in a valid format.
          </unmatchError>
        </postalCode>
      </ccType>

      <ccNum>
        <isMandatory>true</isMandatory>
        <pattern>^\d{16}$</pattern>
        <unmatchError>
          The credit card you entered is not valid.
        </unmatchError>
      </ccNum>

      <ccExp>
        <isMandatory>true</isMandatory>
        <pattern>^(0[1-9]|1[1-2])/\d{2}$</pattern>
        <unmatchError>
          The expiry date must be in the form yy/mm.
        </unmatchError>
      </ccExp>

    </inputs>
  </creditCardForm>
</formValidator>
</config>

Each of these elements have three properties (isMandatory, pattern and unmatchError).  Note that each element in the array don’t have to have the same properties, in the same way that a the elements in a JavaScript array doesn’t have the same type of object.  In the above example, we can  access these array values with the following script:

var inputs = config.getValue("formValidator.creditCardForm.inputs");
var ccExp = inputs["ccExp"]
var ccExpError = ccExp.unmatchError;
<mandatoryField>
        The above field is mandatory
      </mandatoryField>

Examples

Download

You can get full source here:

config.js v.2.0 and example code (zip format)

This code is released under the MIT License. Although the license doesn’t compel you to do so, I would be happy to hear how this library is being used. I would also like to see how people have improved or added to the code so I can share the changes with others. Again, this is not something the license compels you to do – just consider it a courtesy.

6 Comments

6 responses so far ↓
  • 1 syber // Apr 14, 2011 at 10:05 am

    Another great article Zed! I can see this making my life – and my localization work – much tidier :)

  • 2 zoltan // Apr 14, 2011 at 10:38 pm

    @Syber: thanks for the feedback. I have found it useful for all my localization needs thus far. Plus it’s great to have a place to store HTML snippets without hard coding them in your scripts. :)

  • 4 Gavin // Apr 5, 2012 at 11:11 am

    Nice! This is exactly what i was looking for, coming from a Flex background I wasn’t sure how people configured JS applications but its nice to see a similar solution to the one i’ve employed many times in my Flash/Flex projects.

  • 5 zoltan // May 8, 2012 at 11:06 pm

    @Gavin: Thanks for the kudos! I really like this script, and I’m surprised it hasn’t gotten more attention than the other ones on this blog. Appreciate the feedback! :-)

  • 6 kiran // Jul 28, 2015 at 2:25 am

    Hello, nice article zed. Can we use Multiple XML files for configurations and decide on runtime that which file we should read the value.???

Give Feedback

Don't be shy! Give feedback and join the discussion.

Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.

An orange star denotes a required field.