├── README.md ├── davclient.js ├── test.html └── test_client.js /README.md: -------------------------------------------------------------------------------- 1 | jsdavclient 2 | =========== 3 | Low-level JavaScript WebDAV client implementation written in Javascript. 4 | 5 | Supports following methods: MKCOL, MOVE, GET, DELETE, COPY, PUT, PROPFIND 6 | 7 | -------------------------------------------------------------------------------- /davclient.js: -------------------------------------------------------------------------------- 1 | /* 2 | davclient.js - Low-level JavaScript WebDAV client implementation 3 | 4 | Supports following methods: MKCOL, MOVE, GET, DELETE, COPY, PUT, PROPFIND 5 | 6 | Copyright (C) Sven vogler 7 | email s.vogler@gmx.de 8 | 9 | Inspired by implementation of Guido Wesdorp 10 | http://debris.demon.nl/projects/davclient.js/ 11 | 12 | This program is free software; you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation; either version 2 of the License, or 15 | (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License 23 | along with this program; if not, write to the Free Software 24 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 | 26 | */ 27 | 28 | var global = this; 29 | 30 | global.string = new function() { 31 | var string = this; 32 | 33 | this.strip = function strip(s) { 34 | /* returns a string with all leading and trailing whitespace removed */ 35 | var stripspace = /^\s*([\s\S]*?)\s*$/; 36 | return stripspace.exec(s)[1]; 37 | }; 38 | 39 | this.deentitize = function deentitize(s) { 40 | /* convert all standard XML entities to the corresponding characters */ 41 | // first numbered entities 42 | var numberedreg = /&#(x?)([a-f0-9]{2,});/ig; 43 | while (true) { 44 | var match = numberedreg.exec(s); 45 | if (!match) { 46 | break; 47 | }; 48 | var value = match[2]; 49 | var base = 10; 50 | if (match[1]) { 51 | base = 16; 52 | }; 53 | value = String.fromCharCode(parseInt(value, base)); 54 | s = s.replace(new RegExp(match[0], 'g'), value); 55 | }; 56 | // and standard ones 57 | s = s.replace(/>/g, '>'); 58 | s = s.replace(/</g, '<'); 59 | s = s.replace(/'/g, "'"); 60 | s = s.replace(/"/g, '"'); 61 | s = s.replace(/&/g, '&'); 62 | s = s.replace(/ /g, ""); 63 | 64 | // remove the xml declaration as E4X cannot parse it 65 | s = s.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); 66 | 67 | return s; 68 | }; 69 | 70 | 71 | this.encodeBase64 = function encodeBase64(input) { 72 | return base64.encode(input); 73 | } 74 | 75 | var base64 = { 76 | // private property 77 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 78 | 79 | // public method for encoding 80 | encode : function (input) { 81 | var output = ""; 82 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 83 | var i = 0; 84 | 85 | input = base64._utf8_encode(input); 86 | 87 | while (i < input.length) { 88 | 89 | chr1 = input.charCodeAt(i++); 90 | chr2 = input.charCodeAt(i++); 91 | chr3 = input.charCodeAt(i++); 92 | 93 | enc1 = chr1 >> 2; 94 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 95 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 96 | enc4 = chr3 & 63; 97 | 98 | if (isNaN(chr2)) { 99 | enc3 = enc4 = 64; 100 | } else if (isNaN(chr3)) { 101 | enc4 = 64; 102 | } 103 | 104 | output = output + 105 | base64._keyStr.charAt(enc1) + base64._keyStr.charAt(enc2) + 106 | base64._keyStr.charAt(enc3) + base64._keyStr.charAt(enc4); 107 | 108 | } 109 | 110 | return output; 111 | }, 112 | 113 | 114 | // private method for UTF-8 encoding 115 | _utf8_encode : function (string) { 116 | string = string.replace(/\r\n/g,"\n"); 117 | var utftext = ""; 118 | 119 | for (var n = 0; n < string.length; n++) { 120 | 121 | var c = string.charCodeAt(n); 122 | 123 | if (c < 128) { 124 | utftext += String.fromCharCode(c); 125 | } 126 | else if((c > 127) && (c < 2048)) { 127 | utftext += String.fromCharCode((c >> 6) | 192); 128 | utftext += String.fromCharCode((c & 63) | 128); 129 | } 130 | else { 131 | utftext += String.fromCharCode((c >> 12) | 224); 132 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 133 | utftext += String.fromCharCode((c & 63) | 128); 134 | } 135 | 136 | } 137 | 138 | return utftext; 139 | } 140 | } 141 | }; 142 | 143 | global.davlib = new function() { 144 | /* WebDAV for JavaScript 145 | 146 | This is a library containing a low-level and (if loaded, see 147 | 'davfs.js') a high-level API for working with WebDAV capable servers 148 | from JavaScript. 149 | 150 | Quick example of the low-level interface: 151 | 152 | var client = new davlib.DavClient(); 153 | client.initialize(); 154 | 155 | function alertContent(status, statusstring, content) { 156 | if (status != 200) { 157 | alert('error: ' + statusstring); 158 | return; 159 | }; 160 | alert('content received: ' + content); 161 | }; 162 | 163 | client.GET('/foo/bar.txt', alertContent); 164 | 165 | Quick example of the high-level interface: 166 | 167 | var fs = new davlib.DavFS(); 168 | fs.initialize(); 169 | 170 | function alertContent(error, content) { 171 | if (error) { 172 | alert('error: ' + error); 173 | return; 174 | }; 175 | alert('content: ' + content); 176 | }; 177 | 178 | fs.read('/foo/bar.txt', alertContent); 179 | 180 | */ 181 | var davlib = this; 182 | 183 | this.DEBUG = 0; 184 | 185 | this.STATUS_CODES = { 186 | '100': 'Continue', 187 | '101': 'Switching Protocols', 188 | '102': 'Processing', 189 | '200': 'OK', 190 | '201': 'Created', 191 | '202': 'Accepted', 192 | '203': 'None-Authoritive Information', 193 | '204': 'No Content', 194 | '205': 'Reset Content', 195 | '206': 'Partial Content', 196 | '207': 'Multi-Status', 197 | '300': 'Multiple Choices', 198 | '301': 'Moved Permanently', 199 | '302': 'Found', 200 | '303': 'See Other', 201 | '304': 'Not Modified', 202 | '305': 'Use Proxy', 203 | '307': 'Redirect', 204 | '400': 'Bad Request', 205 | '401': 'Unauthorized', 206 | '402': 'Payment Required', 207 | '403': 'Forbidden', 208 | '404': 'Not Found', 209 | '405': 'Method Not Allowed', 210 | '406': 'Not Acceptable', 211 | '407': 'Proxy Authentication Required', 212 | '408': 'Request Time-out', 213 | '409': 'Conflict', 214 | '410': 'Gone', 215 | '411': 'Length Required', 216 | '412': 'Precondition Failed', 217 | '413': 'Request Entity Too Large', 218 | '414': 'Request-URI Too Large', 219 | '415': 'Unsupported Media Type', 220 | '416': 'Requested range not satisfiable', 221 | '417': 'Expectation Failed', 222 | '422': 'Unprocessable Entity', 223 | '423': 'Locked', 224 | '424': 'Failed Dependency', 225 | '500': 'Internal Server Error', 226 | '501': 'Not Implemented', 227 | '502': 'Bad Gateway', 228 | '503': 'Service Unavailable', 229 | '504': 'Gateway Time-out', 230 | '505': 'HTTP Version not supported', 231 | '507': 'Insufficient Storage' 232 | }; 233 | 234 | this.DavClient = function() { 235 | /* Low level (subset of) WebDAV client implementation 236 | 237 | Basically what one would expect from a basic DAV client, it 238 | provides a method for every HTTP method used in basic DAV, it 239 | parses PROPFIND requests to handy JS structures and accepts 240 | similar structures for PROPPATCH. 241 | 242 | Requests are handled asynchronously, so instead of waiting until 243 | the response is sent back from the server and returning the 244 | value directly, a handler is registered that is called when 245 | the response is available and the method that sent the request 246 | is ended. For that reason all request methods accept a 'handler' 247 | argument, which will be called (with 3 arguments: statuscode, 248 | statusstring and content (the latter only where appropriate)) 249 | when the request is handled by the browser. 250 | The reason for this choice is that Mozilla sometimes freezes 251 | when using XMLHttpRequest for synchronous requests. 252 | 253 | The only 'public' methods on the class are the 'initialize' 254 | method, that needs to be called first thing after instantiating 255 | a DavClient object, and the methods that have a name similar to 256 | an HTTP method (GET, PUT, etc.). The latter all get at least a 257 | 'path' argument, a 'handler' argument and a 'context' argument: 258 | 259 | 'path' - an absolute path to the target resource 260 | 'handler' - a function or method that will be called once 261 | the request has finished (see below) 262 | 'context' - the context used to call the handler, the 263 | 'this' variable inside methods, so usually the 264 | object (instance) the handler is bound to (ignore 265 | when the handler is a function) 266 | 267 | All handlers are called with the same 3 arguments: 268 | 269 | 'status' - the HTTP status code 270 | 'statusstring' - a string representation (see STATUS_CODES 271 | array above) of the status code 272 | 'content' - can be a number of different things: 273 | * when there was an error in a method that targets 274 | a single resource, this contains the error body 275 | * when there was an error in a method that targets 276 | a set of resources (multi-status) it contains 277 | a Root object instance (see below) that contains 278 | the error messages of all the objects 279 | * if the method was GET and there was no error, it 280 | will contain the contents of the resource 281 | * if the method was PROPFIND and there was no error, 282 | it will contain a Root object (see below) that 283 | contains the properties of all the resources 284 | targeted 285 | * if there was no error and there is no content to 286 | return, it will contain null 287 | 'headers' - a mapping (associative array) from lowercase header 288 | name to value (string) 289 | 290 | Basic usage example: 291 | 292 | function handler(status, statusstring, content, headers) { 293 | if (content) { 294 | if (status != '200' && status != '204') { 295 | if (status == '207') { 296 | alert('not going to show multi-status ' + 297 | here...'); 298 | }; 299 | alert('Error: ' + statusstring); 300 | } else { 301 | alert('Content: ' + content); 302 | }; 303 | }; 304 | }; 305 | 306 | var dc = new DavClient(); 307 | dc.initialize('localhost'); 308 | 309 | // create a directory 310 | dc.MKCOL('/foo', handler); 311 | 312 | // create a file and save some contents 313 | dc.PUT('/foo/bar.txt', 'baz?', handler); 314 | 315 | // load and alert it (alert happens in the handler) 316 | dc.GET('/foo/bar.txt', handler); 317 | 318 | // delete the dir 319 | dc.DELETE('/foo', handler); 320 | 321 | For detailed information about the HTTP methods and how they 322 | can/should be used in a DAV context, see http://www.webdav.org. 323 | 324 | If you have questions, bug reports, or patches, please file a report 325 | on https://github.com/svogler/jsdavclient 326 | 327 | */ 328 | }; 329 | 330 | this.DavClient.prototype.initialize = function(host, port, protocol, username, password) { 331 | /* the 'constructor' (needs to be called explicitly!!) 332 | 333 | host - the host name or IP 334 | port - HTTP port of the host (optional, defaults to 80) 335 | protocol - protocol part of URLs (optional, defaults to http) 336 | username - the username for authorization (only Basic auth is supported at that time) 337 | password - the password to use 338 | */ 339 | this.host = host || location.hostname; 340 | this.port = port || location.port || 443; 341 | this.protocol = (protocol || location.protocol.substr(0, location.protocol.length - 1 ) || 'https'); 342 | this.username = username || null; 343 | this.password = password || null; 344 | 345 | this.request = null; 346 | }; 347 | 348 | this.DavClient.prototype.OPTIONS = function(path, handler, context) { 349 | /* perform an OPTIONS request 350 | 351 | find out which HTTP methods are understood by the server 352 | */ 353 | // XXX how does this work with * paths? 354 | var request = this._getRequest('OPTIONS', path, handler, context); 355 | request.send(''); 356 | }; 357 | 358 | this.DavClient.prototype.GET = function(path, handler, context) { 359 | /* perform a GET request 360 | 361 | retrieve the contents of a resource 362 | */ 363 | var request = this._getRequest('GET', path, handler, context); 364 | request.send(''); 365 | }; 366 | 367 | this.DavClient.prototype.PUT = function(path, content, handler, 368 | context, locktoken) { 369 | /* perform a PUT request 370 | 371 | save the contents of a resource to the server 372 | 373 | 'content' - the contents of the resource 374 | */ 375 | var request = this._getRequest('PUT', path, handler, context); 376 | request.setRequestHeader("Content-type", "text/xml,charset=UTF-8"); 377 | if (locktoken) { 378 | request.setRequestHeader('If', '<' + locktoken + '>'); 379 | }; 380 | request.send(content); 381 | }; 382 | 383 | this.DavClient.prototype.PROPFIND = function(path, handler, context, depth) { 384 | /* perform a PROPFIND request 385 | 386 | read the metadata of a resource (optionally including its children) 387 | 388 | 'depth' - control recursion depth, default 0 (only returning the 389 | properties for the resource itself) 390 | */ 391 | var request = this._getRequest('PROPFIND', path, handler, context); 392 | depth = depth || 0; 393 | request.setRequestHeader('Depth', depth); 394 | request.setRequestHeader('Content-type', 'text/xml; charset=UTF-8'); 395 | // XXX maybe we want to change this to allow getting selected props 396 | var xml = '' + 397 | '' + 398 | '' + 399 | ''; 400 | request.send(xml); 401 | }; 402 | 403 | this.DavClient.prototype.DELETE = function(path, handler, context, locktoken) { 404 | /* perform a DELETE request 405 | 406 | remove a resource (recursively) 407 | */ 408 | var request = this._getRequest('DELETE', path, handler, context); 409 | if (locktoken) { 410 | request.setRequestHeader('If', '<' + locktoken + '>'); 411 | }; 412 | //request.setRequestHeader("Depth", "Infinity"); 413 | request.send(''); 414 | }; 415 | 416 | this.DavClient.prototype.MKCOL = function(path, handler, 417 | context, locktoken) { 418 | /* perform a MKCOL request 419 | 420 | create a collection 421 | */ 422 | var request = this._getRequest('MKCOL', path, handler, context); 423 | if (locktoken) { 424 | request.setRequestHeader('If', '<' + locktoken + '>'); 425 | }; 426 | request.send(''); 427 | }; 428 | 429 | this.DavClient.prototype.COPY = function(path, topath, handler, context, overwrite, locktoken) { 430 | /* perform a COPY request 431 | 432 | create a copy of a resource 433 | 434 | 'topath' - the path to copy the resource to 435 | 'overwrite' - whether or not to fail when the resource 436 | already exists (optional) 437 | */ 438 | var request = this._getRequest('COPY', path, handler, context); 439 | var tourl = this._generateUrl(topath); 440 | request.setRequestHeader("Destination", tourl); 441 | if (overwrite) { 442 | request.setRequestHeader("Overwrite", "F"); 443 | }; 444 | if (locktoken) { 445 | request.setRequestHeader('If', '<' + locktoken + '>'); 446 | }; 447 | request.send(''); 448 | }; 449 | 450 | this.DavClient.prototype.MOVE = function(path, topath, handler, context, overwrite, locktoken) { 451 | /* perform a MOVE request 452 | 453 | move a resource from location 454 | 455 | 'topath' - the path to move the resource to 456 | 'overwrite' - whether or not to fail when the resource 457 | already exists (optional) 458 | */ 459 | var request = this._getRequest('MOVE', path, handler, context); 460 | var tourl = this._generateUrl(topath); 461 | request.setRequestHeader("Destination", tourl); 462 | if (overwrite) { 463 | request.setRequestHeader("Overwrite", "F"); 464 | }; 465 | if (locktoken) { 466 | request.setRequestHeader('If', '<' + locktoken + '>'); 467 | }; 468 | request.send(''); 469 | }; 470 | 471 | 472 | 473 | this.DavClient.prototype._getRequest = function(method, path, 474 | handler, context) { 475 | /* prepare a request */ 476 | var request = davlib.getXmlHttpRequest(); 477 | request.onreadystatechange = this._wrapHandler(handler, request, context); 478 | 479 | var url = this._generateUrl(path); 480 | request.open(method, url, true); 481 | request.setRequestHeader('Accept-Encoding', ' '); 482 | request.setRequestHeader('Authorization', this._createBasicAuth(this.username, this.password)); 483 | 484 | return request 485 | }; 486 | 487 | this.DavClient.prototype._wrapHandler = function(handler, request, context) { 488 | /* wrap the handler with a callback 489 | 490 | The callback handles multi-status parsing and calls the client's 491 | handler when done 492 | */ 493 | var self = this; 494 | 495 | function HandlerWrapper() { 496 | this.execute = function() { 497 | if (request.readyState == 4) { 498 | var status = request.status.toString(); 499 | var headers = self._parseHeaders(request.getAllResponseHeaders()); 500 | var content = request.responseText; 501 | var statusstring = davlib.STATUS_CODES[status]; 502 | handler.call(context, status, statusstring, content, headers); 503 | }; 504 | }; 505 | }; 506 | return (new HandlerWrapper().execute); 507 | }; 508 | 509 | 510 | this.DavClient.prototype._generateUrl = function(path){ 511 | /* convert a url from a path */ 512 | var url = this.protocol + '://' + this.host; 513 | if (this.port) { 514 | url += ':' + this.port; 515 | }; 516 | url += path; 517 | return url; 518 | }; 519 | 520 | 521 | this.DavClient.prototype._createBasicAuth = function (user, password) { 522 | var tok = user + ':' + password; 523 | var hash = string.encodeBase64(tok); 524 | return "Basic " + hash; 525 | } 526 | 527 | 528 | this.DavClient.prototype._parseHeaders = function(headerstring) { 529 | var lines = headerstring.split('\n'); 530 | var headers = {}; 531 | for (var i=0; i < lines.length; i++) { 532 | var line = string.strip(lines[i]); 533 | if (!line) { 534 | continue; 535 | }; 536 | var chunks = line.split(':'); 537 | var key = string.strip(chunks.shift()); 538 | var value = string.strip(chunks.join(':')); 539 | var lkey = key.toLowerCase(); 540 | if (headers[lkey] !== undefined) { 541 | if (!headers[lkey].push) { 542 | headers[lkey] = [headers[lkey, value]]; 543 | } else { 544 | headers[lkey].push(value); 545 | }; 546 | } else { 547 | headers[lkey] = value; 548 | }; 549 | }; 550 | return headers; 551 | }; 552 | 553 | // some helper functions 554 | this.getXmlHttpRequest = function() { 555 | /* instantiate an XMLHTTPRequest 556 | 557 | this can be improved by testing the user agent better and, in case 558 | of IE, finding out which MSXML is installed and such, but it 559 | seems to work just fine for now 560 | */ 561 | try{ 562 | return new XMLHttpRequest(); 563 | } catch(e) { 564 | // not a Mozilla or Konqueror based browser 565 | }; 566 | try { 567 | return new ActiveXObject('Microsoft.XMLHTTP'); 568 | } catch(e) { 569 | // not IE either... 570 | }; 571 | alert('Your browser does not support XMLHttpRequest, required for ' + 572 | 'WebDAV access.'); 573 | throw('Browser not supported'); 574 | }; 575 | 576 | this.debug = function(text) { 577 | /* simple debug function 578 | 579 | set the DEBUG global to some true value, and messages will appear 580 | on the bottom of the document 581 | */ 582 | if (!davlib.DEBUG) { 583 | return; 584 | }; 585 | var div = document.createElement('div'); 586 | var text = document.createTextNode(text); 587 | div.appendChild(text); 588 | document.getElementsByTagName('body')[0].appendChild(div); 589 | }; 590 | }(); 591 | 592 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebDAV library test 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /test_client.js: -------------------------------------------------------------------------------- 1 | var client = new davlib.DavClient(); 2 | client.initialize(location.hostname, 443, 'https', 'user', 'password'); 3 | 4 | 5 | function writeToDiv(line, emphasize) { 6 | var div = document.getElementById('testdiv'); 7 | var textnode = document.createTextNode(line); 8 | var newdiv = document.createElement('div'); 9 | newdiv.appendChild(textnode); 10 | if (emphasize) { 11 | newdiv.style.color = 'red'; 12 | } else { 13 | newdiv.style.color = 'blue'; 14 | }; 15 | div.appendChild(newdiv); 16 | }; 17 | 18 | function assert(statement, debugprint) { 19 | if (!statement) { 20 | writeToDiv('FAILURE: ' + debugprint, 1); 21 | } else { 22 | writeToDiv('success'); 23 | }; 24 | }; 25 | 26 | // since the lib is async I wrote the functions in the order 27 | // they are executed to give a bit of an overview 28 | function runTests() { 29 | testMakeDir(); 30 | }; 31 | 32 | function wrapContinueHandler(currname, handler, expected_status) { 33 | var wrapped = function(status, statusstr, content) { 34 | writeToDiv('status: ' + status + ' (' + statusstr + ')'); 35 | if (content) { 36 | writeToDiv('content: ' + content); 37 | }; 38 | 39 | // multistatus request 40 | if (content && status == 207) { 41 | 42 | var parser, doc = null; 43 | if (window.DOMParser) { 44 | parser = new DOMParser(); 45 | doc = parser.parseFromString(string.deentitize(content), "application/xml"); 46 | } else { // Internet Explorer :-) 47 | doc = new ActiveXObject("Microsoft.XMLDOM"); 48 | doc.loadXML(content); 49 | } 50 | 51 | writeToDiv('Files found:'); 52 | 53 | // list files 54 | for (i = 0; i< doc.getElementsByTagName("response").length; i++) { 55 | // property wrapper for IE (property "text") + Rest (property "textcontent") 56 | // alternative: use jquery for wrapping 57 | writeToDiv(doc.getElementsByTagName("response")[i].firstChild.textContent || doc.getElementsByTagName("response")[i].firstChild.text); 58 | } 59 | }; 60 | 61 | writeToDiv('Expected status: ' + expected_status); 62 | 63 | if (status == expected_status) { 64 | writeToDiv('OK'); 65 | } else { 66 | writeToDiv('FAILED', true); 67 | }; 68 | writeToDiv('--------------------'); 69 | 70 | handler(); 71 | }; 72 | 73 | return wrapped; 74 | }; 75 | 76 | var basedir = '/on/demandware.servlet/webdav/Sites/Cartridges/version2/'; 77 | var folder1 = 'foo/'; 78 | var folder2 = 'bar/'; 79 | var file = 'bar.txt' 80 | 81 | function testMakeDir() { 82 | writeToDiv('Going to create dir ' + basedir + folder1); 83 | client.MKCOL(basedir + folder1, wrapContinueHandler('make dir', testMove, 201)); 84 | }; 85 | 86 | function testMove() { 87 | writeToDiv('Going to move ' + basedir + folder1 + ' to ' + basedir + folder2); 88 | client.MOVE(basedir + folder1, basedir + folder2, wrapContinueHandler('move dir', testCopy, 201)); 89 | }; 90 | 91 | function testCopy() { 92 | writeToDiv('Going to copy ' + basedir + folder2 + ' to ' + basedir + folder1); 93 | client.COPY(basedir + folder2, basedir + folder1, wrapContinueHandler('copy dir', testDeleteDir, 201)); 94 | }; 95 | 96 | function testDeleteDir() { 97 | writeToDiv('Going to delete dir ' + basedir + folder2); 98 | client.DELETE(basedir + folder2, wrapContinueHandler('delete dir', testReadFile1, 204)); 99 | }; 100 | 101 | function testReadFile1() { 102 | writeToDiv('Going to read file ' + basedir + folder1 + file); 103 | client.GET(basedir + folder1 + file, wrapContinueHandler('read file', testWriteFile1, 404)); 104 | }; 105 | 106 | function testWriteFile1() { 107 | writeToDiv('Going to create file ' + basedir + folder1 + file); 108 | client.PUT(basedir + folder1 + file, 'foo', wrapContinueHandler('create file', testReadFile2, 201)); 109 | }; 110 | 111 | function testReadFile2() { 112 | writeToDiv('Going to read file ' + basedir + folder1 + file); 113 | client.GET(basedir + folder1 + file, wrapContinueHandler('read file', testReadDir, 200)); 114 | }; 115 | 116 | function testReadDir() { 117 | writeToDiv('Going to read directory ' + basedir + folder1); 118 | client.PROPFIND(basedir + folder1, wrapContinueHandler('read file', testDelete, 207), this, 1); 119 | }; 120 | 121 | function testDelete() { 122 | writeToDiv('Going to delete file ' + basedir + folder1 + file); 123 | client.DELETE(basedir + folder1 + file, wrapContinueHandler('delete dir', testDelete2, 204)); 124 | }; 125 | 126 | function testDelete2() { 127 | writeToDiv('Going to delete dir ' + basedir + folder1); 128 | client.DELETE(basedir + folder1, wrapContinueHandler('delete dir', finish, 204)); 129 | }; 130 | 131 | function finish() { 132 | writeToDiv('Finished'); 133 | }; 134 | 135 | --------------------------------------------------------------------------------