(function (root, factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(exports);
        if (v !== undefined && v !== null) {
            module.exports = v;
        }

    } else if (typeof define === "function" && define.amd) {
        define(["exports"], factory);

    } else {
        // Browser globals
        root.nl = factory({});
    }
})(this, function (exports) {
    "use strict";

    // This flag is a necessity to support interoperability between CommonJS/AMD/UMD/ES6 module loaders.
    // this flag triggers special compatibility behaviour of non-ES6 loaders to handle "default" exports
    // of this module exactly like ES6.
    //
    // see https://github.com/esnext/es6-module-transpiler/issues/86
    // https://github.com/esnext/es6-module-transpiler/issues/85
    // https://github.com/Microsoft/TypeScript/issues/5844#issuecomment-161425239
    Object.defineProperty(exports, "__esModule", { value: true });

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

/**
 * A webDAV client library for JavaScript
 * @author Niek Bosch
 */

// Create the namespace if that's not done yet
if (nl === undefined) {
  /** @namespace */
  var nl = {};
}
if (nl.sara === undefined) {
  /** @namespace */
  nl.sara = {};
}
if (nl.sara.webdav === undefined) {
  /** @namespace The entire library is in this namespace. */
  nl.sara.webdav = {};
}
if (nl.sara.webdav.codec === undefined) {
  /** @namespace Holds all the standard codecs for converting properties */
  nl.sara.webdav.codec = {};
}

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Exception is already defined, we have a namespace clash!
if (nl.sara.webdav.Exception !== undefined) {
  throw 'Class name nl.sara.webdav.Exception already taken, could not load JavaScript library for WebDAV connectivity.';
}

/**
 * @class An exception
 * @param  {String}  [message]  Optional; A human readable message
 * @param  {Number}  [code]     Optional; The error code. It is best to use the class constants to set this.
 * @property  {String}  message  The exception message
 * @property  {Number}  code     The exception code
 */
nl.sara.webdav.Exception = function(message, code) {
  // First define public attributes
  Object.defineProperty(this, 'message', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'code', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if (message !== undefined) {
    this.message = message;
  }
  if (code !== undefined) {
    this.code = code;
  }
};

/**#@+
 * Declaration of the error code constants
 */
nl.sara.webdav.Exception.WRONG_TYPE = 1;
nl.sara.webdav.Exception.NAMESPACE_TAKEN = 2;
nl.sara.webdav.Exception.UNEXISTING_PROPERTY = 3;
nl.sara.webdav.Exception.WRONG_XML = 4;
nl.sara.webdav.Exception.WRONG_VALUE = 5;
nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER = 6;
nl.sara.webdav.Exception.AJAX_ERROR = 7;
nl.sara.webdav.Exception.NOT_IMPLEMENTED = 8;
/**#@-*/

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Overloads the default toString() method so it returns the message of this exception
 *
 * @returns  {String}  A string representation of this property
 */
nl.sara.webdav.Exception.prototype.toString = function() {
  return this.message;
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Ie is already defined, we have a namespace clash!
if (nl.sara.webdav.Ie !== undefined) {
  throw 'Class name nl.sara.webdav.Ie already taken, could not load JavaScript library for WebDAV connectivity.';
}

/**
 * @class This class holds all IE hacks
 */
nl.sara.webdav.Ie = {};

/**
 * Although this library has no intent to work in IE older than versions 9, it should work in IE and sometimes requires some special attention for this wonderful browser
 *
 * @var  Boolean  True if the current browser is IE
 */
nl.sara.webdav.Ie.isIE = false;
/*@cc_on
nl.sara.webdav.Ie.isIE = true;
@*/

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Returns the localName of a DOM Node object
 *
 * @param    {Node}    node  The node to determine the localname for
 * @returns  {String}        The local name
 */
nl.sara.webdav.Ie.getLocalName = function(node) {
  if (nl.sara.webdav.Ie.isIE && node.baseName) {
    return node.baseName;
  }else{
    return node.localName;
  }
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Client is already defined, we have a namespace clash!
if (nl.sara.webdav.Client !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Client already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Connection to a WebDAV server
 *
 * @param  {Array}  [config]  A configuration object. It can contain the following fields:
 *                            - host; String containing the hostname to connect to (default: current host)
 *                            - useHTTPS; If set to (boolean) false, a regular HTTP connection will be used, any other value (even 0 or '' which evaluate to false) and HTTPS will be used instead. Is ignored if host is not set. (default: true)
 *                            - port; Integer with the port number to use. Is ignored if host is not set. (default: 443 for HTTPS and 80 for HTTP)
 *                            - defaultHeaders; An array containing default headers to use for each request. Header names should be the keys. (default: {} i.e. no default headers)
 *                            - username; A string with the username to use for authentication. (default: the current username if any)
 *                            - password; A string with the password to use for authentication. This is only used if a username is supplied. (default: the current password if any)
 *                            For backwards compatibility reasons, it is also
 *                            possible to specify the 'host', 'useHTTPS', 'port'
 *                            and 'defaultHeaders' as regular parameters (in
 *                            that order). Note that useHTTPS works differently
 *                            in that case; any other value than boolean True
 *                            will result in regular HTTP. If you want to supply
 *                            a username and password, you will have to use the
 *                            config array, you cannot pass them as extra normal
 *                            parameters.
 */
nl.sara.webdav.Client = function(config, useHTTPS, port, defaultHeaders) {
  // First define private attributes
  Object.defineProperty(this, '_baseUrl', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_username', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_password', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_headers', {
    'value': {},
    'enumerable': false,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if ( config !== undefined ) {
    var host;
    if ( typeof( config ) !== 'string' ) {
      host = config['host'];
      useHTTPS = ( config['useHTTPS'] !== false );
      port = config['port'];
      defaultHeaders = config['defaultHeaders'];
      
      if ( config['username'] !== undefined ) {
        this._username = config['username'];
        if ( config['password'] !== undefined ) {
          this._password = config['password'];
        }else{
          this._password = '';
        }
      }
    }else{
      host = config;
    }
    
    if ( host !== undefined ) {
      // if the configuration item is a string, then we have to work in compatibility mode; first parameter is the host, second, the protocol, third the port and fourth aditional headers
      var protocol = (useHTTPS === true) ? 'https' : 'http';
      port = (port !== undefined) ? port : ((protocol === 'https') ? 443 : 80);
      this._baseUrl = protocol + '://' + host + ((((protocol === 'http') && (port === 80)) || ((protocol === 'https') && (port === 443))) ? '' : ':' + port);
    }
  }
  
  if (defaultHeaders !== undefined) {
    this._headers = defaultHeaders;
  }
};

/**#@+
 * Class constant
 */
nl.sara.webdav.Client.PROPNAME = 1;
nl.sara.webdav.Client.ALLPROP = 2;
nl.sara.webdav.Client.INFINITY = 'infinity';
nl.sara.webdav.Client.FAIL_ON_OVERWRITE = 3;
nl.sara.webdav.Client.TRUNCATE_ON_OVERWRITE = 4;
nl.sara.webdav.Client.SILENT_OVERWRITE = 5;
/**#@-*/

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Converts a path to the full url (i.e. appends the protocol and host part)
 *
 * @param   {String}  path  The path on the server
 * @returns {String}        The full url to the path
 */
nl.sara.webdav.Client.prototype.getUrl = function(path) {
  if (path.substring(0,1) !== '/') {
    path = '/' + path;
  }
  if (this._baseUrl !== null) {
    return this._baseUrl + path;
  }else{
    return path;
  }
};

/**
 * Perform a WebDAV PROPFIND request
 *
 * @param    {String}                         path             The path get a PROPFIND for
 * @param    {Function(status,body,headers)}  callback         Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         [depth=0]        Optional; Value for the 'depth' header, should be either '0', '1' or the class constant INFINITY. When omitted, '0' is used. See RFC 4918.
 * @param    {mixed}                          [props=ALLPROP]  Optional; The properties to fetch. Should be either one of the class constants PROPNAME or ALLPROP or an array with Property objects. When omitted, ALLPROP is assumed. See RFC 4918.
 * @param    {nl.sara.webdav.Property[]}      [include]        Optional; An array with Property objects used for the <include> element. This is only used for ALLPROP requests. When omitted, no <include> element is send. See RFC 4918.
 * @param    {Array}                          headers          Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                           The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.propfind = function(path, callback, depth, props, include, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('PROPFIND requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (!(typeof path === "string")) {
    throw new nl.sara.webdav.Exception('PROPFIND parameter; path should be a string', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // Check the depth header
  if (depth === undefined) { // We default depth to 0, not to infinity as this is not supported by all servers
    depth = 0;
  }
  var depthHeader = null;
  switch (depth) {
    case 0:
    case 1:
      depthHeader = depth;
      break;
    case nl.sara.webdav.Client.INFINITY:
      depthHeader = 'infinity';
      break;
    default:
      throw new nl.sara.webdav.Exception("Depth header should be '0', '1' or nl.sara.webdav.Client.INFINITY", nl.sara.webdav.Exception.WRONG_VALUE);
    break;
  }

  // Create the request XML
  if (props === undefined) {
    props = nl.sara.webdav.Client.ALLPROP;
  }
  var propsBody = document.implementation.createDocument("DAV:", "propfind", null);
  switch (props) { // Find out what the request is for
    case nl.sara.webdav.Client.PROPNAME: // User wants all property names
      propsBody.documentElement.appendChild(propsBody.createElementNS('DAV:', 'propname'));
      break;
    case nl.sara.webdav.Client.ALLPROP: // User wants all properties
      propsBody.documentElement.appendChild(propsBody.createElementNS('DAV:', 'allprop'));
      if (include !== undefined) { // There is content for the <DAV: include> tags, so parse it
        if (!(include instanceof Array)) {
          throw new nl.sara.webdav.Exception('Propfind parameter; include should be an array', nl.sara.webdav.Exception.WRONG_TYPE);
        }
        var includeBody = propsBody.createElementNS('DAV:', 'include');
        for (var i = 0; i < include.length; i++) {
          var item = include[i];
          if (!nl.sara.webdav.Ie.isIE && !(item instanceof nl.sara.webdav.Property)) {
            continue;
          }
          includeBody.appendChild(propsBody.createElementNS(item.namespace, item.tagname));
        }
        if (includeBody.hasChildNodes()) { // But only really add the <include> tag if there is valid content
          propsBody.documentElement.appendChild(includeBody);
        }
      }
      break;
    default: // The default is to expect an array with Property objects; the user wants the values of these properties
      if (!(props instanceof Array)) {
        throw new nl.sara.webdav.Exception('Props parameter should be nl.sara.webdav.Client.PROPNAME, nl.sara.webdav.Client.ALLPROP or an array with Property objects', nl.sara.webdav.Exception.WRONG_VALUE);
      }
      var propertyBody = propsBody.createElementNS('DAV:', 'prop');
      for (var i = 0; i < props.length; i++) { // Cycle through the array
        var prop = props[i];
        if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) {
          continue;
        }
        propertyBody.appendChild(propsBody.createElementNS(prop.namespace, prop.tagname));
      }
      if (!propertyBody.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it
        throw new nl.sara.webdav.Exception("Propfind parameter; if props is an array, it should be an array of Properties", nl.sara.webdav.Exception.WRONG_TYPE);
      }
      propsBody.documentElement.appendChild(propertyBody);
    break;
  }
  var serializer = new XMLSerializer();
  var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(propsBody);

  // And then send the request
  var ajax = this.getAjax("PROPFIND", url, callback, headers);
  ajax.setRequestHeader('Depth', depthHeader);
  ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"');
  ajax.send(body);

  return this;
};

/**
 * Perform a WebDAV PROPPATCH request
 *
 * @param    {String}                         path        The path do a PROPPATCH on
 * @param    {Function(status,body,headers)}  callback    Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {nl.sara.webdav.Property[]}      [setProps]  Optional; The properties to set. If not set, delProps should be set. Can be omitted with 'undefined'.
 * @param    {nl.sara.webdav.Property[]}      [delProps]  Optional; The properties to delete. If not set, setProps should be set.
 * @param    {Array}                          headers     Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                      The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.proppatch = function(path, callback, setProps, delProps, headers) {
  if ((path === undefined) || (callback === undefined) || ((setProps === undefined) && (delProps === undefined))) {
    throw new nl.sara.webdav.Exception('PROPPATCH requires the parameters path, callback and at least one of setProps or delProps', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (!(typeof path === "string") || ((setProps !== undefined) && !(setProps instanceof Array)) || ((delProps !== undefined) && !(delProps instanceof Array))) {
    throw new nl.sara.webdav.Exception('PROPPATCH parameter; path should be a string, setProps and delProps should be arrays', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // Create the request XML
  var propsBody = document.implementation.createDocument("DAV:", "propertyupdate", null);
  if (setProps !== undefined) {
    var props = propsBody.createElementNS('DAV:', 'prop');
    for (var i = 0; i < setProps.length; i++) { // Cycle through the array
      var prop = setProps[i];
      if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) {
        continue;
      }
      var element = propsBody.createElementNS(prop.namespace, prop.tagname);
      for (var j = 0; j < prop.xmlvalue.length; j++) {
        var nodeCopy = propsBody.importNode(prop.xmlvalue.item(j), true);
        element.appendChild(nodeCopy);
      }
      props.appendChild(element);
    }
    if (!props.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it
      throw new nl.sara.webdav.Exception("Proppatch parameter; setProps should be an array of Properties", nl.sara.webdav.Exception.WRONG_TYPE);
    }
    var set = propsBody.createElementNS('DAV:', 'set');
    set.appendChild(props);
    propsBody.documentElement.appendChild(set);
  }
  if (delProps !== undefined) {
    var props = propsBody.createElementNS('DAV:', 'prop');
    for (var i = 0; i < delProps.length; i++) { // Cycle through the array
      var prop = delProps[i];
      if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) {
        continue;
      }
      var element = propsBody.createElementNS(prop.namespace, prop.tagname);
      props.appendChild(element);
    }
    if (!props.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it
      throw new nl.sara.webdav.Exception("Proppatch parameter; delProps should be an array of Properties", nl.sara.webdav.Exception.WRONG_TYPE);
    }
    var remove = propsBody.createElementNS('DAV:', 'remove');
    remove.appendChild(props);
    propsBody.documentElement.appendChild(remove);
  }
  var serializer = new XMLSerializer();
  var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(propsBody);

  // And then send the request
  var ajax = this.getAjax("PROPPATCH", url, callback, headers);
  ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"');
  ajax.send(body);

  return this;
};

/**
 * Perform a WebDAV MKCOL request
 *
 * @param    {String}                         path                                              The path to perform MKCOL on
 * @param    {Function(status,body,headers)}  callback                                          Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         [body]                                            Optional; a body to include in the MKCOL request.
 * @param    {String}                         [contenttype='application/xml; charset="utf-8"']  Optional; the content type of the body (i.e. value for the Content-Type header). If omitted, but body is specified, then 'application/xml; charset="utf-8"' is assumed
 * @param    {Array}                          headers                                           Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                                                            The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.mkcol = function(path, callback, body, contenttype, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('MKCOL requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) {
    throw new nl.sara.webdav.Exception('MKCOL parameter; path and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = this.getAjax("MKCOL", url, callback, headers);
  if (body !== undefined) {
    if (contenttype !== undefined) {
      ajax.setRequestHeader('Content-Type', contenttype);
    }
    ajax.send(body);
  }else{
    ajax.send();
  }

  return this;
};

/**
 * Perform a WebDAV DELETE request
 *
 * Because 'delete' is an operator in JavaScript, I had to name this method
 * 'remove'. However, performs a regular DELETE request as described in
 * RFC 4918
 *
 * @param    {String}                         path      The path to perform DELETE on
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Array}                          headers   Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                    The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.remove = function(path, callback, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('DELETE requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (typeof path !== "string"){
    throw new nl.sara.webdav.Exception('DELETE parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = this.getAjax("DELETE", url, callback, headers);
  ajax.send();

  return this;
};

/**
 * Perform a WebDAV GET request
 *
 * @param    {String}                    path      The path to GET
 * @param    {Function(status,content)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Array}                     headers   Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}               The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.get = function(path, callback, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('GET requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (typeof path !== "string"){
    throw new nl.sara.webdav.Exception('GET parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("GET", url, callback, headers);
  ajax.send();

  return this;
};

/**
 * Perform a WebDAV HEAD request
 *
 * @param    {String}                         path      The path to perform HEAD on
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Array}                          headers   Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                    The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.head = function(path, callback, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('HEAD requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (typeof path !== "string"){
    throw new nl.sara.webdav.Exception('HEAD parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("HEAD", url, callback, headers);
  ajax.send();

  return this;
};

/**
 * Perform a WebDAV PUT request
 *
 * @param    {String}                         path           The path to perform PUT on
 * @param    {Function(status,body,headers)}  callback       Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         body           The content to include in the PUT request.
 * @param    {String}                         [contenttype]  Optional; the content type of the body (i.e. value for the Content-Type header).
 * @param    {Array}                          headers        Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                         The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.put = function(path, callback, body, contenttype, headers) {
  if ((path === undefined) || (callback === undefined) || (body === undefined)) {
    throw new nl.sara.webdav.Exception('PUT requires the parameters path, callback and body', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) {
    throw new nl.sara.webdav.Exception('PUT parameter; path and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("PUT", url, callback, headers);
  if (contenttype !== undefined) {
    ajax.setRequestHeader('Content-Type', contenttype);
  }
  ajax.send(body);

  return this;
};

/**
 * Perform a WebDAV OPTIONS request
 *
 * @param    {String}                         path                                               The path to perform OPTIONS on
 * @param    {Function(status,body,headers)}  callback                                           Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         [body]                                             Optional; a body to include in the OPTIONS request.
 * @param    {String}                         [contenttype='application/x-www-form-urlencoded']  Optional; the content type of the body (i.e. value for the Content-Type header). If omitted, but body is specified, then 'application/x-www-form-urlencoded' is assumed
 * @param    {Array}                          headers                                            Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                                                             The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.options = function(path, callback, body, contenttype, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('OPTIONS requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || ((body !== undefined) && (typeof body !== 'string')) || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) {
    throw new nl.sara.webdav.Exception('OPTIONS parameter; path, body and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("OPTIONS", url, callback, headers);
  if ( body !== undefined ) {
    if (contenttype !== undefined) {
      ajax.setRequestHeader('Content-Type', contenttype);
    }else{
      ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    ajax.send(body);
  }else{
    ajax.send();
  }

  return this;
};

/**
 * Perform a WebDAV POST request
 *
 * @param    {String}                         path                                               The path to perform POST on
 * @param    {Function(status,body,headers)}  callback                                           Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         [body]                                             Optional; a body to include in the POST request.
 * @param    {String}                         [contenttype='application/x-www-form-urlencoded']  Optional; the content type of the body (i.e. value for the Content-Type header). If omitted, but body is specified, then 'application/x-www-form-urlencoded' is assumed
 * @param    {Array}                          headers                                            Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                                                             The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.post = function(path, callback, body, contenttype, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('POST requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || ((body !== undefined) && (typeof body !== 'string')) || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) {
    throw new nl.sara.webdav.Exception('POST parameter; path, body and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("POST", url, callback, headers);
  if ( body !== undefined ) {
    if (contenttype !== undefined) {
      ajax.setRequestHeader('Content-Type', contenttype);
    }else{
      ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    ajax.send(body);
  }else{
    ajax.send();
  }

  return this;
};

/**
 * Perform a WebDAV COPY request
 *
 * @param    {String}                         path                              The path to perform COPY on
 * @param    {Function(status,body,headers)}  callback                          Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         destination                       The destination to copy to. Should be either a full URL or a path from the root on this server (i.e. it should start with a /)
 * @param    {Integer}                        [overwriteMode=SILENT_OVERWRITE]  Optional; Specify what to do when destination resource already exists. Should be either FAIL_ON_OVERWRITE or SILENT_OVERWRITE. The default is SILENT_OVERWRITE.
 * @param    {String}                         [depth]                           Optional; Should be '0' or 'infinity'. This is used in case of a collection; 0 means only copy the collection itself, infinity means copy also everything contained in the collection
 * @param    {Array}                          headers                           Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                                            The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.copy = function(path, callback, destination, overwriteMode, depth, headers) {
  if ((path === undefined) || (callback === undefined) || (destination === undefined)) {
    throw new nl.sara.webdav.Exception('COPY requires the parameters path, callback and destination', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || (typeof destination !== "string")){
    throw new nl.sara.webdav.Exception('COPY parameter; path and destination should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // If the destination starts with a / it is a absolute url on the same host, so prepare a complete URL
  if (destination.substr(0,1) === '/') {
    destination = this.getUrl(destination);
  } // Else I assume it is a complete URL already

  // And then send the request
  var ajax = this.getAjax("COPY", url, callback, headers);
  ajax.setRequestHeader('Destination', destination);
  if (depth !== undefined) {
    if ((depth !== 0) && (depth !== 'infinity')) {
      throw new nl.sara.webdav.Exception("COPY parameter; depth should be '0' or 'infinity'", nl.sara.webdav.Exception.WRONG_VALUE);
    }
    ajax.setRequestHeader('Depth', depth);
  }
  if (overwriteMode === nl.sara.webdav.Client.FAIL_ON_OVERWRITE) {
    ajax.setRequestHeader('Overwrite', 'F');
  }
  ajax.send();

  return this;
};

/**
 * Perform a WebDAV MOVE request
 *
 * @param    {String}                         path                              The path to perform MOVE on
 * @param    {Function(status,body,headers)}  callback                          Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         destination                       The destination to move to. Should be either a full URL or a path from the root on this server (i.e. it should start with a /)
 * @param    {Number}                         [overwriteMode=SILENT_OVERWRITE]  Optional; Specify what to do when destination resource already exists. Should be either FAIL_ON_OVERWRITE, TRUNCATE_ON_OVERWRITE or SILENT_OVERWRITE. The default is SILENT_OVERWRITE.
 * @param    {Array}                          headers                           Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                                            The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.move = function(path, callback, destination, overwriteMode, headers) {
  if ((path === undefined) || (callback === undefined) || (destination === undefined)) {
    throw new nl.sara.webdav.Exception('MOVE requires the parameters path, callback and destination', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || (typeof destination !== "string")){
    throw new nl.sara.webdav.Exception('MOVE parameter; path and destination should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // If the destination starts with a / it is a absolute url on the same host, so prepare a complete URL
  if (destination.substr(0,1) === '/') {
    destination = this.getUrl(destination);
  } // Else I assume it is a complete URL already

  // And then send the request
  var ajax = this.getAjax("MOVE", url, callback, headers);
  ajax.setRequestHeader('Destination', destination);
  if (overwriteMode === nl.sara.webdav.Client.FAIL_ON_OVERWRITE) {
    ajax.setRequestHeader('Overwrite', 'F');
  }else if (overwriteMode === nl.sara.webdav.Client.TRUNCATE_ON_OVERWRITE) {
    ajax.setRequestHeader('Overwrite', 'T');
  }
  ajax.send();

  return this;
};

/**
 * Perform a WebDAV LOCK request
 *
 * @param    {String}                         path      The path to perform LOCK on
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Document}                       body      Optional; The (XML DOM) document to parse and send as the request body
 * @param    {Array}                          headers   Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                    The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.lock = function(path, callback, body, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('LOCK requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || (!nl.sara.webdav.Ie.isIE && (body !== undefined) && !(body instanceof Document))) {
    throw new nl.sara.webdav.Exception('LOCK parameter; path should be a string, body should be an instance of Document', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // Parse the body
  var serializer = new XMLSerializer();
  var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(body);

  // And then send the request
  var ajax = null;
  ajax = this.getAjax("LOCK", url, callback, headers);
  if (body !== undefined) {
    ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"');
    ajax.send(body);
  }else{
    ajax.send();
  }

  return this;
};

/**
 * Perform a WebDAV UNLOCK request
 *
 * @param    {String}                         path       The path to perform UNLOCK on
 * @param    {Function(status,body,headers)}  callback   Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {String}                         lockToken  The lock token to unlock
 * @param    {Array}                          headers    Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                     The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.unlock = function(path, callback, lockToken, headers) {
  if ((path === undefined) || (callback === undefined) || (lockToken === undefined)) {
    throw new nl.sara.webdav.Exception('UNLOCK requires the parameters path, callback and lockToken', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ( (typeof path !== "string") || (typeof lockToken !== "string") ){
    throw new nl.sara.webdav.Exception('UNLOCK parameter; path and lockToken should be strings', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // And then send the request
  headers['Lock-Token'] = lockToken;
  var ajax = null;
  ajax = this.getAjax("UNLOCK", url, callback, headers);
  ajax.send();

  return this;
};

/**
 * Prepares a XMLHttpRequest object to be used for an AJAX request
 *
 * @static
 * @param    {String}                         method    The HTTP/webDAV method to use (e.g. GET, POST, PROPFIND)
 * @param    {String}                         url       The url to connect to
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Array}                          headers   Additional headers to set
 * @returns  {XMLHttpRequest}                           A prepared XMLHttpRequest
 */
nl.sara.webdav.Client.prototype.getAjax = function(method, url, callback, headers) {
  var /** @type XMLHttpRequest */ ajax = ( ( ( typeof Components !== 'undefined' ) && ( typeof Components.classes !== 'undefined' ) ) ? Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest) : new XMLHttpRequest());
  if ( this._username !== null ) {
    ajax.open( method, url, true, this._username, this._password );
  }else{
    ajax.open( method, url, true );
  }
  ajax.onreadystatechange = function() {
    nl.sara.webdav.Client.ajaxHandler( ajax, callback );
  };
  
  if (headers === undefined) {
    headers = {};
  }
  for (var header in this._headers) {
    if (headers[header] === undefined) {
      ajax.setRequestHeader(header, this._headers[header]);
    }
  }
  for (var header in headers) {
    ajax.setRequestHeader(header, headers[header]);
  }
  return ajax;
};

/**
 * AJAX request handler. Parses Multistatus (if available) and call a user specified callback function
 *
 * @static
 * @param    {XMLHttpRequest}                 ajax      The XMLHttpRequest object which performed the request
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @returns  {void}
 */
nl.sara.webdav.Client.ajaxHandler = function(ajax, callback) {
  if (ajax.readyState === 4){ //if request has completed
    var body = ajax.responseText;
    if (ajax.status === 207) {
      // Parse the response to a Multistatus object
      for (var counter = 0; counter < ajax.responseXML.childNodes.length; counter++) {
        if (nl.sara.webdav.Ie.getLocalName(ajax.responseXML.childNodes.item(counter)) === 'multistatus') {
          body = new nl.sara.webdav.Multistatus(ajax.responseXML.childNodes.item(counter));
          break;
        }
      }
    }
    callback(ajax.status, body, ajax.getAllResponseHeaders());
  }
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Codec is already defined, we have a namespace clash!
if (nl.sara.webdav.Codec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace name nl.sara.webdav.Codec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class A codec transcodes the xml to a custom Javascript object
 *
 * @param  {String}                    [namespace]  Optional; The namespace of the property to transcode
 * @param  {String}                    [tagname]    Optional; The tag name of the property to transcode
 * @param  {function(mixed[,xmlDoc])}  [toXML]      Optional; Function which should return a Document with the NodeList of the documentElement as the value of this property
 * @param  {function(NodeList)}        [fromXML]    Optional; Functions which should return a representation of xmlvalue
 * @property  {String}                    namespace  The namespace of the property to transcode
 * @property  {String}                    tagname    The tag name of the property to transcode
 * @property  {function(mixed[,xmlDoc])}  toXML      Function which should return a Document with the NodeList of the documentElement as the value of this property
 * @property  {function(NodeList)}        fromXML    Functions which should return a representation of xmlvalue
 */
nl.sara.webdav.Codec = function(namespace, tagname, toXML, fromXML) {
  // First define public attributes
  Object.defineProperty(this, 'namespace', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'tagname', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'toXML', {
    'value': undefined,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  // Second define public attributes
  Object.defineProperty(this, 'fromXML', {
    'value': undefined,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if (namespace !== undefined) {
    this.namespace = namespace;
  }
  if (tagname !== undefined) {
    this.tagname = tagname;
  }
  if (toXML !== undefined) {
    this.toXML = toXML;
  }
  if (fromXML !== undefined) {
    this.fromXML = fromXML;
  }
};

// End of file

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Multistatus is already defined, we have a namespace clash!
if (nl.sara.webdav.Multistatus !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Multistatus already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class WebDAV Multistatus response body
 *
 * @param  {Node}  [xmlNode]  Optional; the xmlNode describing the multistatus object (should be compliant with RFC 4918)
 * @property  {String}  responsedescription  The response description
 */
nl.sara.webdav.Multistatus = function(xmlNode) {
  // First define private attributes
  Object.defineProperty(this, '_responses', {
    'value': {},
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  // Second define public attributes
  Object.defineProperty(this, 'responsedescription', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if (typeof xmlNode !== 'undefined') {
    if ((xmlNode.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(xmlNode) !== 'multistatus')) {
      throw new nl.sara.webdav.Exception('Node is not of type DAV:multistatus', nl.sara.webdav.Exception.WRONG_XML);
    }
    var data = xmlNode.childNodes;
    for (var i = 0; i < data.length; i++) {
      var child = data.item(i);
      if ((child.namespaceURI === null) || (child.namespaceURI !== 'DAV:')) { // Skip if not from the right namespace
        continue;
      }
      switch (nl.sara.webdav.Ie.getLocalName(child)) {
        case 'responsedescription': // responsedescription is always CDATA, so just take the text
          this.responsedescription = child.childNodes.item(0).nodeValue;
          break;
        case 'response': // response node should be parsed further
          var response = new nl.sara.webdav.Response(child);
          var responseChilds = child.childNodes;
          var hrefs = [];
          for (var j = 0; j < responseChilds.length; j++) {
            var responseChild = responseChilds.item(j);
            if ((nl.sara.webdav.Ie.getLocalName(responseChild) === 'href') && (responseChild.namespaceURI !== null) && (responseChild.namespaceURI === 'DAV:')) { // For each HREF element we create a separate response object
              hrefs.push(responseChild.childNodes.item(0).nodeValue);
            }
          }
          if (hrefs.length > 1) { // Multiple HREFs = start copying the response (this makes sure it is not parsed over and over again). No deep copying needed; there can't be a propstat
            for (var k = 0; k < hrefs.length; k++) {
              var copyResponse = new nl.sara.webdav.Response();
              copyResponse.href = hrefs[k];
              copyResponse.status = response.status;
              copyResponse.error = response.error;
              copyResponse.responsedescription = response.responsedescription;
              copyResponse.location = response.location;
              this.addResponse(copyResponse);
            }
          }else{
            this.addResponse(response);
          }
        break;
      }
    }
  }
};

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Adds a Response
 *
 * @param    {nl.sara.webdav.Response}     response  The response
 * @returns  {nl.sara.webdav.Multistatus}            The multistatus itself for chaining methods
 */
nl.sara.webdav.Multistatus.prototype.addResponse = function(response) {
  if (!nl.sara.webdav.Ie.isIE && !(response instanceof nl.sara.webdav.Response)) {
    throw new nl.sara.webdav.Exception('Response should be instance of Response', nl.sara.webdav.Exception.WRONG_TYPE);
  }
  var name = response.href;
  this._responses[name] = response;
  return this;
};

/**
 * Gets a Response
 *
 * @param    {String}    name           The name of the response to get
 * @returns  {nl.sara.webdav.Response}  The value of the WebDAV property or undefined if the WebDAV property doesn't exist
 */
nl.sara.webdav.Multistatus.prototype.getResponse = function(name) {
  return this._responses[name];
};

/**
 * Gets the response names
 *
 * @returns  {String[]}  The names of the different responses
 */
nl.sara.webdav.Multistatus.prototype.getResponseNames = function() {
  return Object.keys(this._responses);
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Property is already defined, we have a namespace clash!
if (nl.sara.webdav.Property !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace name nl.sara.webdav.Property already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class a WebDAV property
 *
 * @param  {Node}      [xmlNode]              Optional; The xmlNode describing the propstat object (should be compliant with RFC 4918)
 * @param  {Number}    [status]               Optional; The (HTTP) status code
 * @param  {String}    [responsedescription]  Optional; The response description
 * @param  {String[]}  [errors]               Optional; An array of errors
 * @property  {String}    namespace            The namespace
 * @property  {String}    tagname              The tag name
 * @property  {NodeList}  xmlvalue             A NodeList with the value of this property
 * @property  {Number}    status               The (HTTP) status code
 * @property  {String}    responsedescription  The response description
 */
nl.sara.webdav.Property = function(xmlNode, status, responsedescription, errors) {
  // First define private attributes
  Object.defineProperty(this, '_xmlvalue', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_errors', {
    'value': [],
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  // Second define public attributes
  Object.defineProperty(this, 'namespace', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'tagname', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'status', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'responsedescription', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if ((typeof xmlNode !== 'undefined') && (xmlNode.nodeType === 1)) {
    this.namespace = xmlNode.namespaceURI;
    this.tagname = nl.sara.webdav.Ie.getLocalName(xmlNode);
    this.xmlvalue = xmlNode.childNodes;
  }
  if (status !== undefined) {
    this.status = status;
  }
  if (responsedescription !== undefined) {
    this.responsedescription = responsedescription;
  }
  if (errors instanceof Array) {
    for (var i = 0; i < errors.length; i++) {
      this.addError(errors[i]);
    }
  }
};

//######################### DEFINE PUBLIC ATTRIBUTES ###########################
(function() {
  // This creates a (private) static variable. It will contain all codecs
  var codecNamespaces = {};

  Object.defineProperty(nl.sara.webdav.Property.prototype, 'xmlvalue', {
    'set': function(value) {
      if (value === null) {
        this._xmlvalue = null;
        return;
      }
      if (!nl.sara.webdav.Ie.isIE && !(value instanceof NodeList)) {
        throw new nl.sara.webdav.Exception('xmlvalue must be an instance of NodeList', nl.sara.webdav.Exception.WRONG_TYPE);
      }
      this._xmlvalue = value;
    },
    'get': function() {
      return this._xmlvalue;
    }
  });

//########################## DEFINE PUBLIC METHODS #############################
  /**
   * Adds functions to encode or decode properties
   *
   * This allows exact control in how Property.xmlvalue is parsed when
   * getParsedValue() is called or how it is rebuild when
   * setValueAndRebuildXml() is called. You can specify two functions: 'fromXML'
   * and 'toXML'. These should be complementary. That is, toXML should be able
   * to create a NodeList based on the output of fromXML. For example:
   * A == toXML(fromXML(A)) &&
   * B == fromXML(toXML(B))
   *
   * @param    {nl.sara.webdav.Codec}  codec  The codec to add
   * @returns  {void}
   */
  nl.sara.webdav.Property.addCodec = function(codec) {
    if (typeof codec.namespace !== 'string') {
      throw new nl.sara.webdav.Exception('addCodec: codec.namespace must be a String', nl.sara.webdav.Exception.WRONG_TYPE);
    }
    if (typeof codec.tagname !== 'string') {
      throw new nl.sara.webdav.Exception('addCodec: codec.tagname must be a String', nl.sara.webdav.Exception.WRONG_TYPE);
    }
    if (codecNamespaces[codec.namespace] === undefined) {
      codecNamespaces[codec.namespace] = {};
    }
    codecNamespaces[codec.namespace][codec.tagname] = {
      'fromXML': (codec.fromXML ? codec.fromXML : undefined),
      'toXML': (codec.toXML ? codec.toXML : undefined)
    };
  };

  /**
   * Sets a new value and rebuilds xmlvalue
   *
   * If a codec for this property is specified, it will use this codec to
   * rebuild xmlvallue. Else it will attempt to create one CDATA element with
   * the string value of whatever was fiven as parameter.
   *
   * @param   {mixed}  value  The object to base the xmlvalue on
   * @return  {void}
   */
  nl.sara.webdav.Property.prototype.setValueAndRebuildXml = function(value) {
    // Call codec to automatically create correct 'xmlvalue'
    var xmlDoc = document.implementation.createDocument(this.namespace, this.tagname, null);
    if ((codecNamespaces[this.namespace] === undefined) ||
        (codecNamespaces[this.namespace][this.tagname] === undefined) ||
        (codecNamespaces[this.namespace][this.tagname]['toXML'] === undefined)) {
      // No 'toXML' function set, so create a NodeList with just one CDATA node
      if (value !== null) { // If the value is NULL, then we should add anything to the NodeList
        var cdata = xmlDoc.createCDATASection(value);
        xmlDoc.documentElement.appendChild(cdata);
      }
      this._xmlvalue = xmlDoc.documentElement.childNodes;
    }else{
      this._xmlvalue = codecNamespaces[this.namespace][this.tagname]['toXML'](value, xmlDoc).documentElement.childNodes;
    }
  };

  /**
   * Parses the xmlvalue
   *
   * If a codec for this property is specified, it will use this codec to parse
   * the XML nodes. Else it will attempt to parse it as text or CDATA elements.
   *
   * @return  {mixed}  If a codec is specified, the return type depends on that
   * codec. If no codec is specified and at least one node in xmlvalue is not a
   * text or CDATA node, it will return undefined. If xmlvalue is empty, it will
   * return null.
   */
  nl.sara.webdav.Property.prototype.getParsedValue = function() {
    // Call codec to automatically create correct 'value'
    if (this._xmlvalue.length > 0) {
      if ((codecNamespaces[this.namespace] === undefined) ||
          (codecNamespaces[this.namespace][this.tagname] === undefined) ||
          (codecNamespaces[this.namespace][this.tagname]['fromXML'] === undefined)) {
        // No 'fromXML' function set, so try to create a text value based on TextNodes and CDATA nodes. If other nodes are present, set 'value' to null
        var parsedValue = '';
        for (var i = 0; i < this._xmlvalue.length; i++) {
          var node = this._xmlvalue.item(i);
          if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored
            parsedValue += node.nodeValue;
          }else{ // If even one of the nodes is not text or CDATA, then we don't parse a text value at all
            parsedValue = undefined;
            break;
          }
        }
        return parsedValue;
      }else{
        return codecNamespaces[this.namespace][this.tagname]['fromXML'](this._xmlvalue);
      }
    }
    return null;
  };
})(); // Ends the static scope

/**
* Adds an error to this property
*
* @param    {Node}      error  The Node which represents the error
* @returns  {Property}         Itself for chaining multiple methods
*/
nl.sara.webdav.Property.prototype.addError = function(error) {
  if (!nl.sara.webdav.Ie.isIE && !(error instanceof Node)) {
    throw new nl.sara.webdav.Exception('Error must be an instance of Node', nl.sara.webdav.Exception.WRONG_TYPE);
  }
  this._errors.push(error);
  return this;
};

/**
* Returns all errors
*
* @returns {array} An array of Node representing the error
*/
nl.sara.webdav.Property.prototype.getErrors = function() {
  return this._errors;
};

/**
 * Overloads the default toString() method so it returns the value of this property
 *
 * @returns  {String}  A string representation of this property
 */
nl.sara.webdav.Property.prototype.toString = function() {
  return this.getParsedValue();
};

// End of Property

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Response is already defined, we have a namespace clash!
if (nl.sara.webdav.Response !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Response already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class a WebDAV response
 *
 * @param  {Node}  [xmlNode]  Optional; the xmlNode describing the response object (should be compliant with RFC 4918)
 * @property  {String}  href                 The path of the resource
 * @property  {String}  status               The (HTTP) status code
 * @property  {String}  error                The error text
 * @property  {String}  responsedescription  The response description
 * @property  {String}  location             In case of a 3XX response, the value that would normally be in the 'Location' header
 */
nl.sara.webdav.Response = function(xmlNode) {
  // First define private attributes
  Object.defineProperty(this, '_namespaces', {
    'value': {},
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  // Second define public attributes
  Object.defineProperty(this, 'href', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'status', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'error', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'responsedescription', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'location', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if (typeof xmlNode !== 'undefined') {
    if ((xmlNode.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(xmlNode) !== 'response')) {
      throw new nl.sara.webdav.Exception('Node is not of type DAV:response', nl.sara.webdav.Exception.WRONG_XML);
    }
    var data = xmlNode.childNodes;
    for (var i = 0; i < data.length; i++) {
      var child = data.item(i);
      if ((child.namespaceURI === null) || (child.namespaceURI !== 'DAV:')) { // Skip if not from the right namespace
        continue;
      }
      switch (nl.sara.webdav.Ie.getLocalName(child)) {
        case 'href':
        case 'status':
        case 'error':
        case 'responsedescription':
          // always CDATA, so just take the text
          this[nl.sara.webdav.Ie.getLocalName(child)] = child.childNodes.item(0).nodeValue;
          break;
        case 'location':
          this[nl.sara.webdav.Ie.getLocalName(child)] = child.childNodes.item(0).childNodes.item(0).nodeValue;
          break;
        case 'propstat': // propstat node should be parsed further
          var propstatChilds = child.childNodes;
          // First find status, error, responsedescription and props (as Node objects, not yet as Property objects)
          var status = null;
          var errors = [];
          var responsedescription = null;
          var props = [];
          for (var j = 0; j < propstatChilds.length; j++) { // Parse the child nodes of the propstat element
            var propstatChild = propstatChilds.item(j);
            if ((propstatChild.nodeType !== 1) || (propstatChild.namespaceURI !== 'DAV:')) {
              continue;
            }
            switch (nl.sara.webdav.Ie.getLocalName(propstatChild)) {
              case 'prop':
                for (var k = 0; k < propstatChild.childNodes.length; k++) {
                  props.push(propstatChild.childNodes.item(k));
                }
                break;
              case 'error':
                for (k = 0; k < propstatChild.childNodes.length; k++) {
                  errors.push(propstatChild.childNodes.item(k));
                }
                break;
                break;
              case 'status': // always CDATA, so just take the text
                status = propstatChild.childNodes.item(0).nodeValue;
                status = parseInt(status.substr(status.indexOf(' ') + 1, 3));
                break;
              case 'responsedescription': // always CDATA, so just take the text
                responsedescription = propstatChild.childNodes.item(0).nodeValue;
              break;
            }
          }

          // Then create and add a new property for each element found in DAV:prop
          for (j = 0; j < props.length; j++) {
            if (props[j].nodeType === 1) {
              var property = new nl.sara.webdav.Property(props[j], status, responsedescription, errors);
              this.addProperty(property);
            }
          }
        break;
      }
    }
  }
};

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Adds a WebDAV property
 *
 * @param    {nl.sara.webdav.Property}  property  The property
 * @returns  {nl.sara.webdav.Response}            The response itself for chaining methods
 */
nl.sara.webdav.Response.prototype.addProperty = function(property) {
  if (!nl.sara.webdav.Ie.isIE && !(property instanceof nl.sara.webdav.Property)) {
    throw new nl.sara.webdav.Exception('Response property should be instance of Property', nl.sara.webdav.Exception.WRONG_TYPE);
  }
  var namespace = property.namespace;
  if (typeof namespace !== 'string') {
    namespace = '';
  }
  if (this._namespaces[namespace] === undefined) {
    this._namespaces[namespace] = {};
  }

  this._namespaces[namespace][property.tagname] = property;
  return this;
};

/**
 * Gets a WebDAV property
 *
 * @param    {String}                   namespace  The namespace of the WebDAV property
 * @param    {String}                   prop       The WebDAV property to get
 * @returns  {nl.sara.webdav.Property}             The value of the WebDAV property or undefined if the WebDAV property doesn't exist
 */
nl.sara.webdav.Response.prototype.getProperty = function(namespace, prop) {
  if ((this._namespaces[namespace] === undefined) || (this._namespaces[namespace][prop] === undefined)) {
    return undefined;
  }
  return this._namespaces[namespace][prop];
};

/**
 * Gets the namespace names
 *
 * @returns  {String[]}  The names of the different namespaces
 */
nl.sara.webdav.Response.prototype.getNamespaceNames = function() {
  return Object.keys(this._namespaces);
};

/**
 * Gets the property names of a namespace
 *
 * @param    {String}    namespace  The namespace of the WebDAV property
 * @returns  {String[]}             The names of the different properties of a namespace
 */
nl.sara.webdav.Response.prototype.getPropertyNames = function(namespace) {
  if (this._namespaces[namespace] === undefined) {
    return new Array();
  }
  return Object.keys(this._namespaces[namespace]);
};

// End of library

/*
 * Copyright ©2014 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.Principal_collection_setCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.Principal_collection_setCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.Principal_collection_setCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: principal-collection-setCodec to an array with the uri's object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.Principal_collection_setCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.Principal_collection_setCodec.namespace = 'DAV:';
nl.sara.webdav.codec.Principal_collection_setCodec.tagname = 'principal-collection-set';

nl.sara.webdav.codec.Principal_collection_setCodec.fromXML = function(nodelist) {
  var collections = [];
  for ( var key = 0; key < nodelist.length; key++ ) {
    var node = nodelist.item( key );
    if ( ( node.nodeType === 1 ) && ( node.localName === 'href' ) && ( node.namespaceURI === 'DAV:' ) ) { // Only extract data from DAV: href nodes
      var href = '';
      for ( var subkey = 0; subkey < node.childNodes.length; subkey++ ) {
        var childNode = node.childNodes.item( subkey );
        if ( ( childNode.nodeType === 3 ) || ( childNode.nodeType === 4 ) ) { // Make sure text and CDATA content is stored
          href += childNode.nodeValue;
        }
      }
      collections.push( href );
    }
  }
  return collections;
};

nl.sara.webdav.codec.Principal_collection_setCodec.toXML = function(value, xmlDoc){
  for ( var key in value ) {
    var href = xmlDoc.createElementNS( 'DAV:', 'href' );
    href.appendChild( xmlDoc.createCDATASection( value[ key ] ) );
    xmlDoc.documentElement.appendChild( href );
  }
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.Principal_collection_setCodec);

// End of file
/*
 * Copyright ©2014 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.Inherited_acl_setCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.Inherited_acl_setCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.Inherited_acl_setCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: inherited-acl-set to an array with the uri's object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.Inherited_acl_setCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.Inherited_acl_setCodec.namespace = 'DAV:';
nl.sara.webdav.codec.Inherited_acl_setCodec.tagname = 'inherited-acl-set';

nl.sara.webdav.codec.Inherited_acl_setCodec.fromXML = nl.sara.webdav.codec.Principal_collection_setCodec.fromXML;
nl.sara.webdav.codec.Inherited_acl_setCodec.toXML = nl.sara.webdav.codec.Principal_collection_setCodec.toXML;

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.Inherited_acl_setCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Ace is already defined, we have a namespace clash!
if (nl.sara.webdav.Ace !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Ace already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class WebDAV property
 *
 * @param  {Node}  [xmlNode]  Optional; the xmlNode describing the ace object (should be compliant with RFC 3744)
 * @property  {mixed}    principal        The principal. Is one of the class constants ALL, AUTHENTICATED, UNAUTHENTICATED or SELF or a String with the path to the principal or a property. See RFC 3744 for more information on this.
 * @property  {Boolean}  invertprincipal  Whether to invert the principal; true means 'all principals except the one specified'. Default is false.
 * @property  {Boolean}  isprotected      Whether this ACE is protected. Default is false.
 * @property  {Number}   grantdeny        Grant or deny ACE? Is one of the class constants GRANT or DENY.
 * @property  {mixed}    inherited        False if the ACE is not inherited, else a String with the path to the parent collection from which this ACE is inherited.
 */
nl.sara.webdav.Ace = function(xmlNode) {
  // First define private attributes
  Object.defineProperty(this, '_namespaces', {
    'value': {},
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_principal', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_invertprincipal', {
    'value': false,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_grantdeny', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_isprotected', {
    'value': false,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, '_inherited', {
    'value': false,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  function parsePrincipal(object, child) {
    if (!nl.sara.webdav.Ie.isIE && !(child instanceof Node)) {
      throw new nl.sara.webdav.Exception('Principal XML node not recognized!', nl.sara.webdav.Exception.WRONG_XML);
    }
    for (var j = 0; j < child.childNodes.length; j++) {
      var principal = child.childNodes.item(j);
      if ((principal.nodeType !== 1) || (principal.namespaceURI === null) || (principal.namespaceURI !== 'DAV:')) { // Skip if not from the right namespace
        continue;
      }
      switch (nl.sara.webdav.Ie.getLocalName(principal)) {
        case 'href':
          object.principal = principal.childNodes.item(0).nodeValue;
          break;
        case 'all':
          object.principal = nl.sara.webdav.Ace.ALL;
          break;
        case 'authenticated':
          object.principal = nl.sara.webdav.Ace.AUTHENTICATED;
          break;
        case 'unauthenticated':
          object.principal = nl.sara.webdav.Ace.UNAUTHENTICATED;
          break;
        case 'property':
          for (var k = 0; k < principal.childNodes.length; k++) {
            var element = principal.childNodes.item(k);
            if (element.nodeType !== 1) {
              continue;
            }
            var prop = new nl.sara.webdav.Property(element);
            object.principal = prop;
            break;
          }
          break;
        case 'self':
          object.principal = nl.sara.webdav.Ace.SELF;
          break;
        default:
          throw new nl.sara.webdav.Exception('Principal XML Node contains illegal child node: ' + nl.sara.webdav.Ie.getLocalName(principal), nl.sara.webdav.Exception.WRONG_XML);
        break;
      }
    }
  }

  function parsePrivileges(object, privilegeList) {
    for (var i = 0; i < privilegeList.length; i++) {
      var privilege = privilegeList.item(i);
      if ( ( privilege.nodeType === 1 ) &&
           ( privilege.namespaceURI === 'DAV:' ) &&
           ( nl.sara.webdav.Ie.getLocalName( privilege ) === 'privilege' ) ) {
        object.addPrivilege( new nl.sara.webdav.Privilege( privilege.childNodes[0] ) );
      }
    }
  }

  // Parse the XML
  if (typeof xmlNode !== 'undefined') {
    if ((xmlNode.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(xmlNode) !== 'ace')) {
      throw new nl.sara.webdav.Exception('Node is not of type DAV:ace', nl.sara.webdav.Exception.WRONG_XML);
    }
    var data = xmlNode.childNodes;
    for (var i = 0; i < data.length; i++) {
      var child = data.item(i);
      if ((child.namespaceURI === null) || (child.namespaceURI !== 'DAV:')) { // Skip if not from the right namespace
        continue;
      }
      switch (nl.sara.webdav.Ie.getLocalName(child)) {
        case 'principal':
          this.invertprincipal = false;
          parsePrincipal(this, child);
          break;
        case 'invert':
          this.invertprincipal = true;
          for (var j = 0; j < child.childNodes.length; j++) {
            var element = child.childNodes.item(j);
            if ((element.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(element) !== 'principal')) {
              continue;
            }
            parsePrincipal(this, element);
          }
          break;
        case 'grant':
          this.grantdeny = nl.sara.webdav.Ace.GRANT;
          parsePrivileges(this, child.childNodes);
          break;
        case 'deny':
          this.grantdeny = nl.sara.webdav.Ace.DENY;
          parsePrivileges(this, child.childNodes);
          break;
        case 'protected':
          this.isprotected = true;
          break;
        case 'inherited':
          for (var j = 0; j < child.childNodes.length; j++) {
            var element = child.childNodes.item(j);
            if ((element.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(element) !== 'href')) {
              continue;
            }
            this.inherited = child.childNodes.item(j).childNodes.item(0).nodeValue;
          }
        break;
      }
    }
  }
};

/**#@+
 * Class constant
 */
nl.sara.webdav.Ace.GRANT = 1;
nl.sara.webdav.Ace.DENY = 2;
nl.sara.webdav.Ace.ALL = 3;
nl.sara.webdav.Ace.AUTHENTICATED = 4;
nl.sara.webdav.Ace.UNAUTHENTICATED = 5;
nl.sara.webdav.Ace.SELF = 6;
/**#@-*/

//######################### DEFINE PUBLIC ATTRIBUTES ###########################
Object.defineProperty(nl.sara.webdav.Ace.prototype, 'principal', {
  'set': function(value) {
    switch (value) {
      case nl.sara.webdav.Ace.ALL:
      case nl.sara.webdav.Ace.AUTHENTICATED:
      case nl.sara.webdav.Ace.UNAUTHENTICATED:
      case nl.sara.webdav.Ace.SELF:
        break;
      default: // If it isn't one of the constants, it should be either a Property object or a string/URL
        if (!nl.sara.webdav.Ie.isIE && !(value instanceof nl.sara.webdav.Property)) {
          value = String(value);
        }
      break;
    }
    this._principal = value;
  },
  'get': function() {
    return this._principal;
  }
});

Object.defineProperty(nl.sara.webdav.Ace.prototype, 'invertprincipal', {
  'set': function(value) {
    this._invertprincipal = Boolean(value);
  },
  'get': function() {
    return this._invertprincipal;
  }
});

Object.defineProperty(nl.sara.webdav.Ace.prototype, 'isprotected', {
  'set': function(value) {
    this._isprotected = Boolean(value);
  },
  'get': function() {
    return this._isprotected;
  }
});

Object.defineProperty(nl.sara.webdav.Ace.prototype, 'grantdeny', {
  'set': function(value) {
    if ((value !== nl.sara.webdav.Ace.GRANT) && (value !== nl.sara.webdav.Ace.DENY)) {
      throw new nl.sara.webdav.Exception('grantdeny must be either nl.sara.webdav.Ace.GRANT or nl.sara.webdav.Ace.DENY', nl.sara.webdav.Exception.WRONG_VALUE);
    }
    this._grantdeny = value;
  },
  'get': function() {
    return this._grantdeny;
  }
});

Object.defineProperty(nl.sara.webdav.Ace.prototype, 'inherited', {
  'set': function(value) {
    if (Boolean(value)) {
      this._inherited = String(value);
    }else{
      this._inherited = false;
    }
  },
  'get': function() {
    return this._inherited;
  }
});

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Adds a WebDAV privilege
 *
 * @param    {nl.sara.webdav.Privilege}  privilege  The privilege to add
 * @returns  {nl.sara.webdav.Ace}                   The ace itself for chaining methods
 */
nl.sara.webdav.Ace.prototype.addPrivilege = function(privilege) {
  if (!nl.sara.webdav.Ie.isIE && !(privilege instanceof nl.sara.webdav.Privilege)) {
    throw new nl.sara.webdav.Exception('Privilege should be instance of Privilege', nl.sara.webdav.Exception.WRONG_TYPE);
  }
  var namespace = privilege.namespace;
  if (namespace) {
    if (this._namespaces[namespace] === undefined) {
      this._namespaces[namespace] = {};
    }
  }else{
    throw new nl.sara.webdav.Exception('Privilege should have a namespace', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  this._namespaces[namespace][privilege.tagname] = privilege;
  return this;
};

/**
 * Gets a WebDAV privilege
 *
 * @param    {String}                    namespace  The namespace of the privilege
 * @param    {String}                    privilege  The privilege to get
 * @returns  {nl.sara.webdav.Privilege}             The value of the privilege or undefined if the privilege doesn't exist
 */
nl.sara.webdav.Ace.prototype.getPrivilege = function(namespace, privilege) {
  if ((this._namespaces[namespace] === undefined) || (this._namespaces[namespace][privilege] === undefined)) {
    return undefined;
  }
  return this._namespaces[namespace][privilege];
};

/**
 * Gets the namespace names
 *
 * @returns  {String[]}  The names of the different namespaces
 */
nl.sara.webdav.Ace.prototype.getNamespaceNames = function() {
  return Object.keys(this._namespaces);
};

/**
 * Gets the privilege names of a namespace
 *
 * @param    {String}    namespace  The namespace of the privilege
 * @returns  {String[]}             The names of the different privilege of a namespace
 */
nl.sara.webdav.Ace.prototype.getPrivilegeNames = function(namespace) {
  if (this._namespaces[namespace] === undefined) {
    return new Array();
  }
  return Object.keys(this._namespaces[namespace]);
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Acl is already defined, we have a namespace clash!
if (nl.sara.webdav.Acl !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Acl already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Access Control List
 *
 * @param  {Node}  [xmlNode]  Optional; the xmlNode describing the acl object (should be compliant with RFC 3744)
 */
nl.sara.webdav.Acl = function(xmlNode) {
  // First define private attributes
  Object.defineProperty(this, '_aces', {
    'value': [],
    'enumerable': false,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  // Parse the XML
  if (typeof xmlNode !== 'undefined') {
    if ((xmlNode.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(xmlNode) !== 'acl')) {
      throw new nl.sara.webdav.Exception('Node is not of type DAV:acl', nl.sara.webdav.Exception.WRONG_XML);
    }
    for (var i = 0; i < xmlNode.childNodes.length; i++) {
      var child = xmlNode.childNodes.item(i);
      if ((child.namespaceURI === null) || (child.namespaceURI !== 'DAV:') || (nl.sara.webdav.Ie.getLocalName(child) !== 'ace')) { // Skip if not the right element
        continue;
      }
      this.addAce(new nl.sara.webdav.Ace(child));
    }
  }
};

//########################## DEFINE PUBLIC METHODS #############################
/**
 * Adds an ACE
 *
 * @param    {nl.sara.webdav.Ace}  ace       The ACE to add
 * @param    {Number}              position  Optional; The position to add this ACE. If the position is lower than 1, 0 is assumed, of the position is higher than the current length of the ACL or not specified, the ACE is appended to the end.
 * @returns  {nl.sara.webdav.Acl}            The ACL itself for chaining methods
 */
nl.sara.webdav.Acl.prototype.addAce = function(ace, position) {
  if (!nl.sara.webdav.Ie.isIE && !(ace instanceof nl.sara.webdav.Ace)) {
    throw new nl.sara.webdav.Exception('Ace should be instance of Ace', nl.sara.webdav.Exception.WRONG_TYPE);
  }
  if ((position === undefined) || (position > (this._aces.length - 1))) {
    this._aces.push(ace);
  }else{
    position = Number(position);
    if (position < 1) {
      this._aces.unshift(ace);
    }else{
      this._aces.splice(position, 0, ace);
    }
  }
  return this;
};

/**
 * Gets the ACL as an array
 *
 * @returns  {nl.sara.webdav.Ace[]}  An array of the ACE's in this ACL
 */
nl.sara.webdav.Acl.prototype.getAces = function() {
  return this._aces;
};

/**
 * Gets one ACE from a certain position
 *
 * @param    {Number}              position  The position of the ACE
 * @returns  {nl.sara.webdav.Ace}            The ACE
 */
nl.sara.webdav.Acl.prototype.getAce = function(position) {
  position = Number(position);
  if ((position < 0) || (position >= this.getLength())) {
    throw new nl.sara.webdav.Exception('No ACE found on position ' + position, nl.sara.webdav.Exception.UNEXISTING_PROPERTY);
  }
  return this._aces[position];
};

/**
 * Gets the length of the ACL
 *
 * @returns  {Number}  The length of the ACL
 */
nl.sara.webdav.Acl.prototype.getLength = function() {
  return this._aces.length;
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.AclCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.AclCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.AclCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: acl to an nl.sara.webdav.Acl object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.AclCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.AclCodec.namespace = 'DAV:';
nl.sara.webdav.codec.AclCodec.tagname = 'acl';

nl.sara.webdav.codec.AclCodec.fromXML = function(nodelist) {
  // The constructor of Acl will parse a DAV: acl node itself, so just make sure this nodelist is part of a DAV: acl node
  var xmlDoc = document.implementation.createDocument("DAV:", "acl", null);
  var acl = xmlDoc.documentElement;
  for (var i = 0; i < nodelist.length; i++) {
    acl.appendChild( xmlDoc.importNode( nodelist.item( i ), true ) );
  }
  return new nl.sara.webdav.Acl(acl);
};

nl.sara.webdav.codec.AclCodec.toXML = function(acl, xmlDoc){
  var aclLength = acl.getLength();
  for (var i = 0; i < aclLength; i++) { // Loop over the ACE's in this ACL
    var ace = acl.getAce(i);
    var aceBody = xmlDoc.createElementNS('DAV:', 'ace');

    // First create a principal node
    var principal = xmlDoc.createElementNS('DAV:', 'principal');
    var princVal = ace.principal;
    switch (princVal) {
      case nl.sara.webdav.Ace.ALL:
        principal.appendChild(xmlDoc.createElementNS('DAV:', 'all'));
        break;
      case nl.sara.webdav.Ace.AUTHENTICATED:
        principal.appendChild(xmlDoc.createElementNS('DAV:', 'authenticated'));
        break;
      case nl.sara.webdav.Ace.UNAUTHENTICATED:
        principal.appendChild(xmlDoc.createElementNS('DAV:', 'unauthenticated'));
        break;
      case nl.sara.webdav.Ace.SELF:
        principal.appendChild(xmlDoc.createElementNS('DAV:', 'self'));
        break;
      default: // If it isn't one of the constants, it should be either a Property object or a string/URL
        if (typeof princVal === 'string') { // It is a string; the URL of the principal
          var href = xmlDoc.createElementNS('DAV:', 'href');
          href.appendChild(xmlDoc.createCDATASection(princVal));
          principal.appendChild(href);
        }else{ // And else it is a property
          var property = xmlDoc.createElementNS('DAV:', 'property');
          var prop = xmlDoc.createElementNS(princVal.namespace, princVal.tagname);
          if (princVal.xmlvalue !== null) {
            for (var j = 0; j < princVal.xmlvalue.length; j++) {
              var nodeCopy = xmlDoc.importNode(princVal.xmlValue.item(j), true);
              prop.appendChild(nodeCopy);
            }
          }
          property.appendChild(prop);
          principal.appendChild(property);
        }
      break;
    }

    // If the principal should be inverted, put it in an 'invert' element
    if (ace.invertprincipal) {
      var invert = xmlDoc.createElementNS('DAV:', 'invert');
      invert.appendChild(principal);
      aceBody.appendChild(invert);
    }else{
      aceBody.appendChild(principal);
    }

    // Then prepare the privileges
    // grant or deny?
    var privilegeParent = null;
    if (ace.grantdeny === nl.sara.webdav.Ace.DENY) {
      privilegeParent = xmlDoc.createElementNS('DAV:', 'deny');
    }else if (ace.grantdeny === nl.sara.webdav.Ace.GRANT) {
      privilegeParent = xmlDoc.createElementNS('DAV:', 'grant');
    }else{
      throw new nl.sara.webdav.Exception('\'grantdeny\' property not set on one of the ACE\'s in this ACL', nl.sara.webdav.Exception.WRONG_VALUE);
    }
    var namespaces = ace.getNamespaceNames();
    for (j = 0; j < namespaces.length; j++) { // loop through namespaces
      var namespace = namespaces[j];
      var privileges = ace.getPrivilegeNames(namespace);
      for (var k = 0; k < privileges.length; k++) { // loop through each privilege in this namespace
        var privilege = ace.getPrivilege(namespace, privileges[k]);
        var privilegeElement = xmlDoc.createElementNS('DAV:', 'privilege');
        var priv = xmlDoc.createElementNS(privilege.namespace, privilege.tagname);
        if (privilege.xmlvalue !== null) {
          for (var l = 0; l < privilege.xmlvalue.length; l++) {
            var nodeCopy = xmlDoc.importNode(privilege.xmlValue.item(j), true);
            priv.appendChild(nodeCopy);
          }
        }
        privilegeElement.appendChild(priv);
        privilegeParent.appendChild(privilegeElement);
      }
    }
    aceBody.appendChild(privilegeParent);

    xmlDoc.documentElement.appendChild(aceBody);
  }
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.AclCodec);

// End of file

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

/**
 * This plugin adds acl capabilities to the WebDAV client class
 */

/**
 * Perform a WebDAV ACL request
 *
 * @param    {String}                         path      The path to perform ACL on
 * @param    {Function(status,body,headers)}  callback  Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {nl.sara.webdav.Acl}             acl       The ACL to submit
 * @param    {Array}                          headers   Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                    The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.acl = function(path, callback, acl, headers) {
  if ((path === undefined) || (callback === undefined)) {
    throw new nl.sara.webdav.Exception('ACL requires the parameters path, callback and acl', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if (!(typeof path === "string") || (!nl.sara.webdav.Ie.isIE && !(acl instanceof nl.sara.webdav.Acl))) {
    throw new nl.sara.webdav.Exception('ACL parameter; path should be a string, acl should be an instance of Acl', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  var aclBody = document.implementation.createDocument("DAV:", "acl", null);
  aclBody = nl.sara.webdav.codec.AclCodec.toXML(acl, aclBody);

  // Create the request body string
  var serializer = new XMLSerializer();
  var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(aclBody);

  // And then send the request
  var ajax = null;
  if (nl.sara.webdav.Ie.isIE) {
    if (url.lastIndexOf('?') !== -1) {
      url = url + '&_method=acl';
    }else{
      url = url + '?_method=acl';
    }
    ajax = this.getAjax('POST', url, callback, headers);
  }else{
    ajax = this.getAjax("ACL", url, callback, headers);
  }
  ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"');
  ajax.send(body);

  return this;
};

/**
 * Perform a WebDAV REPORT request
 *
 * @param    {String}                         path         The path to perform REPORT on
 * @param    {Function(status,body,headers)}  callback     Querying the server is done asynchronously, this callback function is called when the results are in
 * @param    {Document}                       body         The (XML DOM) document to parse and send as the request body
 * @param    {Array}                          headers      Optional; Additional headers to set
 * @returns  {nl.sara.webdav.Client}                       The client itself for chaining methods
 */
nl.sara.webdav.Client.prototype.report = function(path, callback, body, headers) {
  if ((path === undefined) || (callback === undefined) || (body === undefined)) {
    throw new nl.sara.webdav.Exception('REPORT requires the parameters path, callback and body', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER);
  }
  if ((typeof path !== "string") || (!nl.sara.webdav.Ie.isIE && !(body instanceof Document))) {
    throw new nl.sara.webdav.Exception('REPORT parameter; path should be a string, body should be an instance of Document', nl.sara.webdav.Exception.WRONG_TYPE);
  }

  // Get the full URL, based on the specified path
  var url = this.getUrl(path);

  // Parse the body
  var serializer = new XMLSerializer();
  var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(body);

  // And then send the request
  var ajax = null;
  if (nl.sara.webdav.Ie.isIE) {
    if (url.lastIndexOf('?') !== -1) {
      url = url + '&_method=report';
    }else{
      url = url + '?_method=report';
    }
    ajax = this.getAjax('POST', url, callback, headers);
  }else{
    ajax = this.getAjax("REPORT", url, callback, headers);
  }
  ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"');
  ajax.send(body);

  return this;
};

// End of library

/*
 * Copyright ©2014 SURFsara bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.Current_user_privilege_setCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.Current_user_privilege_setCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.Current_user_privilege_setCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: current-user-privilege-set to an array of privileges
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.Current_user_privilege_setCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.Current_user_privilege_setCodec.namespace = 'DAV:';
nl.sara.webdav.codec.Current_user_privilege_setCodec.tagname = 'current-user-privilege-set';

nl.sara.webdav.codec.Current_user_privilege_setCodec.fromXML = function(nodelist) {
  var privileges = [];
  for ( var key = 0; key < nodelist.length; key++ ) {
    var node = nodelist.item( key );
    if ( ( node.nodeType === 1 )) {
      var privilege = new nl.sara.webdav.Privilege( node );
      privileges.push( privilege );
    }
  }
  return privileges;
};

nl.sara.webdav.codec.Current_user_privilege_setCodec.toXML = function(value, xmlDoc){
  for ( var key in value ) {
    var privilege = xmlDoc.createElementNS( value[ key ].namespace, value[ key ].tagname );
    xmlDoc.documentElement.appendChild( privilege );
  }
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.Current_user_privilege_setCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.Privilege is already defined, we have a namespace clash!
if (nl.sara.webdav.Privilege !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.Privilege already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class WebDAV ACL privilege
 *
 * @param  {Node}  [xmlNode]  Optional; the xmlNode describing the privilege object
 * @property  {String}    namespace  The namespace
 * @property  {String}    tagname    The tag name
 * @property  {NodeList}  xmlvalue   A NodeList with the value of this privilege
 */
nl.sara.webdav.Privilege = function(xmlNode) {
  // First define private attributes
  Object.defineProperty(this, '_xmlvalue', {
    'value': null,
    'enumerable': false,
    'configurable': false,
    'writable': true
  });
  // Second define public attributes
  Object.defineProperty(this, 'namespace', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });
  Object.defineProperty(this, 'tagname', {
    'value': null,
    'enumerable': true,
    'configurable': false,
    'writable': true
  });

  // Constructor logic
  if (typeof xmlNode !== 'undefined') {
    this.namespace = xmlNode.namespaceURI;
    this.tagname = nl.sara.webdav.Ie.getLocalName(xmlNode);
    this.xmlvalue = xmlNode.childNodes;
  }
};

//######################### DEFINE PUBLIC ATTRIBUTES ###########################
(function() {
  // This creates a (private) static variable. It will contain all codecs
  var codecNamespaces = {};

  Object.defineProperty(nl.sara.webdav.Privilege.prototype, 'xmlvalue', {
    'set': function(value) {
      if (value === null) {
        this._xmlvalue = null;
        return;
      }
      if (!nl.sara.webdav.Ie.isIE && !(value instanceof NodeList)) {
        throw new nl.sara.webdav.Exception('xmlvalue must be an instance of NodeList', nl.sara.webdav.Exception.WRONG_TYPE);
      }
      this._xmlvalue = value;
    },
    'get': function() {
      return this._xmlvalue;
    }
  });

//########################## DEFINE PUBLIC METHODS #############################
  /**
   * Adds functions to encode or decode properties
   *
   * This allows exact control in how Privilege.xmlvalue is parsed when
   * getParsedValue() is called and how XML is rebuild when
   * setValueAndRebuildXml() is called. You can specify two functions: 'fromXML
   * and 'toXML'. These should be complementary. That is, toXML should be able
   * to create a NodeList based on the output of fromXML. For example:
   * A == toXML(fromXML(A)) &&
   * B == fromXML(toXML(B))
   *
   * @param    {nl.sara.webdav.Codec}  codec  The codec to add
   * @returns  {void}
   */
  nl.sara.webdav.Privilege.addCodec = function(codec) {
    if (typeof codec.namespace !== 'string') {
      throw new nl.sara.webdav.Exception('addCodec: codec.namespace must be a String', nl.sara.webdav.Exception.WRONG_TYPE);
    }
    if (typeof codec.tagname !== 'string') {
      throw new nl.sara.webdav.Exception('addCodec: codec.tagname must be a String', nl.sara.webdav.Exception.WRONG_TYPE);
    }
    if (codecNamespaces[codec.namespace] === undefined) {
      codecNamespaces[codec.namespace] = {};
    }
    codecNamespaces[codec.namespace][codec.tagname] = {
      'fromXML': (codec.fromXML ? codec.fromXML : undefined),
      'toXML': (codec.toXML ? codec.toXML : undefined)
    };
  };

  /**
   * Sets a new value and rebuilds xmlvalue
   *
   * If a codec for this privilege is specified, it will use this codec to
   * rebuild xmlvallue. Else it will attempt to create one CDATA element with
   * the string value of whatever was fiven as parameter.
   *
   * @param   {mixed}  value  The object to base the xmlvalue on
   * @return  {void}
   */
  nl.sara.webdav.Privilege.prototype.setValueAndRebuildXml = function(value) {
    // Call codec to automatically create correct 'xmlvalue'
    var xmlDoc = document.implementation.createDocument(this.namespace, this.tagname, null);
    if ((codecNamespaces[this.namespace] === undefined) ||
        (codecNamespaces[this.namespace][this.tagname] === undefined) ||
        (codecNamespaces[this.namespace][this.tagname]['toXML'] === undefined)) {
      // No 'toXML' function set, so create a NodeList with just one CDATA node
      if (value !== null) { // If the value is NULL, then we should add anything to the NodeList
        var cdata = xmlDoc.createCDATASection(value);
        xmlDoc.documentElement.appendChild(cdata);
      }
      this._xmlvalue = xmlDoc.documentElement.childNodes;
    }else{
      this._xmlvalue = codecNamespaces[this.namespace][this.tagname]['toXML'](value, xmlDoc).documentElement.childNodes;
    }
  };

  /**
   * Parses the xmlvalue
   *
   * If a codec for this privilege is specified, it will use this codec to parse
   * the XML nodes. Else it will attempt to parse it as text or CDATA elements.
   *
   * @return  {mixed}  If a codec is specified, the return type depends on that
   * codec. If no codec is specified and at least one node in xmlvalue is not a
   * text or CDATA node, it will return undefined. If xmlvalue is empty, it will
   * return null.
   */
  nl.sara.webdav.Privilege.prototype.getParsedValue = function() {
    // Call codec to automatically create correct 'value'
    if (this._xmlvalue.length > 0) {
      if ((codecNamespaces[this.namespace] === undefined) ||
          (codecNamespaces[this.namespace][this.tagname] === undefined) ||
          (codecNamespaces[this.namespace][this.tagname]['fromXML'] === undefined)) {
        // No 'fromXML' function set, so try to create a text value based on TextNodes and CDATA nodes. If other nodes are present, set 'value' to null
        var parsedValue = '';
        for (var i = 0; i < this._xmlvalue.length; i++) {
          var node = this._xmlvalue.item(i);
          if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored
            parsedValue += node.nodeValue;
          }else{ // If even one of the nodes is not text or CDATA, then we don't parse a text value at all
            parsedValue = undefined;
            break;
          }
        }
        return parsedValue;
      }else{
        return codecNamespaces[this.namespace][this.tagname]['fromXML'](this._xmlvalue);
      }
    }
    return null;
  };
})(); // Ends the static scope

/**
 * Overloads the default toString() method so it returns the value of this privilege
 *
 * @returns  {String}  A string representation of this privilege
 */
nl.sara.webdav.Privilege.prototype.toString = function() {
  return this.getParsedValue();
};

// End of library

/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.CreationdateCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.CreationdateCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.CreationdateCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: creationdate to a Date object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.CreationdateCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.CreationdateCodec.namespace = 'DAV:';
nl.sara.webdav.codec.CreationdateCodec.tagname = 'creationdate';

nl.sara.webdav.codec.CreationdateCodec.fromXML = function(nodelist) {
  var node = nodelist.item(0);
  if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored
    return new Date(node.nodeValue);
  }else{ // If the node is not text or CDATA, then we don't parse a value at all
    return null;
  }
};

nl.sara.webdav.codec.CreationdateCodec.toXML = function(value, xmlDoc){
  var cdata = xmlDoc.createCDATASection(value.toISOString());
  xmlDoc.documentElement.appendChild(cdata);
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.CreationdateCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.GetcontentlengthCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.GetcontentlengthCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.GetcontentlengthCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: getcontentlength to an integer
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.GetcontentlengthCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.GetcontentlengthCodec.namespace = 'DAV:';
nl.sara.webdav.codec.GetcontentlengthCodec.tagname = 'getcontentlength';

nl.sara.webdav.codec.GetcontentlengthCodec.fromXML = function(nodelist) {
  var node = nodelist.item(0);
  if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored
    return parseInt(node.nodeValue);
  }else{ // If the node is not text or CDATA, then we don't parse a text value at all
    return null;
  }
};

nl.sara.webdav.codec.GetcontentlengthCodec.toXML = function(value, xmlDoc){
  var cdata = xmlDoc.createCDATASection(value.toString());
  xmlDoc.documentElement.appendChild(cdata);
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.GetcontentlengthCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.GetlastmodifiedCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.GetlastmodifiedCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.GetlastmodifiedCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: getlastmodified to a Date object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.GetlastmodifiedCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.GetlastmodifiedCodec.namespace = 'DAV:';
nl.sara.webdav.codec.GetlastmodifiedCodec.tagname = 'getlastmodified';

nl.sara.webdav.codec.GetlastmodifiedCodec.fromXML = function(nodelist) {
  var node = nodelist.item(0);
  if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored
    return new Date(node.nodeValue);
  }else{ // If the node is not text or CDATA, then we don't parse a value at all
    return null;
  }
};

nl.sara.webdav.codec.GetlastmodifiedCodec.toXML = function(value, xmlDoc){
  function pad(text) {
    text = text.toString();
    while (text.length < 2) {
      text = '0' + text;
    }
    return text;
  }
  // I will parse this myself, because the Date object doesn't have a method which is specified to always give a rfc1123-date (defined in Section 3.3.1 of [RFC2616]). Even though Date.toUTCString() often does so, it doesn't have to.
  var wkday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  var month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  var date1 = pad(value.getUTCDate()) + ' ' + month[value.getUTCMonth()] + ' ' + value.getUTCFullYear();
  var time = pad(value.getUTCHours()) + ':' + pad(value.getUTCMinutes()) + ':' + pad(value.getUTCSeconds());
  var cdata = xmlDoc.createCDATASection(wkday[value.getUTCDay()] + ', ' + date1 + ' ' + time + ' GMT');
  xmlDoc.documentElement.appendChild(cdata);
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.GetlastmodifiedCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If nl.sara.webdav.codec.OwnerCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.OwnerCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.OwnerCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: owner to a Date object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.OwnerCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.OwnerCodec.namespace = 'DAV:';
nl.sara.webdav.codec.OwnerCodec.tagname = 'owner';

nl.sara.webdav.codec.OwnerCodec.fromXML = function(nodelist) {
  var returnValue = null;

  // Assertions whether the formed XML is correct
  var hrefNode = nodelist[0];

  if ((hrefNode.nodeType === 1) &&
      (hrefNode.namespaceURI === 'DAV:') &&
      (hrefNode.localName === 'href'))
  {
    var node = hrefNode.childNodes.item(0);

    if ( ( node.nodeType === 3 ) || ( node.nodeType === 4 ) ) { // Make sure text and CDATA content is stored
      returnValue = node.nodeValue;
    }
  } 
  return returnValue;
};

nl.sara.webdav.codec.OwnerCodec.toXML = function(value, xmlDoc){  
  var hrefNode = xmlDoc.createElementNS( 'DAV:', 'href' );
  hrefNode.appendChild( xmlDoc.createCDATASection( value.toString() ) );
  xmlDoc.documentElement.appendChild( hrefNode );
  
  return xmlDoc
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.OwnerCodec);

// End of file
/*
 * Copyright ©2012 SARA bv, The Netherlands
 *
 * This file is part of js-webdav-client.
 *
 * js-webdav-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * js-webdav-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with js-webdav-client.  If not, see <http://www.gnu.org/licenses/>.
 */
"use strict";

// If it isn't done yet: create a namespace for all the default codecs
if (nl.sara.webdav.codec === undefined) {
  nl.sara.webdav.codec = {};
}

// If nl.sara.webdav.codec.ResourcetypeCodec is already defined, we have a namespace clash!
if (nl.sara.webdav.codec.ResourcetypeCodec !== undefined) {
  throw new nl.sara.webdav.Exception('Namespace nl.sara.webdav.codec.ResourcetypeCodec already taken, could not load JavaScript library for WebDAV connectivity.', nl.sara.webdav.Exception.NAMESPACE_TAKEN);
}

/**
 * @class Adds a codec that converts DAV: resourcetype to a Date object
 * @augments nl.sara.webdav.Codec
 */
nl.sara.webdav.codec.ResourcetypeCodec = new nl.sara.webdav.Codec();
nl.sara.webdav.codec.ResourcetypeCodec.namespace = 'DAV:';
nl.sara.webdav.codec.ResourcetypeCodec.tagname = 'resourcetype';

/**
 * Class constants are a way to specify what the resource type is
 */
nl.sara.webdav.codec.ResourcetypeCodec.COLLECTION = 1;
nl.sara.webdav.codec.ResourcetypeCodec.UNSPECIFIED = 2;
nl.sara.webdav.codec.ResourcetypeCodec.PRINCIPAL = 4;

nl.sara.webdav.codec.ResourcetypeCodec.fromXML = function(nodelist) {
  for (var i = 0; i < nodelist.length; i++) {
    var node = nodelist.item(i);
    if (node.namespaceURI === 'DAV:') {
      switch (nl.sara.webdav.Ie.getLocalName(node)) {
        case 'collection':
          return nl.sara.webdav.codec.ResourcetypeCodec.COLLECTION;
        case 'principal':
          return nl.sara.webdav.codec.ResourcetypeCodec.PRINCIPAL;
      }
    }
  }
  return nl.sara.webdav.codec.ResourcetypeCodec.UNSPECIFIED;
};

nl.sara.webdav.codec.ResourcetypeCodec.toXML = function(value, xmlDoc){
  switch(value) {
    case nl.sara.webdav.codec.ResourcetypeCodec.COLLECTION:
      var collection = xmlDoc.createElementNS('DAV:', 'collection');
      xmlDoc.documentElement.appendChild(collection);
    case nl.sara.webdav.codec.ResourcetypeCodec.PRINCIPAL:
      var collection = xmlDoc.createElementNS('DAV:', 'principal');
      xmlDoc.documentElement.appendChild(collection);
    break;
  }
  return xmlDoc;
};

nl.sara.webdav.Property.addCodec(nl.sara.webdav.codec.ResourcetypeCodec);

// End of file




    exports = nl;
    return exports;
});


