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.Property is already defined, we have a namespace clash! 22 if (nl.sara.webdav.Property !== undefined) { 23 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); 24 } 25 26 /** 27 * @class a WebDAV property 28 * 29 * @param {Node} [xmlNode] Optional; The xmlNode describing the propstat object (should be compliant with RFC 4918) 30 * @param {Number} [status] Optional; The (HTTP) status code 31 * @param {String} [responsedescription] Optional; The response description 32 * @param {String[]} [errors] Optional; An array of errors 33 * @property {String} namespace The namespace 34 * @property {String} tagname The tag name 35 * @property {NodeList} xmlvalue A NodeList with the value of this property 36 * @property {Number} status The (HTTP) status code 37 * @property {String} responsedescription The response description 38 */ 39 nl.sara.webdav.Property = function(xmlNode, status, responsedescription, errors) { 40 // First define private attributes 41 Object.defineProperty(this, '_xmlvalue', { 42 'value': null, 43 'enumerable': false, 44 'configurable': false, 45 'writable': true 46 }); 47 Object.defineProperty(this, '_errors', { 48 'value': [], 49 'enumerable': false, 50 'configurable': false, 51 'writable': true 52 }); 53 // Second define public attributes 54 Object.defineProperty(this, 'namespace', { 55 'value': null, 56 'enumerable': true, 57 'configurable': false, 58 'writable': true 59 }); 60 Object.defineProperty(this, 'tagname', { 61 'value': null, 62 'enumerable': true, 63 'configurable': false, 64 'writable': true 65 }); 66 Object.defineProperty(this, 'status', { 67 'value': null, 68 'enumerable': true, 69 'configurable': false, 70 'writable': true 71 }); 72 Object.defineProperty(this, 'responsedescription', { 73 'value': null, 74 'enumerable': true, 75 'configurable': false, 76 'writable': true 77 }); 78 79 // Constructor logic 80 if ((typeof xmlNode !== 'undefined') && (xmlNode.nodeType === 1)) { 81 this.namespace = xmlNode.namespaceURI; 82 this.tagname = nl.sara.webdav.Ie.getLocalName(xmlNode); 83 this.xmlvalue = xmlNode.childNodes; 84 } 85 if (status !== undefined) { 86 this.status = status; 87 } 88 if (responsedescription !== undefined) { 89 this.responsedescription = responsedescription; 90 } 91 if (errors instanceof Array) { 92 for (var i = 0; i < errors.length; i++) { 93 this.addError(errors[i]); 94 } 95 } 96 }; 97 98 //######################### DEFINE PUBLIC ATTRIBUTES ########################### 99 (function() { 100 // This creates a (private) static variable. It will contain all codecs 101 var codecNamespaces = {}; 102 103 Object.defineProperty(nl.sara.webdav.Property.prototype, 'xmlvalue', { 104 'set': function(value) { 105 if (value === null) { 106 this._xmlvalue = null; 107 return; 108 } 109 if (!nl.sara.webdav.Ie.isIE && !(value instanceof NodeList)) { 110 throw new nl.sara.webdav.Exception('xmlvalue must be an instance of NodeList', nl.sara.webdav.Exception.WRONG_TYPE); 111 } 112 this._xmlvalue = value; 113 }, 114 'get': function() { 115 return this._xmlvalue; 116 } 117 }); 118 119 //########################## DEFINE PUBLIC METHODS ############################# 120 /** 121 * Adds functions to encode or decode properties 122 * 123 * This allows exact control in how Property.xmlvalue is parsed when 124 * getParsedValue() is called or how it is rebuild when 125 * setValueAndRebuildXml() is called. You can specify two functions: 'fromXML' 126 * and 'toXML'. These should be complementary. That is, toXML should be able 127 * to create a NodeList based on the output of fromXML. For example: 128 * A == toXML(fromXML(A)) && 129 * B == fromXML(toXML(B)) 130 * 131 * @param {nl.sara.webdav.Codec} codec The codec to add 132 * @returns {void} 133 */ 134 nl.sara.webdav.Property.addCodec = function(codec) { 135 if (typeof codec.namespace !== 'string') { 136 throw new nl.sara.webdav.Exception('addCodec: codec.namespace must be a String', nl.sara.webdav.Exception.WRONG_TYPE); 137 } 138 if (typeof codec.tagname !== 'string') { 139 throw new nl.sara.webdav.Exception('addCodec: codec.tagname must be a String', nl.sara.webdav.Exception.WRONG_TYPE); 140 } 141 if (codecNamespaces[codec.namespace] === undefined) { 142 codecNamespaces[codec.namespace] = {}; 143 } 144 codecNamespaces[codec.namespace][codec.tagname] = { 145 'fromXML': (codec.fromXML ? codec.fromXML : undefined), 146 'toXML': (codec.toXML ? codec.toXML : undefined) 147 }; 148 }; 149 150 /** 151 * Sets a new value and rebuilds xmlvalue 152 * 153 * If a codec for this property is specified, it will use this codec to 154 * rebuild xmlvallue. Else it will attempt to create one CDATA element with 155 * the string value of whatever was fiven as parameter. 156 * 157 * @param {mixed} value The object to base the xmlvalue on 158 * @return {void} 159 */ 160 nl.sara.webdav.Property.prototype.setValueAndRebuildXml = function(value) { 161 // Call codec to automatically create correct 'xmlvalue' 162 var xmlDoc = document.implementation.createDocument(this.namespace, this.tagname, null); 163 if ((codecNamespaces[this.namespace] === undefined) || 164 (codecNamespaces[this.namespace][this.tagname] === undefined) || 165 (codecNamespaces[this.namespace][this.tagname]['toXML'] === undefined)) { 166 // No 'toXML' function set, so create a NodeList with just one CDATA node 167 if (value !== null) { // If the value is NULL, then we should add anything to the NodeList 168 var cdata = xmlDoc.createCDATASection(value); 169 xmlDoc.documentElement.appendChild(cdata); 170 } 171 this._xmlvalue = xmlDoc.documentElement.childNodes; 172 }else{ 173 this._xmlvalue = codecNamespaces[this.namespace][this.tagname]['toXML'](value, xmlDoc).documentElement.childNodes; 174 } 175 }; 176 177 /** 178 * Parses the xmlvalue 179 * 180 * If a codec for this property is specified, it will use this codec to parse 181 * the XML nodes. Else it will attempt to parse it as text or CDATA elements. 182 * 183 * @return {mixed} If a codec is specified, the return type depends on that 184 * codec. If no codec is specified and at least one node in xmlvalue is not a 185 * text or CDATA node, it will return undefined. If xmlvalue is empty, it will 186 * return null. 187 */ 188 nl.sara.webdav.Property.prototype.getParsedValue = function() { 189 // Call codec to automatically create correct 'value' 190 if (this._xmlvalue.length > 0) { 191 if ((codecNamespaces[this.namespace] === undefined) || 192 (codecNamespaces[this.namespace][this.tagname] === undefined) || 193 (codecNamespaces[this.namespace][this.tagname]['fromXML'] === undefined)) { 194 // 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 195 var parsedValue = ''; 196 for (var i = 0; i < this._xmlvalue.length; i++) { 197 var node = this._xmlvalue.item(i); 198 if ((node.nodeType === 3) || (node.nodeType === 4)) { // Make sure text and CDATA content is stored 199 parsedValue += node.nodeValue; 200 }else{ // If even one of the nodes is not text or CDATA, then we don't parse a text value at all 201 parsedValue = undefined; 202 break; 203 } 204 } 205 return parsedValue; 206 }else{ 207 return codecNamespaces[this.namespace][this.tagname]['fromXML'](this._xmlvalue); 208 } 209 } 210 return null; 211 }; 212 })(); // Ends the static scope 213 214 /** 215 * Adds an error to this property 216 * 217 * @param {Node} error The Node which represents the error 218 * @returns {Property} Itself for chaining multiple methods 219 */ 220 nl.sara.webdav.Property.prototype.addError = function(error) { 221 if (!nl.sara.webdav.Ie.isIE && !(error instanceof Node)) { 222 throw new nl.sara.webdav.Exception('Error must be an instance of Node', nl.sara.webdav.Exception.WRONG_TYPE); 223 } 224 this._errors.push(error); 225 return this; 226 }; 227 228 /** 229 * Returns all errors 230 * 231 * @returns {array} An array of Node representing the error 232 */ 233 nl.sara.webdav.Property.prototype.getErrors = function() { 234 return this._errors; 235 }; 236 237 /** 238 * Overloads the default toString() method so it returns the value of this property 239 * 240 * @returns {String} A string representation of this property 241 */ 242 nl.sara.webdav.Property.prototype.toString = function() { 243 return this.getParsedValue(); 244 }; 245 246 // End of Property 247