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.
260 lines (250 loc) • 9.38 kB
JavaScript
/* vim:set ts=4 sw=4 sts=4 expandtab */
/*jshint undef: true es5: true node: true devel: true evil: true
forin: true latedef: false supernew: true */
/*global define: true */
;
var UUID_INCR = 0;
function binarySearchBitshift(list, val) {
var min = 0;
var max = list.length - 1;
for(;;) {
// fall back to linear search, 11 seems to be a good threshold, but this
// depends on the uses comparator!! (here: ===)
if (min + 11 > max) {
for (var i = min; i <= max; ++i) {
if (val === list[i]) {
return i;
}
}
return -1;
}
var mid = (min + max) >> 1;
var dat = list[mid];
if (val === dat)
return mid;
if (val > dat)
min = mid + 1;
else
max = mid - 1;
}
};
var Base = module.exports = Object.freeze(Object.create(Object.prototype, {
UUID: {
value: UUID_INCR
},
UUIDS: {
value: [UUID_INCR]
},
/**
* Creates an object that inherits from `this` object (Analog of
* `new Object()`).
*
* Example:
* var Dog = Base.extend({
* bark: function bark() {
* return "Ruff! Ruff!";
* }
* });
* var dog = Dog.new();
*
* @type {Object}
*/
"new": {
value: function create() {
var object = Object.create(this);
object.initialize.apply(object, arguments);
return object;
}
},
/**
* When new instance of the this prototype is created it's `initialize`
* method is called with all the arguments passed to the `new`. You can
* override `initialize` to set up an instance.
*
* @type {mixed}
*/
initialize: {
value: function initialize() {}
},
/**
* Merges all the properties of the passed objects into `this` instance (This
* method can be used on instances only as prototype objects are frozen).
*
* If two or more argument objects have own properties with the same name,
* the property is overridden, with precedence from right to left, implying,
* that properties of the object on the left are overridden by a same named
* property of the object on the right.
*
* Example:
* var Pet = Dog.extend({
* initialize: function initialize(options) {
* // this.name = options.name -> would have thrown (frozen prototype)
* this.merge(options) // will override all properties.
* },
* call: function(name) {
* return this.name === name ? this.bark() : "";
* },
* name: null
* });
* var pet = Pet.new({ name: "Pippa", breed: "Jack Russell" });
* pet.call("Pippa"); // 'Ruff! Ruff!'
*
* @param {Object} [obj1] override prototype's properties with the values in this object
* @type {void}
*/
merge: {
value: function merge() {
var descriptor = {};
var uuids = Array.isArray(this.UUIDS) ? [].concat(this.UUIDS) : [this.UUID];
Array.prototype.forEach.call(arguments, function(properties) {
// Make sure the merged-in object has a UUID
if (!properties.hasOwnProperty("UUID")) {
properties.UUID = ++UUID_INCR;
if (!properties.UUIDS)
properties.UUIDS = [];
properties.UUIDS.push(properties.UUID);
}
// Copy the object properties over
Object.getOwnPropertyNames(properties).forEach(function(name) {
if (name == "UUIDS") {
properties[name].forEach(function(uuid) {
if (binarySearchBitshift(uuids, uuid) == -1)
uuids.push(uuid);
});
// Keep the list sorted at all times (binary search)
uuids.sort(function (a, b) { return a - b; });
}
else
descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
});
});
var base = ++UUID_INCR;
descriptor.UUID = {
value: base
};
uuids.push(base);
descriptor.UUIDS = {
value: uuids
};
Object.defineProperties(this, descriptor);
return this;
}
},
/**
* Takes any number of argument objects and returns frozen, composite object
* that inherits from `this` object and combines all of the own properties of
* the argument objects. (Objects returned by this function are frozen as
* they are intended to be used as types).
*
* If two or more argument objects have own properties with the same name,
* the property is overridden, with precedence from right to left, implying,
* that properties of the object on the left are overridden by a same named
* property of the object on the right.
*
* Example:
* // ### Object composition ###
*
* var HEX = Base.extend({
* hex: function hex() {
* return "#" + this.color;
* }
* });
*
* var RGB = Base.extend({
* red: function red() {
* return parseInt(this.color.substr(0, 2), 16);
* },
* green: function green() {
* return parseInt(this.color.substr(2, 2), 16);
* },
* blue: function blue() {
* return parseInt(this.color.substr(4, 2), 16);
* }
* });
*
* var CMYK = Base.extend(RGB, {
* black: function black() {
* var color = Math.max(Math.max(this.red(), this.green()), this.blue());
* return (1 - color / 255).toFixed(4);
* },
* cyan: function cyan() {
* var K = this.black();
* return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
* },
* magenta: function magenta() {
* var K = this.black();
* return (((1 - this.green() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
* },
* yellow: function yellow() {
* var K = this.black();
* return (((1 - this.blue() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
* }
* });
*
* var Color = Base.extend(HEX, RGB, CMYK, {
* initialize: function Color(color) {
* this.color = color;
* }
* });
*
* // ### Prototypal inheritance ###
*
* var Pixel = Color.extend({
* initialize: function Pixel(x, y, hex) {
* Color.initialize.call(this, hex);
* this.x = x;
* this.y = y;
* },
* toString: function toString() {
* return this.x + ":" + this.y + "@" + this.hex();
* }
* });
*
* var pixel = Pixel.new(11, 23, "CC3399");
* pixel.toString(); // 11:23@#CC3399
*
* pixel.red(); // 204
* pixel.green(); // 51
* pixel.blue(); // 153
*
* pixel.cyan(); // 0.0000
* pixel.magenta(); // 0.7500
* pixel.yellow(); // 0.2500
*
* @param {Object} obj1 extend object's properties with the values in this object
*/
extend: {
value: function extend() {
return this.merge.apply(Object.create(this), arguments);
}
},
/**
* Checks if an object was merged with this object, using Base.extend() or
* Base#merge(). Each object that was merged in at least once carries a
* signature in the shape of a bitflag.
*
* Example:
* To continue with the example provided by Base.extend():
*
* // an instance of Color should contain the following objects:
* var color = Color.new("CC3399");
* color.hasFeature(HEX); // true
* color.hasFeature(RGB); // true
* color.hasFeature(CMYK); // true
* color.hasFeature(Pixel); // false
*
* // an instance of Pixel should contain the following objects:
* var pixel = Pixel.new(11, 23, "CC3399");
* pixel.hasFeature(HEX); // true
* pixel.hasFeature(RGB); // true
* pixel.hasFeature(CMYK); // true
* pixel.hasFeature(Color); // true
*
* @param {Object} obj1: check if this object is part of this object
*/
hasFeature: {
value: function hasFeature(base) {
return typeof base.UUID != "undefined" && binarySearchBitshift(this.UUIDS, base.UUID) != -1;
}
}
}));