ical.js-one.com
Version:
[](http://travis-ci.org/mozilla-comm/ical.js)
214 lines (177 loc) • 5.27 kB
JavaScript
ICAL.stringify = (function() {
'use strict';
var LINE_ENDING = '\r\n';
var DEFAULT_TYPE = 'text';
var design = ICAL.design;
var helpers = ICAL.helpers;
/**
* Convert a full jCal Array into a ical document.
*
* @param {Array} jCal document.
* @return {String} ical document.
*/
function stringify(jCal) {
if (!jCal[0] || jCal[0] !== 'icalendar') {
throw new Error('must provide full jCal document');
}
// 1 because we skip the initial element.
var i = 1;
var len = jCal.length;
var result = '';
for (; i < len; i++) {
result += stringify.component(jCal[i]) + LINE_ENDING;
}
return result;
}
/**
* Converts an jCal component array into a ICAL string.
* Recursive will resolve sub-components.
*
* Exact component/property order is not saved all
* properties will come before subcomponents.
*
* @param {Array} component jCal fragment of a component.
*/
stringify.component = function(component) {
var name = component[0].toUpperCase();
var result = 'BEGIN:' + name + LINE_ENDING;
var props = component[1];
var propIdx = 0;
var propLen = props.length;
for (; propIdx < propLen; propIdx++) {
result += stringify.property(props[propIdx]) + LINE_ENDING;
}
var comps = component[2];
var compIdx = 0;
var compLen = comps.length;
for (; compIdx < compLen; compIdx++) {
result += stringify.component(comps[compIdx]) + LINE_ENDING;
}
result += 'END:' + name;
return result;
}
/**
* Converts a single property to a ICAL string.
*
* @param {Array} property jCal property.
*/
stringify.property = function(property) {
var name = property[0].toUpperCase();
var jsName = property[0];
var params = property[1];
var line = name;
var paramName;
for (paramName in params) {
if (params.hasOwnProperty(paramName)) {
line += ';' + paramName.toUpperCase();
line += '=' + stringify.propertyValue(params[paramName]);
}
}
// there is no value so return.
if (property.length === 3) {
// if no params where inserted and no value
// we given we must add a blank value.
if (!paramName) {
line += ':';
}
return line;
}
var valueType = property[2];
var propDetails;
var multiValue = false;
var isDefault = false;
if (jsName in design.property) {
propDetails = design.property[jsName];
if ('multiValue' in propDetails) {
multiValue = propDetails.multiValue;
}
if ('defaultType' in propDetails) {
if (valueType === propDetails.defaultType) {
isDefault = true;
}
} else {
if (valueType === DEFAULT_TYPE) {
isDefault = true;
}
}
} else {
if (valueType === DEFAULT_TYPE) {
isDefault = true;
}
}
// push the VALUE property if type is not the default
// for the current property.
if (!isDefault) {
// value will never contain ;/:/, so we don't escape it here.
line += ';VALUE=' + valueType.toUpperCase();
}
line += ':';
if (multiValue) {
line += stringify.multiValue(
property.slice(3), multiValue, valueType
);
} else {
line += stringify.value(property[3], valueType);
}
return ICAL.helpers.foldline(line);
}
/**
* Handles escaping of property values that may contain:
*
* COLON (:), SEMICOLON (;), or COMMA (,)
*
* If any of the above are present the result is wrapped
* in double quotes.
*
* @param {String} value raw value.
* @return {String} given or escaped value when needed.
*/
stringify.propertyValue = function(value) {
if ((helpers.unescapedIndexOf(value, ',') === -1) &&
(helpers.unescapedIndexOf(value, ':') === -1) &&
(helpers.unescapedIndexOf(value, ';') === -1)) {
return value;
}
return '"' + value + '"';
}
/**
* Converts an array of ical values into a single
* string based on a type and a delimiter value (like ",").
*
* @param {Array} values list of values to convert.
* @param {String} delim used to join the values usually (",", ";", ":").
* @param {String} type lowecase ical value type
* (like boolean, date-time, etc..).
*
* @return {String} ical string for value.
*/
stringify.multiValue = function(values, delim, type) {
var result = '';
var len = values.length;
var i = 0;
for (; i < len; i++) {
result += stringify.value(values[i], type);
if (i !== (len - 1)) {
result += delim;
}
}
return result;
}
/**
* Processes a single ical value runs the associated "toICAL"
* method from the design value type if available to convert
* the value.
*
* @param {String|Numeric} value some formatted value.
* @param {String} type lowecase ical value type
* (like boolean, date-time, etc..).
* @return {String} ical value for single value.
*/
stringify.value = function(value, type) {
if (type in design.value && 'toICAL' in design.value[type]) {
return design.value[type].toICAL(value);
}
return value;
}
return stringify;
}());