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