1 /* 2 * Copyright ©2012 SARA bv, The Netherlands 3 * 4 * This file is part of js-webdav-client. 5 * 6 * js-webdav-client is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published 8 * by the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * js-webdav-client is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with js-webdav-client. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 "use strict"; 20 21 // If nl.sara.webdav.Client is already defined, we have a namespace clash! 22 if (nl.sara.webdav.Client !== undefined) { 23 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); 24 } 25 26 /** 27 * @class Connection to a WebDAV server 28 * 29 * @param {Array} [config] A configuration object. It can contain the following fields: 30 * - host; String containing the hostname to connect to (default: current host) 31 * - 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) 32 * - port; Integer with the port number to use. Is ignored if host is not set. (default: 443 for HTTPS and 80 for HTTP) 33 * - defaultHeaders; An array containing default headers to use for each request. Header names should be the keys. (default: {} i.e. no default headers) 34 * - username; A string with the username to use for authentication. (default: the current username if any) 35 * - 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) 36 * For backwards compatibility reasons, it is also 37 * possible to specify the 'host', 'useHTTPS', 'port' 38 * and 'defaultHeaders' as regular parameters (in 39 * that order). Note that useHTTPS works differently 40 * in that case; any other value than boolean True 41 * will result in regular HTTP. If you want to supply 42 * a username and password, you will have to use the 43 * config array, you cannot pass them as extra normal 44 * parameters. 45 */ 46 nl.sara.webdav.Client = function(config, useHTTPS, port, defaultHeaders) { 47 // First define private attributes 48 Object.defineProperty(this, '_baseUrl', { 49 'value': null, 50 'enumerable': false, 51 'configurable': false, 52 'writable': true 53 }); 54 Object.defineProperty(this, '_username', { 55 'value': null, 56 'enumerable': false, 57 'configurable': false, 58 'writable': true 59 }); 60 Object.defineProperty(this, '_password', { 61 'value': null, 62 'enumerable': false, 63 'configurable': false, 64 'writable': true 65 }); 66 Object.defineProperty(this, '_headers', { 67 'value': {}, 68 'enumerable': false, 69 'configurable': false, 70 'writable': true 71 }); 72 73 // Constructor logic 74 if ( config !== undefined ) { 75 var host; 76 if ( typeof( config ) !== 'string' ) { 77 host = config['host']; 78 useHTTPS = ( config['useHTTPS'] !== false ); 79 port = config['port']; 80 defaultHeaders = config['defaultHeaders']; 81 82 if ( config['username'] !== undefined ) { 83 this._username = config['username']; 84 if ( config['password'] !== undefined ) { 85 this._password = config['password']; 86 }else{ 87 this._password = ''; 88 } 89 } 90 }else{ 91 host = config; 92 } 93 94 if ( host !== undefined ) { 95 // 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 96 var protocol = (useHTTPS === true) ? 'https' : 'http'; 97 port = (port !== undefined) ? port : ((protocol === 'https') ? 443 : 80); 98 this._baseUrl = protocol + '://' + host + ((((protocol === 'http') && (port === 80)) || ((protocol === 'https') && (port === 443))) ? '' : ':' + port); 99 } 100 } 101 102 if (defaultHeaders !== undefined) { 103 this._headers = defaultHeaders; 104 } 105 }; 106 107 /**#@+ 108 * Class constant 109 */ 110 nl.sara.webdav.Client.PROPNAME = 1; 111 nl.sara.webdav.Client.ALLPROP = 2; 112 nl.sara.webdav.Client.INFINITY = 'infinity'; 113 nl.sara.webdav.Client.FAIL_ON_OVERWRITE = 3; 114 nl.sara.webdav.Client.TRUNCATE_ON_OVERWRITE = 4; 115 nl.sara.webdav.Client.SILENT_OVERWRITE = 5; 116 /**#@-*/ 117 118 //########################## DEFINE PUBLIC METHODS ############################# 119 /** 120 * Converts a path to the full url (i.e. appends the protocol and host part) 121 * 122 * @param {String} path The path on the server 123 * @returns {String} The full url to the path 124 */ 125 nl.sara.webdav.Client.prototype.getUrl = function(path) { 126 if (path.substring(0,1) !== '/') { 127 path = '/' + path; 128 } 129 if (this._baseUrl !== null) { 130 return this._baseUrl + path; 131 }else{ 132 return path; 133 } 134 }; 135 136 /** 137 * Perform a WebDAV PROPFIND request 138 * 139 * @param {String} path The path get a PROPFIND for 140 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 141 * @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. 142 * @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. 143 * @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. 144 * @param {Array} headers Optional; Additional headers to set 145 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 146 */ 147 nl.sara.webdav.Client.prototype.propfind = function(path, callback, depth, props, include, headers) { 148 if ((path === undefined) || (callback === undefined)) { 149 throw new nl.sara.webdav.Exception('PROPFIND requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 150 } 151 if (!(typeof path === "string")) { 152 throw new nl.sara.webdav.Exception('PROPFIND parameter; path should be a string', nl.sara.webdav.Exception.WRONG_TYPE); 153 } 154 155 // Get the full URL, based on the specified path 156 var url = this.getUrl(path); 157 158 // Check the depth header 159 if (depth === undefined) { // We default depth to 0, not to infinity as this is not supported by all servers 160 depth = 0; 161 } 162 var depthHeader = null; 163 switch (depth) { 164 case 0: 165 case 1: 166 depthHeader = depth; 167 break; 168 case nl.sara.webdav.Client.INFINITY: 169 depthHeader = 'infinity'; 170 break; 171 default: 172 throw new nl.sara.webdav.Exception("Depth header should be '0', '1' or nl.sara.webdav.Client.INFINITY", nl.sara.webdav.Exception.WRONG_VALUE); 173 break; 174 } 175 176 // Create the request XML 177 if (props === undefined) { 178 props = nl.sara.webdav.Client.ALLPROP; 179 } 180 var propsBody = document.implementation.createDocument("DAV:", "propfind", null); 181 switch (props) { // Find out what the request is for 182 case nl.sara.webdav.Client.PROPNAME: // User wants all property names 183 propsBody.documentElement.appendChild(propsBody.createElementNS('DAV:', 'propname')); 184 break; 185 case nl.sara.webdav.Client.ALLPROP: // User wants all properties 186 propsBody.documentElement.appendChild(propsBody.createElementNS('DAV:', 'allprop')); 187 if (include !== undefined) { // There is content for the <DAV: include> tags, so parse it 188 if (!(include instanceof Array)) { 189 throw new nl.sara.webdav.Exception('Propfind parameter; include should be an array', nl.sara.webdav.Exception.WRONG_TYPE); 190 } 191 var includeBody = propsBody.createElementNS('DAV:', 'include'); 192 for (var i = 0; i < include.length; i++) { 193 var item = include[i]; 194 if (!nl.sara.webdav.Ie.isIE && !(item instanceof nl.sara.webdav.Property)) { 195 continue; 196 } 197 includeBody.appendChild(propsBody.createElementNS(item.namespace, item.tagname)); 198 } 199 if (includeBody.hasChildNodes()) { // But only really add the <include> tag if there is valid content 200 propsBody.documentElement.appendChild(includeBody); 201 } 202 } 203 break; 204 default: // The default is to expect an array with Property objects; the user wants the values of these properties 205 if (!(props instanceof Array)) { 206 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); 207 } 208 var propertyBody = propsBody.createElementNS('DAV:', 'prop'); 209 for (var i = 0; i < props.length; i++) { // Cycle through the array 210 var prop = props[i]; 211 if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) { 212 continue; 213 } 214 propertyBody.appendChild(propsBody.createElementNS(prop.namespace, prop.tagname)); 215 } 216 if (!propertyBody.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it 217 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); 218 } 219 propsBody.documentElement.appendChild(propertyBody); 220 break; 221 } 222 var serializer = new XMLSerializer(); 223 var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(propsBody); 224 225 // And then send the request 226 var ajax = this.getAjax("PROPFIND", url, callback, headers); 227 ajax.setRequestHeader('Depth', depthHeader); 228 ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"'); 229 ajax.send(body); 230 231 return this; 232 }; 233 234 /** 235 * Perform a WebDAV PROPPATCH request 236 * 237 * @param {String} path The path do a PROPPATCH on 238 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 239 * @param {nl.sara.webdav.Property[]} [setProps] Optional; The properties to set. If not set, delProps should be set. Can be omitted with 'undefined'. 240 * @param {nl.sara.webdav.Property[]} [delProps] Optional; The properties to delete. If not set, setProps should be set. 241 * @param {Array} headers Optional; Additional headers to set 242 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 243 */ 244 nl.sara.webdav.Client.prototype.proppatch = function(path, callback, setProps, delProps, headers) { 245 if ((path === undefined) || (callback === undefined) || ((setProps === undefined) && (delProps === undefined))) { 246 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); 247 } 248 if (!(typeof path === "string") || ((setProps !== undefined) && !(setProps instanceof Array)) || ((delProps !== undefined) && !(delProps instanceof Array))) { 249 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); 250 } 251 252 // Get the full URL, based on the specified path 253 var url = this.getUrl(path); 254 255 // Create the request XML 256 var propsBody = document.implementation.createDocument("DAV:", "propertyupdate", null); 257 propsBody.documentElement.setAttribute("xmlns:D", "DAV:"); 258 if (setProps !== undefined) { 259 var props = propsBody.createElementNS('DAV:', 'prop'); 260 for (var i = 0; i < setProps.length; i++) { // Cycle through the array 261 var prop = setProps[i]; 262 if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) { 263 continue; 264 } 265 var element = propsBody.createElementNS(prop.namespace, prop.tagname); 266 for (var j = 0; j < prop.xmlvalue.length; j++) { 267 var nodeCopy = propsBody.importNode(prop.xmlvalue.item(j), true); 268 element.appendChild(nodeCopy); 269 } 270 props.appendChild(element); 271 } 272 if (!props.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it 273 throw new nl.sara.webdav.Exception("Proppatch parameter; setProps should be an array of Properties", nl.sara.webdav.Exception.WRONG_TYPE); 274 } 275 var set = propsBody.createElementNS('DAV:', 'set'); 276 set.appendChild(props); 277 propsBody.documentElement.appendChild(set); 278 } 279 if (delProps !== undefined) { 280 var props = propsBody.createElementNS('DAV:', 'prop'); 281 for (var i = 0; i < delProps.length; i++) { // Cycle through the array 282 var prop = delProps[i]; 283 if (!nl.sara.webdav.Ie.isIE && !(prop instanceof nl.sara.webdav.Property)) { 284 continue; 285 } 286 var element = propsBody.createElementNS(prop.namespace, prop.tagname); 287 props.appendChild(element); 288 } 289 if (!props.hasChildNodes()) { // But if no properties are found, then the array didn't have Property objects in it 290 throw new nl.sara.webdav.Exception("Proppatch parameter; delProps should be an array of Properties", nl.sara.webdav.Exception.WRONG_TYPE); 291 } 292 var remove = propsBody.createElementNS('DAV:', 'remove'); 293 remove.appendChild(props); 294 propsBody.documentElement.appendChild(remove); 295 } 296 var serializer = new XMLSerializer(); 297 var body = '<?xml version="1.0" encoding="utf-8" ?>' + serializer.serializeToString(propsBody); 298 299 // And then send the request 300 var ajax = this.getAjax("PROPPATCH", url, callback, headers); 301 ajax.setRequestHeader('Content-Type', 'application/xml; charset="utf-8"'); 302 ajax.send(body); 303 304 return this; 305 }; 306 307 /** 308 * Perform a WebDAV MKCOL request 309 * 310 * @param {String} path The path to perform MKCOL on 311 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 312 * @param {String} [body] Optional; a body to include in the MKCOL request. 313 * @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 314 * @param {Array} headers Optional; Additional headers to set 315 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 316 */ 317 nl.sara.webdav.Client.prototype.mkcol = function(path, callback, body, contenttype, headers) { 318 if ((path === undefined) || (callback === undefined)) { 319 throw new nl.sara.webdav.Exception('MKCOL requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 320 } 321 if ((typeof path !== "string") || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) { 322 throw new nl.sara.webdav.Exception('MKCOL parameter; path and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 323 } 324 325 // Get the full URL, based on the specified path 326 var url = this.getUrl(path); 327 328 // And then send the request 329 var ajax = this.getAjax("MKCOL", url, callback, headers); 330 if (body !== undefined) { 331 if (contenttype !== undefined) { 332 ajax.setRequestHeader('Content-Type', contenttype); 333 } 334 ajax.send(body); 335 }else{ 336 ajax.send(); 337 } 338 339 return this; 340 }; 341 342 /** 343 * Perform a WebDAV DELETE request 344 * 345 * Because 'delete' is an operator in JavaScript, I had to name this method 346 * 'remove'. However, performs a regular DELETE request as described in 347 * RFC 4918 348 * 349 * @param {String} path The path to perform DELETE on 350 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 351 * @param {Array} headers Optional; Additional headers to set 352 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 353 */ 354 nl.sara.webdav.Client.prototype.remove = function(path, callback, headers) { 355 if ((path === undefined) || (callback === undefined)) { 356 throw new nl.sara.webdav.Exception('DELETE requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 357 } 358 if (typeof path !== "string"){ 359 throw new nl.sara.webdav.Exception('DELETE parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 360 } 361 362 // Get the full URL, based on the specified path 363 var url = this.getUrl(path); 364 365 // And then send the request 366 var ajax = this.getAjax("DELETE", url, callback, headers); 367 ajax.send(); 368 369 return this; 370 }; 371 372 /** 373 * Perform a WebDAV GET request 374 * 375 * @param {String} path The path to GET 376 * @param {Function(status,content)} callback Querying the server is done asynchronously, this callback function is called when the results are in 377 * @param {Array} headers Optional; Additional headers to set 378 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 379 */ 380 nl.sara.webdav.Client.prototype.get = function(path, callback, headers) { 381 if ((path === undefined) || (callback === undefined)) { 382 throw new nl.sara.webdav.Exception('GET requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 383 } 384 if (typeof path !== "string"){ 385 throw new nl.sara.webdav.Exception('GET parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 386 } 387 388 // Get the full URL, based on the specified path 389 var url = this.getUrl(path); 390 391 // And then send the request 392 var ajax = null; 393 ajax = this.getAjax("GET", url, callback, headers); 394 ajax.send(); 395 396 return this; 397 }; 398 399 /** 400 * Perform a WebDAV HEAD request 401 * 402 * @param {String} path The path to perform HEAD on 403 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 404 * @param {Array} headers Optional; Additional headers to set 405 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 406 */ 407 nl.sara.webdav.Client.prototype.head = function(path, callback, headers) { 408 if ((path === undefined) || (callback === undefined)) { 409 throw new nl.sara.webdav.Exception('HEAD requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 410 } 411 if (typeof path !== "string"){ 412 throw new nl.sara.webdav.Exception('HEAD parameter; path should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 413 } 414 415 // Get the full URL, based on the specified path 416 var url = this.getUrl(path); 417 418 // And then send the request 419 var ajax = null; 420 ajax = this.getAjax("HEAD", url, callback, headers); 421 ajax.send(); 422 423 return this; 424 }; 425 426 /** 427 * Perform a WebDAV PUT request 428 * 429 * @param {String} path The path to perform PUT on 430 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 431 * @param {String} body The content to include in the PUT request. 432 * @param {String} [contenttype] Optional; the content type of the body (i.e. value for the Content-Type header). 433 * @param {Array} headers Optional; Additional headers to set 434 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 435 */ 436 nl.sara.webdav.Client.prototype.put = function(path, callback, body, contenttype, headers) { 437 if ((path === undefined) || (callback === undefined) || (body === undefined)) { 438 throw new nl.sara.webdav.Exception('PUT requires the parameters path, callback and body', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 439 } 440 if ((typeof path !== "string") || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) { 441 throw new nl.sara.webdav.Exception('PUT parameter; path and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 442 } 443 444 // Get the full URL, based on the specified path 445 var url = this.getUrl(path); 446 447 // And then send the request 448 var ajax = null; 449 ajax = this.getAjax("PUT", url, callback, headers); 450 if (contenttype !== undefined) { 451 ajax.setRequestHeader('Content-Type', contenttype); 452 } 453 ajax.send(body); 454 455 return this; 456 }; 457 458 /** 459 * Perform a WebDAV POST request 460 * 461 * @param {String} path The path to perform POST on 462 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 463 * @param {String} [body] Optional; a body to include in the POST request. 464 * @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 465 * @param {Array} headers Optional; Additional headers to set 466 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 467 */ 468 nl.sara.webdav.Client.prototype.post = function(path, callback, body, contenttype, headers) { 469 if ((path === undefined) || (callback === undefined)) { 470 throw new nl.sara.webdav.Exception('POST requires the parameters path and callback', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 471 } 472 if ((typeof path !== "string") || ((body !== undefined) && (typeof body !== 'string')) || ((contenttype !== undefined) && (typeof contenttype !== 'string'))) { 473 throw new nl.sara.webdav.Exception('POST parameter; path, body and contenttype should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 474 } 475 476 // Get the full URL, based on the specified path 477 var url = this.getUrl(path); 478 479 // And then send the request 480 var ajax = null; 481 ajax = this.getAjax("POST", url, callback, headers); 482 if ( body !== undefined ) { 483 if (contenttype !== undefined) { 484 ajax.setRequestHeader('Content-Type', contenttype); 485 }else{ 486 ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 487 } 488 ajax.send(body); 489 }else{ 490 ajax.send(); 491 } 492 493 return this; 494 }; 495 496 /** 497 * Perform a WebDAV COPY request 498 * 499 * @param {String} path The path to perform COPY on 500 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 501 * @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 /) 502 * @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. 503 * @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 504 * @param {Array} headers Optional; Additional headers to set 505 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 506 */ 507 nl.sara.webdav.Client.prototype.copy = function(path, callback, destination, overwriteMode, depth, headers) { 508 if ((path === undefined) || (callback === undefined) || (destination === undefined)) { 509 throw new nl.sara.webdav.Exception('COPY requires the parameters path, callback and destination', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 510 } 511 if ((typeof path !== "string") || (typeof destination !== "string")){ 512 throw new nl.sara.webdav.Exception('COPY parameter; path and destination should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 513 } 514 515 // Get the full URL, based on the specified path 516 var url = this.getUrl(path); 517 518 // If the destination starts with a / it is a absolute url on the same host, so prepare a complete URL 519 if (destination.substr(0,1) === '/') { 520 destination = this.getUrl(destination); 521 } // Else I assume it is a complete URL already 522 523 // And then send the request 524 var ajax = this.getAjax("COPY", url, callback, headers); 525 ajax.setRequestHeader('Destination', destination); 526 if (depth !== undefined) { 527 if ((depth !== 0) && (depth !== 'infinity')) { 528 throw new nl.sara.webdav.Exception("COPY parameter; depth should be '0' or 'infinity'", nl.sara.webdav.Exception.WRONG_VALUE); 529 } 530 ajax.setRequestHeader('Depth', depth); 531 } 532 if (overwriteMode === nl.sara.webdav.Client.FAIL_ON_OVERWRITE) { 533 ajax.setRequestHeader('Overwrite', 'F'); 534 } 535 ajax.send(); 536 537 return this; 538 }; 539 540 /** 541 * Perform a WebDAV MOVE request 542 * 543 * @param {String} path The path to perform MOVE on 544 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 545 * @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 /) 546 * @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. 547 * @param {Array} headers Optional; Additional headers to set 548 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 549 */ 550 nl.sara.webdav.Client.prototype.move = function(path, callback, destination, overwriteMode, headers) { 551 if ((path === undefined) || (callback === undefined) || (destination === undefined)) { 552 throw new nl.sara.webdav.Exception('MOVE requires the parameters path, callback and destination', nl.sara.webdav.Exception.MISSING_REQUIRED_PARAMETER); 553 } 554 if ((typeof path !== "string") || (typeof destination !== "string")){ 555 throw new nl.sara.webdav.Exception('MOVE parameter; path and destination should be strings', nl.sara.webdav.Exception.WRONG_TYPE); 556 } 557 558 // Get the full URL, based on the specified path 559 var url = this.getUrl(path); 560 561 // If the destination starts with a / it is a absolute url on the same host, so prepare a complete URL 562 if (destination.substr(0,1) === '/') { 563 destination = this.getUrl(destination); 564 } // Else I assume it is a complete URL already 565 566 // And then send the request 567 var ajax = this.getAjax("MOVE", url, callback, headers); 568 ajax.setRequestHeader('Destination', destination); 569 if (overwriteMode === nl.sara.webdav.Client.FAIL_ON_OVERWRITE) { 570 ajax.setRequestHeader('Overwrite', 'F'); 571 }else if (overwriteMode === nl.sara.webdav.Client.TRUNCATE_ON_OVERWRITE) { 572 ajax.setRequestHeader('Overwrite', 'T'); 573 } 574 ajax.send(); 575 576 return this; 577 }; 578 579 /** 580 * Perform a WebDAV LOCK request 581 * 582 * @param {String} path The path to perform LOCK on 583 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 584 * @param {Array} headers Optional; Additional headers to set 585 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 586 */ 587 nl.sara.webdav.Client.prototype.lock = function(path, callback, headers) { 588 throw new nl.sara.webdav.Exception('LOCK is not implemented yet', nl.sara.webdav.Exception.NOT_IMPLEMENTED); 589 return this; 590 }; 591 592 /** 593 * Perform a WebDAV UNLOCK request 594 * 595 * @param {String} path The path to perform UNLOCK on 596 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 597 * @param {Array} headers Optional; Additional headers to set 598 * @returns {nl.sara.webdav.Client} The client itself for chaining methods 599 */ 600 nl.sara.webdav.Client.prototype.unlock = function(path, callback, headers) { 601 throw new nl.sara.webdav.Exception('UNLOCK is not implemented yet', nl.sara.webdav.Exception.NOT_IMPLEMENTED); 602 return this; 603 }; 604 605 /** 606 * Prepares a XMLHttpRequest object to be used for an AJAX request 607 * 608 * @static 609 * @param {String} method The HTTP/webDAV method to use (e.g. GET, POST, PROPFIND) 610 * @param {String} url The url to connect to 611 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 612 * @param {Array} headers Additional headers to set 613 * @returns {XMLHttpRequest} A prepared XMLHttpRequest 614 */ 615 nl.sara.webdav.Client.prototype.getAjax = function(method, url, callback, headers) { 616 var /** @type XMLHttpRequest */ ajax = new XMLHttpRequest(); 617 if ( this._username !== null ) { 618 ajax.open( method, url, true, this._username, this._password ); 619 }else{ 620 ajax.open( method, url, true ); 621 } 622 ajax.onreadystatechange = function() { 623 nl.sara.webdav.Client.ajaxHandler( ajax, callback ); 624 }; 625 626 if (headers === undefined) { 627 headers = {}; 628 } 629 for (var header in this._headers) { 630 if (headers[header] === undefined) { 631 ajax.setRequestHeader(header, this._headers[header]); 632 } 633 } 634 for (var header in headers) { 635 ajax.setRequestHeader(header, headers[header]); 636 } 637 return ajax; 638 }; 639 640 /** 641 * AJAX request handler. Parses Multistatus (if available) and call a user specified callback function 642 * 643 * @static 644 * @param {XMLHttpRequest} ajax The XMLHttpRequest object which performed the request 645 * @param {Function(status,body,headers)} callback Querying the server is done asynchronously, this callback function is called when the results are in 646 * @returns {void} 647 */ 648 nl.sara.webdav.Client.ajaxHandler = function(ajax, callback) { 649 if (ajax.readyState === 4){ //if request has completed 650 var body = ajax.responseText; 651 if (ajax.status === 207) { 652 // Parse the response to a Multistatus object 653 for (var counter = 0; counter < ajax.responseXML.childNodes.length; counter++) { 654 if (nl.sara.webdav.Ie.getLocalName(ajax.responseXML.childNodes.item(counter)) === 'multistatus') { 655 body = new nl.sara.webdav.Multistatus(ajax.responseXML.childNodes.item(counter)); 656 break; 657 } 658 } 659 } 660 callback(ajax.status, body, ajax.getAllResponseHeaders()); 661 } 662 }; 663 664 // End of library 665