jsdav-ext
Version:
jsDAV allows you to easily add WebDAV support to a NodeJS application. jsDAV is meant to cover the entire standard, and attempts to allow integration using an easy to understand API.
292 lines (262 loc) • 8.74 kB
JavaScript
/*
* @package jsDAV
* @subpackage VObject
* @copyright Copyright(c) 2013 Mike de Boer. <info AT mikedeboer DOT nl>
* @author Mike de Boer <info AT mikedeboer DOT nl>
* @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
*/
"use strict";
var jsVObject_Node = require("./node");
var jsVObject_Parameter = require("./parameter");
var Util = require("../shared/util");
/**
* VObject Property
*
* A property in VObject is usually in the form PARAMNAME:paramValue.
* An example is : SUMMARY:Weekly meeting
*
* Properties can also have parameters:
* SUMMARY;LANG=en:Weekly meeting.
*/
var jsVObject_Property = module.exports = jsVObject_Node.extend({
/**
* Propertyname
*
* @var string
*/
name: null,
/**
* Group name
*
* This may be something like 'HOME' for vcards.
*
* @var string
*/
group: null,
/**
* Property value
*
* @var string
*/
value: null,
/**
* If properties are added to this map, they will be automatically mapped
* to their respective classes, if parsed by the reader or constructed with
* the 'create' method.
*
* @var array
*/
classMap: {
"COMPLETED" : "./property/dateTime",
"CREATED" : "./property/dateTime",
"DTEND" : "./property/dateTime",
"DTSTAMP" : "./property/dateTime",
"DTSTART" : "./property/dateTime",
"DUE" : "./property/dateTime",
"EXDATE" : "./property/multiDateTime",
"LAST-MODIFIED": "./property/dateTime",
"RECURRENCE-ID": "./property/dateTime",
"TRIGGER" : "./property/dateTime",
"N" : "./property/compound",
"ORG" : "./property/compound",
"ADR" : "./property/compound",
"CATEGORIES" : "./property/compound"
},
/**
* Creates the new property by name, but in addition will also see if
* there's a class mapped to the property name.
*
* Parameters can be specified with the optional third argument. Parameters
* must be a key->value map of the parameter name, and value. If the value
* is specified as an array, it is assumed that multiple parameters with
* the same name should be added.
*
* @param {String} name
* @param {String} value
* @param {Object} parameters
* @return Property
*/
create: function(name, value, parameters) {
value = value || null;
parameters = parameters || {};
name = name.toUpperCase();
var shortName = name;
if (shortName.indexOf(".") > -1) {
var parts = shortName.split(".");
shortName = parts[1];
}
if (this.classMap[shortName])
return require(this.classMap[shortName]).new(name, value, parameters);
else
return jsVObject_Property.new(name, value, parameters);
},
/**
* Creates a new property object
*
* Parameters can be specified with the optional third argument. Parameters
* must be a key->value map of the parameter name, and value. If the value
* is specified as an array, it is assumed that multiple parameters with
* the same name should be added.
*
* @param {String} name
* @param {String} value
* @param {Object} parameters
*/
initialize: function(name, value, parameters) {
value = value || null;
parameters = parameters || {};
if (!Util.isScalar(value) && value !== null)
throw new Error("The value argument must be scalar or null");
this.children = [];
name = name.toUpperCase();
var group = null;
if (name.indexOf(".") > -1) {
var parts = name.split(".");
group = parts[0];
name = parts[1];
}
this.name = name;
this.group = group;
this.setValue(value);
var paramValues, i, l;
for (var paramName in parameters) {
paramValues = parameters[paramName];
if (!Array.isArray(paramValues))
paramValues = [paramValues];
for (i = 0, l = paramValues.length; i < l; ++i)
this.add(paramName, paramValues[i]);
}
},
setParts: function(parts) {
this.setValue(parts);
},
getParts: function() {
if(this.value === null) {
return [];
} else if(this.value instanceof Array) {
return this.value;
} else {
return [ this.value ];
}
},
getValue: function() {
return this.value;
},
/**
* Updates the internal value
*
* @param {String} value
* @return void
*/
setValue: function(value) {
this.value = value;
},
/**
* Turns the object back into a serialized blob.
*
* @return string
*/
serialize: function() {
var str = this.name;
if (this.group)
str = this.group + "." + this.name;
this.children.forEach(function(param) {
str += ";" + param.serialize();
});
str += ":" + this.value
.replace("\\", "\\\\")
.replace("\n", "\\n");
var out = "";
while (str.length > 0) {
if (str.length > 75) {
out += str.substr(0,75) + "\r\n";
str = " " + str.substr(75, str.length);
}
else {
out += str + "\r\n";
str = "";
break;
}
}
return out;
},
/**
* Adds a new componenten or element
*
* You can call this method with the following syntaxes:
*
* add(Parameter element)
* add(string name, value)
*
* The first version adds an Parameter
* The second adds a property as a string.
*
* @param {mixed} item
* @param {mixed} itemValue
* @return void
*/
add: function(item, itemValue) {
itemValue = itemValue || null;
if (item.hasFeature(jsVObject_Parameter)) {
if (itemValue !== null)
throw new Error("The second argument must not be specified, when passing a VObject");
item.parent = this;
this.children.push(item);
}
else if(typeof item == "string") {
var parameter = new jsVObject_Parameter(item, itemValue);
parameter.parent = this;
this.children.push(parameter);
}
else
throw new Error("The first argument must either be a Node a string");
},
/**
* Called when this object is being cast to a string
*
* @return string
*/
toString: function() {
return this.value.toString();
},
/**
* Validates the node for correctness.
*
* The following options are supported:
* - Node::REPAIR - If something is broken, and automatic repair may
* be attempted.
*
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @param {Number} options
* @return array
*/
validate: function(options) {
options = options || 0;
var warnings = [];
// Checking if the propertyname does not contain any invalid bytes.
if (!/^([A-Z0-9\-]+)/.test(this.name)) {
warnings.push({
"level": 1,
"message": "The propertyname: " + this.name + " contains invalid characters. Only A-Z, 0-9 and - are allowed",
"node": this
});
if (options & this.REPAIR) {
// Uppercasing and converting underscores to dashes.
this.name = this.name.replace("_", "-").toUpperCase();
// Removing every other invalid character
this.name = this.name.replace(/([^A-Z0-9\-])/g, "");
}
}
// Validating inner parameters
this.children.forEach(function(param) {
warnings = warnings.concat(param.validate(options));
});
return warnings;
}
});