Skip to content

Parser Design Data

Zearin edited this page Jan 30, 2020 · 4 revisions

The ical.js parser uses the design data available via ICAL.design to decide if the received data is semantically correct. It contains information about components, properties, property parameters, and property values. There is specific design data for iCalendar, vCard 3, and vCard 4 which is used based on the component type or the passed design set.

Not all aspects of the design data you see in design.js are actually enforced. A strict mode parser or validator is conceivable in the future.

Registering property data

If a new property is added for which the property name is not set in the design data, the default type unknown will be set in the jCal array as the type. While this is not incorrect (and works well when converting from jCal to iCalendar by fully omitting the VALUE parameter), it's usually best to register an explicit type for the property.

The iCalendar specification works with implicit value and parameter types, but allows explicitly mentioning the value data type through the VALUE parameter. jCal, on the other hand, explicitly mentions the value type in its array structure. When converting from iCalendar to jCal, some assumptions have to be made if the value data type is not explicitly mentioned. This is mostly the case for X-Properties and additionally registered IANA properties.

While the iCalendar specification assumes that unknown values should be of the TEXT type, this doesn't work in practice. If an extension to iCalendar has registered a property with the default type of PERIOD while also allowing multiple values, round-tripping between iCalendar and jCal will cause extra escaping of the comma character used to separate the values (FOO:bar,baz becomes FOO:bar\,baz).

To solve this, jCal has introduced the "unknown" type. It is used for all properties the parser doesn't know about, and keeps the value as-is. Let's say you have your own X-Property that uses multiple values, and you want to retrieve them separately. (Alternatively, suppose there is a new IANA property from an extension that has not been added to the ical.js design data. In this latter case, please file an issue to make sure it will be included in the future!)

You can make sure your property is understood by adding information to the design set for the type of component you are working with:

// iCalendar: X-MY-PROPERTY:foo
//      jCal: ["x-my-property", {}, "text", "foo"]
ICAL.design.icalendar.property["x-my-property"] = {
  defaultType: "text", // [required] The default type from ICAL.design.value
  allowedTypes: ["period", "text"], // [optional] Valid value types this property can have (currently unused)
  detectType: function(value) { ... } // [optional] A function called to determine which type the value is 
};

// iCalendar: X-MY-MULTIVAL:foo,bar,baz
//      jCal: ["x-my-multival", {}, "text", "foo", "bar", "baz"]
ICAL.design.icalendar.property["x-my-multival"] = {
  defaultType: "text", // [required] The default type from ICAL.design.value
  multiValue: ","      // [optional] This property takes multiple, comma-separated values
                       //              and turns each of them into a jCal value.
};

// iCalendar: X-MY-STRUCTUREDVAL:foo;bar;baz
//      jCal: ["x-my-structuredval", {}, "text", [ "foo", "bar", "baz" ] ]
ICAL.design.icalendar.property["x-my-structuredval"] = {
  defaultType: "text", // [required] The default type from ICAL.design.value
  structuredValue: ";" // [optional] This property takes multiple, semicolon-separated values
                       //              and turns them into a single jCal value.
};

Registering parameter value types

A parameter doesn't have an explicit value type, but can be parsed in various ways. If you have your own parameters that need a different format, you can do it the following way:

// iCalendar: SUMMARY;MYENUMPARAM=FOO:value
//      jCal: ["summary", { "myenumparam": "FOO" }, "text", "value"]
ICAL.design.icalendar.param["myenumparam"] = {
  values: [ "FOO", "BAR" ],  // [optional] An array of valid values for this parameter
  allowXName: true,          // If true, X-Parameters are also allowed
  allowIanaToken: true       // If true, other IANA tokens are also allowed
};

// iCalendar: SUMMARY;MYTYPEDPARAM=user@example.com,user2@example.com:value
//      jCal: ["summary", { "mytypedparam": ["user@example.com", "user2@example.com" ] }, "text", "value" ]
ICAL.design.icalendar.param["mytypedparam"] = {
  valueType: "cal-address", // If set, the parameter must use this value type as a format
  multiValue: ","           // The parameter can consist of multiple values itself, which will be an array
};

// iCalendar: SUMMARY;MYMULTIPARAM="FOO","BAR":value
//      jCal: ["summary", { "mymultiparam": ["FOO", "BAR"] }, "text", "value"]
ICAL.design.icalendar.param["mymultiparam"] = { 
  valueType: "text", // If set, the parameter must use this value type as a format
  multiValue: ",",   // The parameter can consist of multiple values itself, which will be an array
  multiValueSeparateDQuote: true 
                     // If true, double quotes enclose (multi-)values instead of the whole property.
};

Registering data value types

Registering a new data value type is rather unlikely, this is usually the result of a new RFC defining a value type. Data value types are things like "text", "recur", "cal-address". If this does happen, please file an issue.

Here is the code to register a value type:

ICAL.design.icalendar.value["newvalue"] = { ... };

The object passed may optionally contain the following functions and properties:

  • decorate and undecorate: Functions to augment the way the value is returned (i.e. as ICAL.Duration)
  • fromICAL and toICAL: Functions to modify the way the value is formatted between iCalendar and jCal
  • values: An array of valid values for this value type (currently unused)
  • matches: A regular expression the value type must match (currently unused)

The functions are further described in the following sections.

Conversion between iCalendar and jCal values

Property value design data contains two functions called fromICAL() and toICAL(), which are used to convert property values between iCalendar and jCal. The type used depends on the value data type in jCal. Here are a few examples:

ICAL.design.icalendar.value['date-time'].fromICAL("20140102T030405");   // "2014-01-02T03:04:05"
ICAL.design.icalendar.value['date-time'].toICAL("2014-01-02T03:04:05"); // "20140102T030405"

ICAL.design.icalendar.value['recur'].fromICAL("FREQ=WEEKLY;COUNT=2");      // { freq: "WEEKLY", count: 2 }
ICAL.design.icalendar.value['recur'].toICAL({ freq: "WEEKLY", count: 2 }); // "FREQ=WEEKLY;COUNT=2"

Decorating Values

Property value design data also contains two functions called decorate() and undecorate(). These are used on the next layer to take jCal data and turn it into a rich object which provides more methods to process the data value.

These functions do not take raw iCalendar values, which are different (especially for dates and recurrence).

Examples:

var icalTime = ICAL.design.icalendar.value['date-time'].decorate("2014-01-02T03:04:05"); // Returns an ICAL.Time object
ICAL.design.icalendar.value['date-time'].undecorate(icalTime); // Returns "2014-01-02T03:04:05"

var icalRecur = ICAL.design.icalendar.value['recur'].decorate({ freq: "WEEKLY", count: 2 }); // Returns an ICAL.Recur object
ICAL.design.icalendar.value['recur'].undecorate(icalRecur); // Returns { freq: "WEEKLY", count: 2 }