1 /* 2 * jQuery CURIE @VERSION 3 * 4 * Copyright (c) 2008,2009 Jeni Tennison 5 * Licensed under the MIT (MIT-LICENSE.txt) 6 * 7 * Depends: 8 * jquery.uri.js 9 */ 10 /** 11 * @fileOverview XML Schema datatype handling 12 * @author <a href="mailto:jeni@jenitennison.com">Jeni Tennison</a> 13 * @copyright (c) 2008,2009 Jeni Tennison 14 * @license MIT license (MIT-LICENSE.txt) 15 * @version 1.0 16 * @requires jquery.uri.js 17 */ 18 19 (function ($) { 20 21 var strip = function (value) { 22 return value.replace(/[ \t\n\r]+/, ' ').replace(/^ +/, '').replace(/ +$/, ''); 23 }; 24 25 /** 26 * Creates a new jQuery.typedValue object. This should be invoked as a method 27 * rather than constructed using new. 28 * @class Represents a value with an XML Schema datatype 29 * @param {String} value The string representation of the value 30 * @param {String} datatype The XML Schema datatype URI 31 * @returns {jQuery.typedValue} 32 * @example intValue = jQuery.typedValue('42', 'http://www.w3.org/2001/XMLSchema#integer'); 33 */ 34 $.typedValue = function (value, datatype) { 35 return $.typedValue.fn.init(value, datatype); 36 }; 37 38 $.typedValue.fn = $.typedValue.prototype = { 39 /** 40 * The string representation of the value 41 * @memberOf jQuery.typedValue# 42 */ 43 representation: undefined, 44 /** 45 * The value as an object. The type of the object will 46 * depend on the XML Schema datatype URI specified 47 * in the constructor. The following table lists the mappings 48 * currently supported: 49 * <table> 50 * <tr> 51 * <th>XML Schema Datatype</th> 52 * <th>Value type</th> 53 * </tr> 54 * <tr> 55 * <td>http://www.w3.org/2001/XMLSchema#string</td> 56 * <td>string</td> 57 * </tr> 58 * <tr> 59 * <td>http://www.w3.org/2001/XMLSchema#boolean</td> 60 * <td>bool</td> 61 * </tr> 62 * <tr> 63 * <td>http://www.w3.org/2001/XMLSchema#decimal</td> 64 * <td>string</td> 65 * </tr> 66 * <tr> 67 * <td>http://www.w3.org/2001/XMLSchema#integer</td> 68 * <td>int</td> 69 * </tr> 70 * <tr> 71 * <td>http://www.w3.org/2001/XMLSchema#int</td> 72 * <td>int</td> 73 * </tr> 74 * <tr> 75 * <td>http://www.w3.org/2001/XMLSchema#float</td> 76 * <td>float</td> 77 * </tr> 78 * <tr> 79 * <td>http://www.w3.org/2001/XMLSchema#double</td> 80 * <td>float</td> 81 * </tr> 82 * <tr> 83 * <td>http://www.w3.org/2001/XMLSchema#dateTime</td> 84 * <td>string</td> 85 * </tr> 86 * <tr> 87 * <td>http://www.w3.org/2001/XMLSchema#date</td> 88 * <td>string</td> 89 * </tr> 90 * <tr> 91 * <td>http://www.w3.org/2001/XMLSchema#gMonthDay</td> 92 * <td>string</td> 93 * </tr> 94 * <tr> 95 * <td>http://www.w3.org/2001/XMLSchema#anyURI</td> 96 * <td>string</td> 97 * </tr> 98 * </table> 99 * @memberOf jQuery.typedValue# 100 */ 101 value: undefined, 102 /** 103 * The XML Schema datatype URI for the value's datatype 104 * @memberOf jQuery.typedValue# 105 */ 106 datatype: undefined, 107 108 init: function (value, datatype) { 109 var d; 110 if ($.typedValue.valid(value, datatype)) { 111 d = $.typedValue.types[datatype]; 112 this.representation = value; 113 this.datatype = datatype; 114 this.value = d.value(d.strip ? strip(value) : value); 115 return this; 116 } else { 117 throw { 118 name: 'InvalidValue', 119 message: value + ' is not a valid ' + datatype + ' value' 120 }; 121 } 122 } 123 }; 124 125 $.typedValue.fn.init.prototype = $.typedValue.fn; 126 127 /** 128 * An object that holds the datatypes supported by the script. The properties of this object are the URIs of the datatypes, and each datatype has four properties: 129 * <dl> 130 * <dt>strip</dt> 131 * <dd>A boolean value that indicates whether whitespace should be stripped from the value prior to testing against the regular expression or passing to the value function.</dd> 132 * <dt>regex</dt> 133 * <dd>A regular expression that valid values of the type must match.</dd> 134 * <dt>validate</dt> 135 * <dd>Optional. A function that performs further testing on the value.</dd> 136 * <dt>value</dt> 137 * <dd>A function that returns a Javascript object equivalent for the value.</dd> 138 * </dl> 139 * You can add to this object as necessary for your own datatypes, and {@link jQuery.typedValue} and {@link jQuery.typedValue.valid} will work with them. 140 * @see jQuery.typedValue 141 * @see jQuery.typedValue.valid 142 */ 143 $.typedValue.types = {}; 144 145 $.typedValue.types['http://www.w3.org/2001/XMLSchema#string'] = { 146 regex: /^.*$/, 147 strip: false, 148 /** @ignore */ 149 value: function (v) { 150 return v; 151 } 152 }; 153 154 $.typedValue.types['http://www.w3.org/2001/XMLSchema#boolean'] = { 155 regex: /^(?:true|false|1|0)$/, 156 strip: true, 157 /** @ignore */ 158 value: function (v) { 159 return v === 'true' || v === '1'; 160 } 161 }; 162 163 $.typedValue.types['http://www.w3.org/2001/XMLSchema#decimal'] = { 164 regex: /^[\-\+]?(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)$/, 165 strip: true, 166 /** @ignore */ 167 value: function (v) { 168 return v; 169 } 170 }; 171 172 $.typedValue.types['http://www.w3.org/2001/XMLSchema#integer'] = { 173 regex: /^[\-\+]?[0-9]+$/, 174 strip: true, 175 /** @ignore */ 176 value: function (v) { 177 return parseInt(v, 10); 178 } 179 }; 180 181 $.typedValue.types['http://www.w3.org/2001/XMLSchema#int'] = { 182 regex: /^[\-\+]?[0-9]+$/, 183 strip: true, 184 /** @ignore */ 185 value: function (v) { 186 return parseInt(v, 10); 187 } 188 }; 189 190 $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'] = { 191 regex: /^(?:[\-\+]?(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)(?:[eE][\-\+]?[0-9]+)?|[\-\+]?INF|NaN)$/, 192 strip: true, 193 /** @ignore */ 194 value: function (v) { 195 if (v === '-INF') { 196 return -1 / 0; 197 } else if (v === 'INF' || v === '+INF') { 198 return 1 / 0; 199 } else { 200 return parseFloat(v); 201 } 202 } 203 }; 204 205 $.typedValue.types['http://www.w3.org/2001/XMLSchema#double'] = { 206 regex: $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'].regex, 207 strip: true, 208 value: $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'].value 209 }; 210 211 $.typedValue.types['http://www.w3.org/2001/XMLSchema#duration'] = { 212 regex: /^([\-\+])?P(?:([0-9]+)Y)?(?:([0-9]+)M)?(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+(?:\.[0-9]+))?S)?)$/, 213 /** @ignore */ 214 validate: function (v) { 215 var m = this.regex.exec(v); 216 return m[2] || m[3] || m[4] || m[5] || m[6] || m[7]; 217 }, 218 strip: true, 219 /** @ignore */ 220 value: function (v) { 221 return v; 222 } 223 }; 224 225 $.typedValue.types['http://www.w3.org/2001/XMLSchema#dateTime'] = { 226 regex: /^(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):(([0-9]{2})(\.([0-9]+))?)((?:[\-\+]([0-9]{2}):([0-9]{2}))|Z)?$/, 227 /** @ignore */ 228 validate: function (v) { 229 var 230 m = this.regex.exec(v), 231 year = parseInt(m[1], 10), 232 tz = m[10] === undefined || m[10] === 'Z' ? '+0000' : m[10].replace(/:/, ''), 233 date; 234 if (year === 0 || 235 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 236 return false; 237 } 238 try { 239 year = year < 100 ? Math.abs(year) + 1000 : year; 240 month = parseInt(m[2], 10); 241 day = parseInt(m[3], 10); 242 if (day > 31) { 243 return false; 244 } else if (day > 30 && !(month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12)) { 245 return false; 246 } else if (month === 2) { 247 if (day > 29) { 248 return false; 249 } else if (day === 29 && (year % 4 !== 0 || (year % 100 === 0 && year % 400 !== 0))) { 250 return false; 251 } 252 } 253 date = '' + year + '/' + m[2] + '/' + m[3] + ' ' + m[4] + ':' + m[5] + ':' + m[7] + ' ' + tz; 254 date = new Date(date); 255 return true; 256 } catch (e) { 257 return false; 258 } 259 }, 260 strip: true, 261 /** @ignore */ 262 value: function (v) { 263 return v; 264 } 265 }; 266 267 $.typedValue.types['http://www.w3.org/2001/XMLSchema#date'] = { 268 regex: /^(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})((?:[\-\+]([0-9]{2}):([0-9]{2}))|Z)?$/, 269 /** @ignore */ 270 validate: function (v) { 271 var 272 m = this.regex.exec(v), 273 year = parseInt(m[1], 10), 274 month = parseInt(m[2], 10), 275 day = parseInt(m[3], 10), 276 tz = m[10] === undefined || m[10] === 'Z' ? '+0000' : m[10].replace(/:/, ''); 277 if (year === 0 || 278 month > 12 || 279 day > 31 || 280 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 281 return false; 282 } else { 283 return true; 284 } 285 }, 286 strip: true, 287 /** @ignore */ 288 value: function (v) { 289 return v; 290 } 291 }; 292 293 $.typedValue.types['http://www.w3.org/2001/XMLSchema#gMonthDay'] = { 294 regex: /^--([0-9]{2})-([0-9]{2})((?:[\-\+]([0-9]{2}):([0-9]{2}))|Z)?$/, 295 /** @ignore */ 296 validate: function (v) { 297 var 298 m = this.regex.exec(v), 299 month = parseInt(m[1], 10), 300 day = parseInt(m[2], 10), 301 tz = m[3] === undefined || m[3] === 'Z' ? '+0000' : m[3].replace(/:/, ''); 302 if (month > 12 || 303 day > 31 || 304 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 305 return false; 306 } else if (month === 2 && day > 29) { 307 return false; 308 } else if ((month === 4 || month === 6 || month === 9 || month === 11) && day > 30) { 309 return false; 310 } else { 311 return true; 312 } 313 }, 314 strip: true, 315 /** @ignore */ 316 value: function (v) { 317 return v; 318 } 319 }; 320 321 $.typedValue.types['http://www.w3.org/2001/XMLSchema#anyURI'] = { 322 regex: /^.*$/, 323 strip: true, 324 /** @ignore */ 325 value: function (v, options) { 326 var opts = $.extend({}, $.typedValue.defaults, options); 327 return $.uri.resolve(v, opts.base); 328 } 329 }; 330 331 $.typedValue.defaults = { 332 base: $.uri.base(), 333 namespaces: {} 334 }; 335 336 /** 337 * Checks whether a value is valid according to a given datatype. The datatype must be held in the {@link jQuery.typedValue.types} object. 338 * @param {String} value The value to validate. 339 * @param {String} datatype The URI for the datatype against which the value will be validated. 340 * @returns {boolean} True if the value is valid. 341 * @throws {String} Errors if the datatype has not been specified in the {@link jQuery.typedValue.types} object. 342 * @example validDate = $.typedValue.valid(date, 'http://www.w3.org/2001/XMLSchema#date'); 343 */ 344 $.typedValue.valid = function (value, datatype) { 345 var d = $.typedValue.types[datatype]; 346 if (d === undefined) { 347 throw "InvalidDatatype: The datatype " + datatype + " can't be recognised"; 348 } else { 349 value = d.strip ? strip(value) : value; 350 if (d.regex.test(value)) { 351 return d.validate === undefined ? true : d.validate(value); 352 } else { 353 return false; 354 } 355 } 356 }; 357 358 })(jQuery); 359