Permalink
Browse files

FIRST!

  • Loading branch information...
0 parents commit 57baa403121442c61f584c9cf29360a247679cb2 @kriskowal committed Nov 28, 2012
Showing with 360 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +4 −0 .travis.yml
  3. +20 −0 LICENSE.md
  4. +33 −0 README.md
  5. +24 −0 package.json
  6. +125 −0 test/url2-spec.js
  7. +151 −0 url2.js
@@ -0,0 +1,3 @@
+node_modules
+.coverage_data
+cover_html
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.6
+ - 0.8
@@ -0,0 +1,20 @@
+
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
@@ -0,0 +1,33 @@
+
+URL2
+----
+
+This module builds upon the existing URL module in NodeJS, but adds
+`relative(source, target)` which returns the shortest relative path
+between any two equally qualified URL’s. If the paths are not equally
+qualified, it returns the target.
+
+In addition, this package augments the URL object definition as returned
+by `parse` and consumed by `format`.
+
+- `pathname` is broken down into
+ - `root`: whether the path is qualified from the root of the
+ domain.
+ - `relative`: the relative path, from the root if `pathname` is
+ qualified from the domain root.
+ - `directories`: an array of path components
+ - `file`: the name of the file, or null if the URL points to a
+ directory, indicated by a final slash.
+
+Additionally, `format` uses the `path` property if it exists, instead of
+`pathname` and `search`.
+
+----
+
+Based on my earlier work for [Narwhal][].
+
+[Narwhal]: https://github.com/kriskowal/narwhal-lib/blob/master/lib/narwhal/uri.js
+
+Copyright 2012 Kristopher Michael Kowal. All rights reserved.
+MIT License
+
@@ -0,0 +1,24 @@
+{
+ "name": "url2",
+ "version": "0.0.0",
+ "description": "Node's URL module plus relative pathing",
+ "author": "Kris Kowal <kris@cixar.com> (https://github.com/kriskowal/url2)",
+ "homepage": "https://github.com/kriskowal/url2",
+ "keywords": ["url"],
+ "main": "url2.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/kriskowal/url2.git"
+ },
+ "devDependencies": {
+ "jshint": ">=0.9.1",
+ "jasmine-node": "*",
+ "cover": "*",
+ "opener": "*"
+ },
+ "scripts": {
+ "test": "jasmine-node test",
+ "lint": "jshint url2.js",
+ "cover": "cover run node_modules/jasmine-node/bin/jasmine-node test && cover report html && opener cover_html/index.html"
+ }
+}
@@ -0,0 +1,125 @@
+
+var URL = require("../url2");
+
+var tests = [
+
+ {
+ source: "",
+ target: "",
+ relative: ""
+ },
+
+ {
+ source: "foo/bar/",
+ target: "foo/bar/",
+ relative: ""
+ },
+
+ {
+ source: "foo/bar/baz",
+ target: "foo/bar/",
+ relative: "./"
+ },
+
+ {
+ source: "foo/bar/",
+ target: "/foo/bar/",
+ relative: "/foo/bar/"
+ },
+
+ {
+ source: "/foo/bar/baz",
+ target: "/foo/bar/quux",
+ relative: "quux"
+ },
+
+ {
+ source: "/foo/bar/baz",
+ target: "/foo/bar/quux/asdf",
+ relative: "quux/asdf"
+ },
+
+ {
+ source: "/foo/bar/baz",
+ target: "/foo/bar/quux/baz",
+ relative: "quux/baz"
+ },
+
+ {
+ source: "/foo/bar/baz",
+ target: "/foo/quux/baz",
+ relative: "../quux/baz"
+ },
+
+ {
+ source: "/foo/bar/baz",
+ target: "/foo/quux/baz?a=10",
+ relative: "../quux/baz?a=10"
+ },
+
+ {
+ source: "/foo/bar/baz?a=10",
+ target: "/foo/quux/baz?a=10",
+ relative: "../quux/baz?a=10"
+ },
+
+ {
+ source: "/foo/bar/baz?b=20",
+ target: "/foo/quux/baz?a=10",
+ relative: "../quux/baz?a=10"
+ },
+
+ {
+ source: "http://example.com",
+ target: "/foo/bar",
+ relative: "/foo/bar"
+ },
+
+ {
+ source: "",
+ target: "http://example.com/foo/bar",
+ relative: "http://example.com/foo/bar"
+ },
+
+ {
+ source: "",
+ target: "#foo",
+ relative: "#foo"
+ },
+
+ {
+ source: "",
+ target: "?a=10",
+ relative: "?a=10"
+ },
+
+ {
+ source: "?a=10",
+ target: "#foo",
+ relative: "?a=10#foo"
+ }
+
+];
+
+describe("relative", function () {
+
+ tests.forEach(function (test) {
+ it(
+ test.label || (
+ "from " + JSON.stringify(test.source) + " " +
+ "to " + JSON.stringify(test.target)
+ ),
+ function () {
+ expect(URL.relative(test.source, test.target))
+ .toBe(test.relative)
+ }
+ )
+ });
+
+ it("should format a url with a path property", function () {
+ expect(URL.format({path: "a/b"})).toEqual("a/b");
+ expect(URL.format({path: "a/b?c=d"})).toEqual("a/b?c=d");
+ });
+
+});
+
151 url2.js
@@ -0,0 +1,151 @@
+
+var URL = require("url");
+
+// Node’s URL parse produces:
+// http://user:password@foo.com:8080/path/to/foo/bar?a=10&c=20&d=30#hash
+// ^ ^^^^- auth ----^ ^-----^ |^- pathname----^^- search-----^^ ^
+// | ||| hostname | ^- query ----^| |
+// proto|| | ^--^^- path ----------------------^| |
+// | slashes | port hash^
+// | ^- host ---^ |
+// ^- href ------------------------------------------------------------^
+
+// Node's URL format uses:
+// protocol: gets a colon added if it doesn't have one yet
+// slashes: used
+// auth: used
+// hostname: used if there's no host
+// port: used if there's no host
+// path: NOT USED AT ALL EVER
+// pathname: used
+// search: used
+// query: used if object and no search
+// hash: used
+
+exports.resolve = URL.resolve;
+exports.resolveObject = URL.resolveObject;
+
+exports.parse = parse;
+function parse(url) {
+ var object = URL.parse(url);
+ object.pathname = object.pathname || "";
+ object.root = !!object.pathname.length && object.pathname[0] === "/";
+ if (object.root) {
+ object.relative = object.pathname.slice(1);
+ } else {
+ object.relative = object.pathname;
+ }
+ if (object.relative.length) {
+ object.directories = object.relative.split("/");
+ object.file = object.directories.pop();
+ } else {
+ object.directories = [];
+ object.file = null;
+ }
+ return object;
+}
+
+exports.format = format;
+function format(object) {
+
+ if ("file" in object) {
+ object.directories.push(object.file);
+ delete object.file;
+ }
+
+ if ("directories" in object) {
+ object.relative = object.directories.join("/");
+ delete object.directories;
+ }
+
+ if ("relative" in object) {
+ if (object.root) {
+ object.pathname = "/" + object.relative;
+ } else {
+ object.pathname = object.relative;
+ }
+ delete object.relative;
+ }
+
+ if (object.path != null) {
+ var index = object.path.indexOf("?");
+ if (index == -1) {
+ object.pathname = object.path;
+ object.search = "";
+ } else {
+ object.pathname = object.path.slice(0, index);
+ object.search = object.path.slice(index);
+ }
+ }
+
+ return URL.format(object);
+}
+
+exports.relativeObject = relativeObject;
+function relativeObject(source, target) {
+ source = parse(source);
+ target = parse(target);
+
+ delete target.href;
+
+ if (
+ target.protocol === source.protocol &&
+ target.slashes === source.slashes &&
+ target.auth === source.auth &&
+ target.host === source.host
+ ) {
+ delete target.protocol;
+ delete target.slashes;
+ delete target.auth;
+ delete target.hostname;
+ delete target.port;
+ delete target.host;
+
+ if (
+ !!target.root == !!source.root && !(
+ target.root &&
+ target.directories[0] != source.directories[0]
+ )
+ ) {
+ delete target.path;
+ delete target.root;
+ while (
+ source.directories.length &&
+ target.directories.length &&
+ target.directories[0] == source.directories[0]
+ ) {
+ target.directories.shift();
+ source.directories.shift();
+ }
+ while (source.directories.length) {
+ source.directories.shift();
+ target.directories.unshift('..');
+ }
+
+ if (
+ !target.root &&
+ !target.directories.length &&
+ !target.file && source.file
+ ) {
+ target.directories.push('.');
+ }
+
+ if (
+ target.directories.length === 0 &&
+ target.file === null &&
+ source.search
+ ) {
+ target.search = source.search;
+ }
+
+ }
+ }
+
+ return target;
+}
+
+exports.relative = relative;
+function relative(source, target) {
+ return format(relativeObject(source, target));
+}
+

0 comments on commit 57baa40

Please sign in to comment.