1 /* 2 * $ URIs @VERSION 3 * 4 * Copyright (c) 2008,2009 Jeni Tennison 5 * Licensed under the MIT (MIT-LICENSE.txt) 6 * 7 */ 8 /** 9 * @fileOverview $ URIs 10 * @author <a href="mailto:jeni@jenitennison.com">Jeni Tennison</a> 11 * @copyright (c) 2008,2009 Jeni Tennison 12 * @license MIT license (MIT-LICENSE.txt) 13 * @version 1.0 14 */ 15 /** 16 * @class 17 * @name jQuery 18 * @exports $ as jQuery 19 * @description rdfQuery is a <a href="http://jquery.com/">jQuery</a> plugin. The only fields and methods listed here are those that come as part of the rdfQuery library. 20 */ 21 (function ($) { 22 23 var 24 mem = {}, 25 uriRegex = /^(([a-z][\-a-z0-9+\.]*):)?(\/\/([^\/?#]+))?([^?#]*)?(\?([^#]*))?(#(.*))?$/i, 26 docURI, 27 28 parseURI = function (u) { 29 var m = u.match(uriRegex); 30 if (m === null) { 31 throw "Malformed URI: " + u; 32 } 33 return { 34 scheme: m[1] ? m[2].toLowerCase() : undefined, 35 authority: m[3] ? m[4] : undefined, 36 path: m[5] || '', 37 query: m[6] ? m[7] : undefined, 38 fragment: m[8] ? m[9] : undefined 39 }; 40 }, 41 42 removeDotSegments = function (u) { 43 var r = '', m = []; 44 if (/\./.test(u)) { 45 while (u !== undefined && u !== '') { 46 if (u === '.' || u === '..') { 47 u = ''; 48 } else if (/^\.\.\//.test(u)) { // starts with ../ 49 u = u.substring(3); 50 } else if (/^\.\//.test(u)) { // starts with ./ 51 u = u.substring(2); 52 } else if (/^\/\.(\/|$)/.test(u)) { // starts with /./ or consists of /. 53 u = '/' + u.substring(3); 54 } else if (/^\/\.\.(\/|$)/.test(u)) { // starts with /../ or consists of /.. 55 u = '/' + u.substring(4); 56 r = r.replace(/\/?[^\/]+$/, ''); 57 } else { 58 m = u.match(/^(\/?[^\/]*)(\/.*)?$/); 59 u = m[2]; 60 r = r + m[1]; 61 } 62 } 63 return r; 64 } else { 65 return u; 66 } 67 }, 68 69 merge = function (b, r) { 70 if (b.authority !== '' && (b.path === undefined || b.path === '')) { 71 return '/' + r; 72 } else { 73 return b.path.replace(/[^\/]+$/, '') + r; 74 } 75 }; 76 77 /** 78 * Creates a new jQuery.uri object. This should be invoked as a method rather than constructed using new. 79 * @class Represents a URI 80 * @param {String} [relative=''] 81 * @param {String|jQuery.uri} [base] Defaults to the base URI of the page 82 * @returns {jQuery.uri} The new jQuery.uri object. 83 * @example uri = jQuery.uri('/my/file.html'); 84 */ 85 $.uri = function (relative, base) { 86 var uri; 87 relative = relative || ''; 88 if (mem[relative]) { 89 return mem[relative]; 90 } 91 base = base || $.uri.base(); 92 if (typeof base === 'string') { 93 base = $.uri.absolute(base); 94 } 95 uri = new $.uri.fn.init(relative, base); 96 if (mem[uri]) { 97 return mem[uri]; 98 } else { 99 mem[uri] = uri; 100 return uri; 101 } 102 }; 103 104 $.uri.fn = $.uri.prototype = { 105 /** 106 * The scheme used in the URI 107 * @type String 108 */ 109 scheme: undefined, 110 /** 111 * The authority used in the URI 112 * @type String 113 */ 114 authority: undefined, 115 /** 116 * The path used in the URI 117 * @type String 118 */ 119 path: undefined, 120 /** 121 * The query part of the URI 122 * @type String 123 */ 124 query: undefined, 125 /** 126 * The fragment part of the URI 127 * @type String 128 */ 129 fragment: undefined, 130 131 init: function (relative, base) { 132 var r = {}; 133 base = base || {}; 134 $.extend(this, parseURI(relative)); 135 if (this.scheme === undefined) { 136 this.scheme = base.scheme; 137 if (this.authority !== undefined) { 138 this.path = removeDotSegments(this.path); 139 } else { 140 this.authority = base.authority; 141 if (this.path === '') { 142 this.path = base.path; 143 if (this.query === undefined) { 144 this.query = base.query; 145 } 146 } else { 147 if (!/^\//.test(this.path)) { 148 this.path = merge(base, this.path); 149 } 150 this.path = removeDotSegments(this.path); 151 } 152 } 153 } 154 if (this.scheme === undefined) { 155 throw "Malformed URI: URI is not an absolute URI and no base supplied: " + relative; 156 } 157 return this; 158 }, 159 160 /** 161 * Resolves a relative URI relative to this URI 162 * @param {String} relative 163 * @returns jQuery.uri 164 */ 165 resolve: function (relative) { 166 return $.uri(relative, this); 167 }, 168 169 /** 170 * Creates a relative URI giving the path from this URI to the absolute URI passed as a parameter 171 * @param {String|jQuery.uri} absolute 172 * @returns String 173 */ 174 relative: function (absolute) { 175 var aPath, bPath, i = 0, j, resultPath = [], result = ''; 176 if (typeof absolute === 'string') { 177 absolute = $.uri(absolute, {}); 178 } 179 if (absolute.scheme !== this.scheme || 180 absolute.authority !== this.authority) { 181 return absolute.toString(); 182 } 183 if (absolute.path !== this.path) { 184 aPath = absolute.path.split('/'); 185 bPath = this.path.split('/'); 186 if (aPath[1] !== bPath[1]) { 187 result = absolute.path; 188 } else { 189 while (aPath[i] === bPath[i]) { 190 i += 1; 191 } 192 j = i; 193 for (; i < bPath.length - 1; i += 1) { 194 resultPath.push('..'); 195 } 196 for (; j < aPath.length; j += 1) { 197 resultPath.push(aPath[j]); 198 } 199 result = resultPath.join('/'); 200 } 201 result = absolute.query === undefined ? result : result + '?' + absolute.query; 202 result = absolute.fragment === undefined ? result : result + '#' + absolute.fragment; 203 return result; 204 } 205 if (absolute.query !== undefined && absolute.query !== this.query) { 206 return '?' + absolute.query + (absolute.fragment === undefined ? '' : '#' + absolute.fragment); 207 } 208 if (absolute.fragment !== undefined && absolute.fragment !== this.fragment) { 209 return '#' + absolute.fragment; 210 } 211 return ''; 212 }, 213 214 /** 215 * Returns the URI as an absolute string 216 * @returns String 217 */ 218 toString: function () { 219 var result = ''; 220 if (this._string) { 221 return this._string; 222 } else { 223 result = this.scheme === undefined ? result : (result + this.scheme + ':'); 224 result = this.authority === undefined ? result : (result + '//' + this.authority); 225 result = result + this.path; 226 result = this.query === undefined ? result : (result + '?' + this.query); 227 result = this.fragment === undefined ? result : (result + '#' + this.fragment); 228 this._string = result; 229 return result; 230 } 231 } 232 233 }; 234 235 $.uri.fn.init.prototype = $.uri.fn; 236 237 /** 238 * Creates a {@link jQuery.uri} from a known-to-be-absolute URI 239 * @param {String} 240 * @returns {jQuery.uri} 241 */ 242 $.uri.absolute = function (uri) { 243 return $.uri(uri, {}); 244 }; 245 246 /** 247 * Creates a {@link jQuery.uri} from a relative URI and an optional base URI 248 * @returns {jQuery.uri} 249 * @see jQuery.uri 250 */ 251 $.uri.resolve = function (relative, base) { 252 return $.uri(relative, base); 253 }; 254 255 /** 256 * Creates a string giving the relative path from a base URI to an absolute URI 257 * @param {String} absolute 258 * @param {String} base 259 * @returns {String} 260 */ 261 $.uri.relative = function (absolute, base) { 262 return $.uri(base, {}).relative(absolute); 263 }; 264 265 /** 266 * Returns the base URI of the page 267 * @returns {jQuery.uri} 268 */ 269 $.uri.base = function () { 270 return $(document).base(); 271 }; 272 273 /** 274 * Returns the base URI in scope for the first selected element 275 * @methodOf jQuery# 276 * @name jQuery#base 277 * @returns {jQuery.uri} 278 * @example baseURI = $('img').base(); 279 */ 280 $.fn.base = function () { 281 var base = $(this).parents().andSelf().find('base').attr('href'), 282 doc = $(this)[0].ownerDocument || document, 283 docURI = $.uri.absolute(doc.location === null ? document.location.href : doc.location.href); 284 return base === undefined ? docURI : $.uri(base, docURI); 285 }; 286 287 })(jQuery); 288