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#token</td> 60 * <td>string</td> 61 * </tr> 62 * <tr> 63 * <td>http://www.w3.org/2001/XMLSchema#NCName</td> 64 * <td>string</td> 65 * </tr> 66 * <tr> 67 * <td>http://www.w3.org/2001/XMLSchema#boolean</td> 68 * <td>bool</td> 69 * </tr> 70 * <tr> 71 * <td>http://www.w3.org/2001/XMLSchema#decimal</td> 72 * <td>string</td> 73 * </tr> 74 * <tr> 75 * <td>http://www.w3.org/2001/XMLSchema#integer</td> 76 * <td>int</td> 77 * </tr> 78 * <tr> 79 * <td>http://www.w3.org/2001/XMLSchema#int</td> 80 * <td>int</td> 81 * </tr> 82 * <tr> 83 * <td>http://www.w3.org/2001/XMLSchema#float</td> 84 * <td>float</td> 85 * </tr> 86 * <tr> 87 * <td>http://www.w3.org/2001/XMLSchema#double</td> 88 * <td>float</td> 89 * </tr> 90 * <tr> 91 * <td>http://www.w3.org/2001/XMLSchema#dateTime</td> 92 * <td>string</td> 93 * </tr> 94 * <tr> 95 * <td>http://www.w3.org/2001/XMLSchema#date</td> 96 * <td>string</td> 97 * </tr> 98 * <tr> 99 * <td>http://www.w3.org/2001/XMLSchema#gYear</td> 100 * <td>int</td> 101 * </tr> 102 * <tr> 103 * <td>http://www.w3.org/2001/XMLSchema#gMonthDay</td> 104 * <td>string</td> 105 * </tr> 106 * <tr> 107 * <td>http://www.w3.org/2001/XMLSchema#anyURI</td> 108 * <td>{@link jQuery.uri}</td> 109 * </tr> 110 * </table> 111 * @memberOf jQuery.typedValue# 112 */ 113 value: undefined, 114 /** 115 * The XML Schema datatype URI for the value's datatype 116 * @memberOf jQuery.typedValue# 117 */ 118 datatype: undefined, 119 120 init: function (value, datatype) { 121 var d = $.typedValue.types[datatype]; 122 if ($.typedValue.valid(value, datatype)) { 123 this.representation = value; 124 this.datatype = datatype; 125 this.value = d === undefined ? strip(value) : d.value(d.strip ? strip(value) : value); 126 return this; 127 } else { 128 throw { 129 name: 'InvalidValue', 130 message: value + ' is not a valid ' + datatype + ' value' 131 }; 132 } 133 } 134 }; 135 136 $.typedValue.fn.init.prototype = $.typedValue.fn; 137 138 /** 139 * 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: 140 * <dl> 141 * <dt>strip</dt> 142 * <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> 143 * <dt>regex</dt> 144 * <dd>A regular expression that valid values of the type must match.</dd> 145 * <dt>validate</dt> 146 * <dd>Optional. A function that performs further testing on the value.</dd> 147 * <dt>value</dt> 148 * <dd>A function that returns a Javascript object equivalent for the value.</dd> 149 * </dl> 150 * 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. 151 * @see jQuery.typedValue 152 * @see jQuery.typedValue.valid 153 */ 154 $.typedValue.types = {}; 155 156 $.typedValue.types['http://www.w3.org/2001/XMLSchema#string'] = { 157 regex: /^.*$/, 158 strip: false, 159 /** @ignore */ 160 value: function (v) { 161 return v; 162 } 163 }; 164 165 $.typedValue.types['http://www.w3.org/2001/XMLSchema#token'] = { 166 regex: /^.*$/, 167 strip: true, 168 /** @ignore */ 169 value: function (v) { 170 return strip(v); 171 } 172 }; 173 174 $.typedValue.types['http://www.w3.org/2001/XMLSchema#NCName'] = { 175 regex: /^[a-z_][-\.a-z0-9]+$/i, 176 strip: true, 177 /** @ignore */ 178 value: function (v) { 179 return strip(v); 180 } 181 }; 182 183 $.typedValue.types['http://www.w3.org/2001/XMLSchema#boolean'] = { 184 regex: /^(?:true|false|1|0)$/, 185 strip: true, 186 /** @ignore */ 187 value: function (v) { 188 return v === 'true' || v === '1'; 189 } 190 }; 191 192 $.typedValue.types['http://www.w3.org/2001/XMLSchema#decimal'] = { 193 regex: /^[\-\+]?(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)$/, 194 strip: true, 195 /** @ignore */ 196 value: function (v) { 197 v = v.replace(/^0+/, '') 198 .replace(/0+$/, ''); 199 if (v === '') { 200 v = '0.0'; 201 } 202 if (v.substring(0, 1) === '.') { 203 v = '0' + v; 204 } 205 if (/\.$/.test(v)) { 206 v = v + '0'; 207 } else if (!/\./.test(v)) { 208 v = v + '.0'; 209 } 210 return v; 211 } 212 }; 213 214 $.typedValue.types['http://www.w3.org/2001/XMLSchema#integer'] = { 215 regex: /^[\-\+]?[0-9]+$/, 216 strip: true, 217 /** @ignore */ 218 value: function (v) { 219 return parseInt(v, 10); 220 } 221 }; 222 223 $.typedValue.types['http://www.w3.org/2001/XMLSchema#int'] = { 224 regex: /^[\-\+]?[0-9]+$/, 225 strip: true, 226 /** @ignore */ 227 value: function (v) { 228 return parseInt(v, 10); 229 } 230 }; 231 232 $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'] = { 233 regex: /^(?:[\-\+]?(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)(?:[eE][\-\+]?[0-9]+)?|[\-\+]?INF|NaN)$/, 234 strip: true, 235 /** @ignore */ 236 value: function (v) { 237 if (v === '-INF') { 238 return -1 / 0; 239 } else if (v === 'INF' || v === '+INF') { 240 return 1 / 0; 241 } else { 242 return parseFloat(v); 243 } 244 } 245 }; 246 247 $.typedValue.types['http://www.w3.org/2001/XMLSchema#double'] = { 248 regex: $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'].regex, 249 strip: true, 250 value: $.typedValue.types['http://www.w3.org/2001/XMLSchema#float'].value 251 }; 252 253 $.typedValue.types['http://www.w3.org/2001/XMLSchema#duration'] = { 254 regex: /^([\-\+])?P(?:([0-9]+)Y)?(?:([0-9]+)M)?(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+(?:\.[0-9]+)?)?S)?)$/, 255 /** @ignore */ 256 validate: function (v) { 257 var m = this.regex.exec(v); 258 return m[2] || m[3] || m[4] || m[5] || m[6] || m[7]; 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#yearMonthDuration'] = { 268 regex: /^([\-\+])?P(?:([0-9]+)Y)?(?:([0-9]+)M)?$/, 269 /** @ignore */ 270 validate: function (v) { 271 var m = this.regex.exec(v); 272 return m[2] || m[3]; 273 }, 274 strip: true, 275 /** @ignore */ 276 value: function (v) { 277 var m = this.regex.exec(v), 278 years = m[2] || 0, 279 months = m[3] || 0; 280 months += years * 12; 281 return m[1] === '-' ? -1 * months : months; 282 } 283 }; 284 285 $.typedValue.types['http://www.w3.org/2001/XMLSchema#dateTime'] = { 286 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)?$/, 287 /** @ignore */ 288 validate: function (v) { 289 var 290 m = this.regex.exec(v), 291 year = parseInt(m[1], 10), 292 tz = m[10] === undefined || m[10] === 'Z' ? '+0000' : m[10].replace(/:/, ''), 293 date; 294 if (year === 0 || 295 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 296 return false; 297 } 298 try { 299 year = year < 100 ? Math.abs(year) + 1000 : year; 300 month = parseInt(m[2], 10); 301 day = parseInt(m[3], 10); 302 if (day > 31) { 303 return false; 304 } else if (day > 30 && !(month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12)) { 305 return false; 306 } else if (month === 2) { 307 if (day > 29) { 308 return false; 309 } else if (day === 29 && (year % 4 !== 0 || (year % 100 === 0 && year % 400 !== 0))) { 310 return false; 311 } 312 } 313 date = '' + year + '/' + m[2] + '/' + m[3] + ' ' + m[4] + ':' + m[5] + ':' + m[7] + ' ' + tz; 314 date = new Date(date); 315 return true; 316 } catch (e) { 317 return false; 318 } 319 }, 320 strip: true, 321 /** @ignore */ 322 value: function (v) { 323 return v; 324 } 325 }; 326 327 $.typedValue.types['http://www.w3.org/2001/XMLSchema#date'] = { 328 regex: /^(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})((?:[\-\+]([0-9]{2}):([0-9]{2}))|Z)?$/, 329 /** @ignore */ 330 validate: function (v) { 331 var 332 m = this.regex.exec(v), 333 year = parseInt(m[1], 10), 334 month = parseInt(m[2], 10), 335 day = parseInt(m[3], 10), 336 tz = m[10] === undefined || m[10] === 'Z' ? '+0000' : m[10].replace(/:/, ''); 337 if (year === 0 || 338 month > 12 || 339 day > 31 || 340 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 341 return false; 342 } else { 343 return true; 344 } 345 }, 346 strip: true, 347 /** @ignore */ 348 value: function (v) { 349 return v; 350 } 351 }; 352 353 $.typedValue.types['http://www.w3.org/2001/XMLSchema#gYear'] = { 354 regex: /^-?([0-9]{4,})$/, 355 /** @ignore */ 356 validate: function (v) { 357 var i = parseInt(v, 10); 358 return i !== 0; 359 }, 360 strip: true, 361 /** @ignore */ 362 value: function (v) { 363 return parseInt(v, 10); 364 } 365 }; 366 367 $.typedValue.types['http://www.w3.org/2001/XMLSchema#gMonthDay'] = { 368 regex: /^--([0-9]{2})-([0-9]{2})((?:[\-\+]([0-9]{2}):([0-9]{2}))|Z)?$/, 369 /** @ignore */ 370 validate: function (v) { 371 var 372 m = this.regex.exec(v), 373 month = parseInt(m[1], 10), 374 day = parseInt(m[2], 10), 375 tz = m[3] === undefined || m[3] === 'Z' ? '+0000' : m[3].replace(/:/, ''); 376 if (month > 12 || 377 day > 31 || 378 parseInt(tz, 10) < -1400 || parseInt(tz, 10) > 1400) { 379 return false; 380 } else if (month === 2 && day > 29) { 381 return false; 382 } else if ((month === 4 || month === 6 || month === 9 || month === 11) && day > 30) { 383 return false; 384 } else { 385 return true; 386 } 387 }, 388 strip: true, 389 /** @ignore */ 390 value: function (v) { 391 return v; 392 } 393 }; 394 395 $.typedValue.types['http://www.w3.org/2001/XMLSchema#anyURI'] = { 396 regex: /^.*$/, 397 strip: true, 398 /** @ignore */ 399 value: function (v, options) { 400 var opts = $.extend({}, $.typedValue.defaults, options); 401 return $.uri.resolve(v, opts.base); 402 } 403 }; 404 405 $.typedValue.defaults = { 406 base: $.uri.base(), 407 namespaces: {} 408 }; 409 410 /** 411 * Checks whether a value is valid according to a given datatype. The datatype must be held in the {@link jQuery.typedValue.types} object. 412 * @param {String} value The value to validate. 413 * @param {String} datatype The URI for the datatype against which the value will be validated. 414 * @returns {boolean} True if the value is valid or the datatype is not recognised. 415 * @example validDate = $.typedValue.valid(date, 'http://www.w3.org/2001/XMLSchema#date'); 416 */ 417 $.typedValue.valid = function (value, datatype) { 418 var d = $.typedValue.types[datatype]; 419 if (d === undefined) { 420 return true; 421 } else { 422 value = d.strip ? strip(value) : value; 423 if (d.regex.test(value)) { 424 return d.validate === undefined ? true : d.validate(value); 425 } else { 426 return false; 427 } 428 } 429 }; 430 431 })(jQuery); 432