text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>URI.js - API Documentation</title>
    <meta name="description" content="URI.js is a Javascript library for working with URLs." />

    <link href="screen.css" rel="stylesheet" type="text/css" />
    <link href="prettify/prettify.sunburst.css" rel="stylesheet" type="text/css" />

URI.js

Intro

Understanding URIs

API-Documentation

jQuery Plugin

URI Template

Build

Author

            Constructing an URI

cloning an URI instance

href()

toString(), valueOf()

            Working with URI parts

protocol(), scheme()

username()

password()

hostname()

port()

host()

userinfo()

authority()

subdomain()

domain()

tld()

pathname(), path()

directory()

filename()

suffix()

segment()

segmentCoded()

search(), query()

hash(), fragment()

resource()

determine URL types

            Working with the query string

search(), query()

setSearch(), setQuery()

addSearch(), addQuery()

removeSearch(), removeQuery()

hasSearch(), hasQuery()

            Working with the Fragment (Hash)

Data in Fragments (Hash)

URLs in Fragments (Hash)

            Normalizing URLs

normalize()

normalizeProtocol()

normalizeHostname()

normalizePort()

normalizePathname(), normalizePath()

normalizeSearch(), normalizeQuery()

normalizeHash(), normalizeFragment()

            Charsets / Encodings

iso8859()

unicode()

readable()

            Relative and Absolute URLs

relativeTo()

absoluteTo()

            Comparing URLs

equals()

            Parsing URLs

URI.parse()

URI.parseAuthority()

URI.parseUserinfo()

URI.parseHost()

URI.parseQuery()

            Serializing URLs

URI.build()

URI.buildAuthority()

URI.buildUserinfo()

URI.buildHost()

URI.buildQuery()

            Encoding and Decoding URLs

URI.encode()

URI.decode()

URI.encodeReserved()

URI.encodeQuery()

URI.decodeQuery()

            Static Helper Functions

URI.noConflict()

URI.addQuery()

URI.removeQuery()

URI.commonPath()

URI.withinString()

URI.iso8859()

URI.unicode()

URI.expand()

URI.js API

URI Constructor

var uri = new URI(); // same as new URI(location.href)
// string
var uri = new URI("http://example.org");

// URI object for cloning
var uri = new URI(new URI("http://example.org"));

// URI parts object
var uri = new URI({
  protocol: "http",
  host: "example.org"
});

// without new keyword
var uri = URI("http://example.org");

// resolving right in the constructor
var uri = URI("../foobar.html", "http://example.org/hello/world.html");
// which is exactly the same as
URI("../foobar.html").absoluteTo("http://example.org/hello/world.html");
// but specified in URL constructor

The following parts can be specified in an object:

var uri = new URI({
  protocol: "http", // no trailing :
  username: "user",
  password: "pass",
  hostname: "example.org",
  port: "80", // string, please
  // "path", not "pathname", sorry
  path: "/foo/bar.html",
  // "query", not "search", sorry
  query: "foo=bar&bar=baz", // no leading ?
  // "fragment", not "hash", sorry
  fragment: "frag" // no leading #
});

using only components of URIs:

// Look ma! I'm only working the pathname
var uri = new URI("some/directory/file.html");

// Look ma! I'm only working the query string
var uri = new URI("?foo=bar");

// Look ma! I'm only working the fragment / hash
var uri = new URI("#call-me-hash");

// and any combination of the above…

using DOM elements:

var element = document.createElement('a');
element.href = 'http://example.org';
var uri = new URI(element);
// uri.domain() === 'example.org';

The following DOM elements can be parsed:

<a href="...">
<blockquote cite="...">
<link href="...">
<base href="...">
<script src="...">
<form action="...">
<input type="image" src="...">
<img src="...">
<area href="...">
<iframe src="...">
<embed src="...">
<source src="...">
<track src="...">

any other element yields URI("")

cloning URIs

Get a copy of the current URI instance

var uri = new URI("http://example.org");
var uri2 = uri.clone();

uri2.tld("com");
uri == "http://example.org/";
uri2 == "http://example.com/";

href()

get and set the entire URI

var uri = URI("http://example.com");
uri.href() === "http://example.com/";

uri.href("ftp://google.org");
uri.toString() === "ftp://google.org/"

toString(), valueOf()

serialize the URI to string. valueOf() is an alias to toString(), as string is the base primitive.

var uri = URI("http://example.com");
var s = uri.toString();
typeof s === "string";
s === "http://example.com/";

protocol(), scheme()

.scheme() is an alias of .protocol()

var uri = new URI("http://example.org/foo/hello.html");
// get protocol
uri.protocol(); // returns string "http"
// set protocol
uri.protocol("ftp"); // returns the URI instance for chaining

// relative scheme
uri.protocol("");
uri.toString() === "//example.org/foo/hello.html";

Throws a TypeError on illegal input, that is anything but [a-z0-9.+-] and [empty string] and null

username()

var uri = new URI("http://user:pass@example.org/foo/hello.html");
// get username
uri.username(); // returns string "user"
// set username
uri.username("user"); // returns the URI instance for chaining

password()

var uri = new URI("http://user:pass@example.org/foo/hello.html");
// get password
uri.password(); // returns string "pass"
// set password
uri.password("user"); // returns the URI instance for chaining

hostname()

var uri = new URI("http://example.org/foo/hello.html");
// get hostname
uri.hostname(); // returns string
// set hostname
uri.hostname("example.org"); // returns the URI instance for chaining

.hostname() returns the actual hostname, whereas .host() returns the hostname including the port

port()

var uri = new URI("http://example.org:8080/foo/hello.html");
// get port
uri.port(); // returns string "8080"
// set port
uri.port("80"); // returns the URI instance for chaining

although the port may be considered an integer, within URI it is a string.

Throws a TypeError on illegal input

host()

var uri = new URI("http://example.org:80/foo/hello.html");
// get host
uri.host(); // returns string "example.org:80"
// set host
uri.host("example.org:80"); // returns the URI instance for chaining

.hostname() returns the actual hostname, whereas .host() returns the hostname including the port

userinfo()

Userinfo is comprised of username and password

var uri = new URI("http://user:pass@example.org:88/foo/hello.html");
// get userinfo
uri.userinfo(); // returns string "user:pass"
// set userinfo
uri.userinfo("user:pass"); // returns the URI instance for chaining

authority()

Authority is comprised of username, password, hostname and port

var uri = new URI("http://user:pass@example.org:88/foo/hello.html");
// get authority
uri.authority(); // returns string "user:pass@example.org:88"
// set authority
uri.authority("user:pass@example.org:80"); // returns the URI instance for chaining

.authority() will reset any of username, password and port if they're not specified.

domain()

.domain() is a convenience method that returns example.org from the hostname www.example.org.

var uri = new URI("http://example.org/foo/hello.html");
// get domain
uri.domain(); // returns string "example.org"
// set domain
uri.domain("otherdomain.com"); // returns the URI instance for chaining

// Second Level Domain (SLD) Support (as of URI.js 1.5.0)
uri = new URI("http://example.co.uk/foo/hello.html");
uri.domain(); // return string "example.co.uk"
uri.domain(true); // return string "co.uk"

.domain() will throw an error if you pass it an empty string.

Throws a TypeError on illegal input

subdomain()

.subdomain() is a convenience method that returns www from the hostname www.example.org.

var uri = new URI("http://www.example.org/foo/hello.html");
// get subdomain
uri.subdomain(); // returns string "www"
// set subdomain
uri.subdomain("other.subdomain"); // returns the URI instance for chaining

Throws a TypeError on illegal input

tld()

.tld() is a convenience method that returns org from the hostname www.example.org.

var uri = new URI("http://example.org/foo/hello.html");
// get tld
uri.tld(); // returns string "org"
// set tld
uri.tld("com"); // returns the URI instance for chaining

// Second Level Domain (SLD) Support (as of URI.js 1.5.0)
uri = new URI("http://example.co.uk/foo/hello.html");
uri.tld(); // return string "co.uk"
uri.tld(true); // return string "uk"

Throws an Error if you pass it an empty string or use it on an IP-host.

pathname(), path()

.path() is an alias of .pathname()

var uri = new URI("http://example.org/foo/hello.html");
// get pathname
uri.pathname(); // returns string "/foo/hello.html"
// set pathname
uri.pathname("/foo/hello.html"); // returns the URI instance for chaining

// will encode for you
uri.pathname("/hello world/");
uri.pathname() === "/hello%20world/";
// will decode for you
uri.pathname(true) === "/hello world/";

// will return empty string for empty paths, but:
URI("").path() === "";
URI("/").path() === "/";
URI("http://example.org").path() === "/";

directory()

.directory() is an convenience method for mutating the directory part of a path

var uri = new URI("http://example.org/foo/hello.html");
// get directory
uri.directory(); // returns string "/foo" (no trailing slash)
// set directory
uri.directory("/bar"); // returns the URI instance for chaining
// uri == "http://example.org/bar/hello.html"

// will encode for you
uri.directory("/hello world/");
uri.directory() === "/hello%20world";
// will decode for you
uri.directory(true) === "/hello world";

uri.href("http://example.com/foo").directory()
// -&t; "/"
uri.href("/foo").directory()
// -&t; "/"
uri.href("foo").directory()
// -&t; ""

filename()

.filename() is an convenience method for mutating the filename part of a path

var uri = new URI("http://example.org/foo/hello.html");
// get filename
uri.filename(); // returns string "hello.html" (no leading slash)
// set filename
uri.filename("world.xml"); // returns the URI instance for chaining
// uri == "http://example.org/foo/world.xml"

// will encode for you
uri.filename("hello world.html");
uri.filename() === "hello%20world.html";
// will decode for you
uri.filename(true) === "hello world.html";

If you pass ../file.html, the directory will be changed accordingly

suffix()

.suffix() is an convenience method for mutating the filename part of a path

var uri = new URI("http://example.org/foo/hello.html");
// get suffix
uri.suffix(); // returns string "html" (no leading dot)
// set suffix
uri.suffix("xml"); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.xml"

// will encode for you
uri.suffix("würgh");
uri.suffix() === "w%C3%BCrgh";
// will decode for you
uri.suffix(true) === "würgh";

segment()

.segment() allows convenient access to directory levels / URN segments within the path. See .segmentCoded() for an interface that transparently encodes and decodes path segments.

var uri = new URI("http://example.org/foo/hello.html");
// get segments
uri.segment(); // returns array ["foo", "hello.html"]
// set segments
uri.segment(["foo", "bar", "foobar.html"]); // -> http://example.org/foo/bar/foobar.html

// get specific level
uri.segment(0); // returns "foo"
uri.segment(1); // returns "bar"
uri.segment(-1); // returns "foobar.html"
// set specific level
uri.segment(0, "bar"); // -> http://example.org/bar/bar/foobar.html
// remove specific level
uri.segment(0, ""); // -> http://example.org/bar/foobar.html

// append level
uri.segment("appendthis"); // -> http://example.org/bar/foobar.html/appendthis

segmentCoded()

.segmentCoded() works the same way .segment() does, with the difference of transparently encoding and decoding values.

var uri = new URI("http://example.org/foo/hello%20world.html");
// get segments
uri.segmentCoded(); // returns array ["foo", "hello world.html"]
// set segments
uri.segmentCoded(["foo", "bar", "foo bar.html"]); // -> http://example.org/foo/bar/foo%20bar.html

// get specific level
uri.segmentCoded(0); // returns "foo"
uri.segmentCoded(1); // returns "bar"
uri.segmentCoded(-1); // returns "foo bar.html"
// set specific level
uri.segmentCoded(0, "bar bam"); // -> http://example.org/bar%20bam/bar/foobar.html
// remove specific level
uri.segmentCoded(0, ""); // -> http://example.org/bar/foobar.html

// append level
uri.segmentCoded("append this"); // -> http://example.org/bar/foobar.html/append%20this

search(), query()

var uri = new URI("http://example.org/foo/hello.html?foo=bar&bar=baz");
// get search
uri.search(); // returns string "?foo=bar&bar=baz" (leading ?)
// get query
uri.query(); // returns string "foo=bar&bar=baz" (no leading ?)

// .query() and .search() behave the same for the following:

// set search
uri.search("?foo=bar&bar=baz"); // returns the URI instance for chaining
uri.search("foo=bar&bar=baz"); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.html?foo=bar&bar=baz"

// remove query
uri.search(""); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.html"

// get data map:
uri.search(true); // returns { foo: "bar", hello : ["world", "mars"] }

// set data map:
uri.search({ foo: "bar", hello : ["world", "mars"] });
// uri == "http://example.org/bar/world.html?foo=bar&hello=world&hello=mars"

// overwrite data through callback
uri.search(function(data) {
  return { hello : "world" };
});
// uri == "http://example.org/bar/world.html?hello=world"

// augment data through callback
uri.search(function(data) {
  data.foo = "bar";
});
// uri == "http://example.org/bar/world.html?hello=world&foo=bar"

// CAUTION: beware of arrays, the following are not quite the same
// If you're dealing with PHP, you probably want the latter…
uri.search("?foo=bar&bar=baz");
uri.search("?foo=bar[]&bar[]=baz");

Note that names and values passed in an object are encoded automatically.
        The object, resulting from parsing the query string, contains decoded values

Hint: If you're using jQuery, have a look at their .serialize() function.

hash(), fragment()

var uri = new URI("http://example.org/foo/hello.html#world");
// get hash
uri.hash(); // returns string "#world" (leading #)
// get fragment
uri.fragment(); // returns string "world" (no leading #)

// remove fragment
uri.fragment(""); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.html"

// .hash() and .fragment() behave the same for the following:

// set hash
uri.hash("#mars"); // returns the URI instance for chaining
uri.hash("mars"); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.xml#mars"

resource()

Resource is comprised of path, query and fragment

var uri = new URI("http://example.org/foo/hello.html?query=string#hash");
// get resource
uri.resource(); // returns string "/foo/hello.html?query=string#hash"
// set resource
uri.resource("/mars.txt?query=foo#other"); // returns the URI instance for chaining

.resource() will reset any of path, query and fragment if they're not specified.

is()

.is() tells what a URL is. It responds with a boolean and can be asked the following questions:

relative

true if URL doesn't have a hostname

absolute

true if URL has a hostname

urn

true if URI looks like a URN

url

true if URI is a URL

domain, name

true if hostname is not an IP

sld

true if hostname is a second level domain (i.e. "example.co.uk")

idn

true if hostname contains non-alphanumeric characters and is not an IP

punycode

true if hostname contains xn--

ip

true if hostname is IPv4 or IPv6

ip4, ipv4, inet4

true if hostname is IPv4

ip6, ipv6, inet6

true if hostname is IPv6

var uri = new URI("http://example.org/");
uri.is("relative") === false;
uri.is("absolute") === true;
uri.is("urn") === false;
uri.is("url") === true;
uri.is("name") === true;
uri.is("sld") === false;
uri.is("punycode") === false;
uri.is("IDN") === false; // case doesn't matter
uri.is("idn") === false; // case doesn't matter
uri.is("ip") === false;

var uri = new URI("http://123.123.123.123/");
uri.is("relative") === false;
uri.is("absolute") === true;
uri.is("urn") === false;
uri.is("url") === true;
uri.is("name") === false;
uri.is("sld") === false;
uri.is("IP") === true;
uri.is("IPv4") === true;
uri.is("IPv6") === false;

var uri = new URI("http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/");
uri.is("IP") === true;
uri.is("IPv4") === false;
uri.is("IPv6") === true;

var uri = new URI("/hello/world.html");
uri.is("relative") === true;
uri.is("absolute") === false;
uri.is("urn") === false;
uri.is("url") === true;
uri.is("name") === false;
uri.is("IP") === false;

var uri = new URI("http://example.co.uk/");
uri.is("name") === true;
uri.is("sld") === true;

var uri = new URI("mailto:mail@example.org");
uri.is("relative") === false;
uri.is("absolute") === false;
uri.is("urn") === true;
uri.is("url") === false;
uri.is("name") === false;
uri.is("sld") === false;
uri.is("punycode") === false;
uri.is("idn") === false;
uri.is("ip") === false;

Working with the query string

setSearch(), setQuery()

.setQuery() is an alias of .setSearch()

var uri = new URI("?hello=world");
uri.setSearch("hello", "mars"); // returns the URI instance for chaining
// uri == "?hello=mars"

uri.setSearch({ foo: "bar", goodbye : ["world", "mars"] });
// uri == "?hello=mars&foo=bar&goodbye=world&goodbye=mars"

uri.setSearch("goodbye", "sun");
// uri == "?hello=mars&foo=bar&goodbye=sun"

// CAUTION: beware of arrays, the following are not quite the same
// If you're dealing with PHP, you probably want the latter…
uri.setSearch("foo", ["bar", "baz"]);
uri.setSearch("foo[]", ["bar", "baz"]);

Note that names and values passed in are encoded automatically.

addSearch(), addQuery()

.addQuery() is an alias of .addSearch()

var uri = new URI("?hello=world");
uri.addSearch("hello", "mars"); // returns the URI instance for chaining
// uri == "?hello=world&hello=mars"

uri.addSearch({ foo: "bar", goodbye : ["world", "mars"] });
// uri == "?hello=world&hello=mars&foo=bar&goodbye=world&goodbye=mars"

uri.addSearch("no-value");
// uri == "?hello=world&hello=mars&foo=bar&goodbye=world&goodbye=mars&no-value"

// CAUTION: beware of arrays, the following are not quite the same
// If you're dealing with PHP, you probably want the latter…
uri.addSearch("foo", ["bar", "baz"]);
uri.addSearch("foo[]", ["bar", "baz"]);

Note that names and values passed in are encoded automatically.

removeSearch(), removeQuery()

.removeQuery() is an alias of .removeSearch()

var uri = new URI("?hello=world&hello=mars&foo=bar");
// remove an attribute
uri.removeSearch("hello"); // returns the URI instance for chaining
// uri == "?foo=bar"

// remove an attribute with value filter
uri.search("?hello=world&hello=mars&foo=bar");
uri.removeSearch("hello", "world"); // returns the URI instance for chaining
// uri == "?hello=mars&foo=bar"

// remove multiple values
uri.search("?hello=world&hello=mars&foo=bar&mine=true");
uri.removeSearch(["hello", "foo"]);
// uri == "?mine=true"

// remove multiple values with value filter
uri.search("?hello=world&hello=mars&foo=bar&mine=true&a=1&a=2&a=3");
uri.removeSearch({hello: "world", foo: undefined, a: ["1", "3"]});
// uri == "?hello=mars&mine=true&a=2"

// remove multiple values with RegExp
uri.search("?hello=world&hello=mars&foo=bar&mine=true&a=1&a=2&a=3");
uri.removeSearch(/^hello/);
// uri == "?foo=bar&mine=true&a=1&a=2&a=3"

// filter values with RegExp
uri.search("?foo=bar&foo=baz&foo=bam&obj=bam&bar=bar&bar=baz&bar=bam");
uri.removeSearch('foo', /[rz]$/);
// uri == "?foo=bam&obj=bam&bar=bar&bar=baz&bar=bam"

hasSearch(), hasQuery()

.hasQuery() is an alias of .hasSearch(). The method checks the existence and value of a given parameter within the query string.

var uri = URI("?string=bar&list=one&list=two&number=123&null&empty=");

// check if parameter exists (regardless of value)
uri.hasQuery("string") === true;
uri.hasQuery("nono") === false;

// check if parameter has a truthy / falsy value
uri.hasQuery("string", true) === true;
uri.hasQuery("string", false) === false;
uri.hasQuery("empty", true) === false;
uri.hasQuery("empty", false) === true;

// check if parameter has a given value
uri.hasQuery("string", "bar") === true;
uri.hasQuery("number", 123) === true;

// check if value is contained in parameter list
uri.hasQuery("list", "two", true) === true;
uri.hasQuery("list", ["two"], true) === true;
uri.hasQuery("list", "three", true) === false;
uri.hasQuery("list", ["two", "three"], true) === false;
uri.hasQuery("list", /ne$/, true) === true;

// check if parameter matches an expression
uri.hasQuery("string", /ar$/) === true;

// check by comparison function
uri.hasQuery("string", function(value, name, data) {
  // value === "bar";
  // name === "string";
  // data === uri.query(true);
  return true;
}) === true;

Working with the Fragment (Hash)

        There are virtually no limits to what you might do with fragments (hash).
        Every system has their own bag of tricks.
        As a result URI.js cannot offer any of the following tools right out of the box.
        The most common abuse of fragments are storing URLs or query string like data.

        Usually a prefix is used to identify data with special meaning. This prefix can be pretty much what you want.
        For URIs it's usually ! and for query-like data it often is ?.
        But they don't have to, which is why you can define a global default: URI.fragmentPrefix = "$";

Query String Fragments

The file src/URI.fragmentQuery.js is a "plugin" that allows you to store data in hashes in the same manner the .query() functions provide.

var uri = new URI("#?hello=world");
uri.addFragment("hello", "mars"); // returns the URI instance for chaining
// uri == "#?hello=world&hello=mars"

// to change the fragment prefix on an instance level:
uri.fragmentPrefix("!");

// to change the fragment prefix on a global level:
URI.fragmentPrefix = "!";

URL Fragments

The file src/URI.fragmentURI.js is a "plugin" that allows you to store URLs in hashes.

var uri = URI("http://example.org/#!/foo/bar/baz.html");
var furi = uri.fragment(true);

// manipulating the fragment URI
furi.pathname() === "/foo/bar/baz.html";
furi.pathname("/hello.html");

// has direct effect on the actual URI
uri.toString() === "http://example.org/#!/hello.html"

// to change the fragment prefix on an instance level:
uri.fragmentPrefix("?");

// to change the fragment prefix on a global level:
URI.fragmentPrefix = "?";

Normalizing URLs

normalize()

executes normalizeProtocol(), normalizeHostname(), normalizePort(), normalizePath(), normalizeSearch(), normalizeHash()

normalizeProtocol()

var uri = new URI("hTTp://www.example.org/");
// normalize protocol
uri.normalizeProtocol(); // returns the URI instance for chaining
// uri == "http://www.example.org/"

normalizeHostname()

For IDN conversion punycode.js must be available (bundled in URI.js).
        For IPv6-best-notation conversion IPv6.js must be available (bundled in URI.js). Also lower-cases hostnames.

var uri = new URI("http://www.exämple.org/");
// normalize IDN host
uri.normalizeHostname(); // returns the URI instance for chaining
// uri == "http://www.xn--exmple-cua.org/"

// normalize IPv6 host
uri.hostname("fe80:0000:0000:0000:0204:61ff:fe9d:f156");
uri.normalizeHostname(); // returns the URI instance for chaining
// uri == "http://fe80::204:61ff:fe9d:f156/"

// normalize hostname to lower case
uri.hostname("wWw.eXample.Org");
uri.normalizeHostname(); // returns the URI instance for chaining
// uri == "http://www.example.org/"

There is no .normalizeHost(), as .host() is a property comprised of .hostname() and .port()

normalizePort()

Removes the port, if it's the default for the given protocol (http: 80, https: 443, ftp: 21).

The list of default ports can be modified at URI.defaultPorts

var uri = new URI("http://example.org:80/foo.html");
// normalize port
uri.normalizePort(); // returns the URI instance for chaining
// uri == "http://example.org/foo.html"

normalizePathname(), normalizePath()

.normalizePath() is an alias of .normalizePathname(), they resolve relative hierarchies

var uri = new URI("/hello/foo/woo/.././../world.html");
// normalize path
uri.normalizePathname(); // returns the URI instance for chaining
// uri == "/hello/world.html"

normalizeSearch(), normalizeQuery()

Turns ?&foo=bar&&foo=bar&foo=baz& into ?foo=bar&foo=baz and removes ? if there is no query string.

var uri = new URI("?&foo=bar&&foo=bar&foo=baz&");
// normalize search
uri.normalizeSearch(); // returns the URI instance for chaining
// uri == "?foo=bar&foo=baz"

normalizeHash(), normalizeFragment()

removes # if there is no hash

var uri = new URI("http://example.org/foo/hello.html#");
// normalize hash
uri.normalizeHash(); // returns the URI instance for chaining
// uri == "http://example.org/bar/world.xml"

Charsets / Encodings

iso8859()

.iso8859() converts unicode-encoded escape sequences to ISO8859-encoded escape sequences. It does this by calling .normalize() internally.

var uri = new URI("/%C3%A4.html");
uri.iso8859(); // returns the URI instance for chaining
// uri == "/%E4.html"

You can make URI work with ISO8859 encoding by default by calling URI.iso8859().

unicode()

.unicode() converts ISO8859-encoded escape sequences to unicode-encoded escape sequences. It does this by calling .normalize() internally.

var uri = new URI("/%E4.html");
uri.unicode(); // returns the URI instance for chaining
// uri == "/%C3%A4.html"

Formatting URLs

readable()

Formats URLs to be human readable (much like your browser does nowadays).

var uri = new URI("http://foo:bar@www.xn--exmple-cua.org/"
  + "hello%20world/ä.html?foo%5B%5D=b+är#fragment");

uri.readable() === "http://www.exämple.org/"
  + "hello world/ä.html?foo[]=b är#fragment";

Relative and Absolute URLs

relativeTo()

.relativeTo() compares two paths an makes one relative to the other

var uri = new URI("/relative/path");
// make path relative
var relUri = uri.relativeTo("/relative/sub/foo/sub/file"); // returns a new URI instance
// relUri == "../../../path"

// also works for relative directories
URI("../world.html").absoluteTo("foo/bar/baz.html");
// -> "foo/world.html"

// absolute URLs are passed through unchanged
URI("http://example.org/world.html")
  .absoluteTo("http://google.com/baz");
// -> "http://example.org/world.html"

// absolute URLs relative to absolute URLs
URI("http://example.org/world.html").clone().authority("")
  .absoluteTo("http://google.com/baz");
// -> "http://google.com/world.html"

// equal URLs are relative by empty string
URI("http://www.example.com:8080/dir/file")
  .relativeTo('http://www.example.com:8080/dir/file')
  .toString();
// -> ""

// relative on fragment and query string as well
URI("http://www.example.com:8080/dir/file?foo=bar#abcd")
  .relativeTo('http://www.example.com:8080/dir/file')
  .toString();
// -> "?foo=bar#abcd"

.relativeTo() and .absoluteTo() reverse each other.

absoluteTo()

.absoluteTo() makes a relative path absolute based on another path

var uri = new URI("../../../path");
// make path absolute
var relUri = uri.absoluteTo("/relative/sub/foo/sub/file"); // returns a new URI instance
// relUri == "/relative/path"

.relativeTo() and .absoluteTo() reverse each other.

Comparing URLs

equals()

.equals() determines if the given URLs are the same - disregarding default ports, capitalization, dot-pathnames, query-parameter order, etc.

var a = "http://example.org/foo/bar.html"
  + "?foo=bar&hello=world&hello=mars#fragment";
var b;

// normalizing URI before comparison:
b = "http://exAMPle.org:80/foo/../foo/bar.html"
  + "?foo=bar&hello=world&hello=mars#fragment";

a !== b;
URI(a).equals(b) === true;

// comparing query string parameters:
b = "http://example.org/foo/bar.html"
  + "?hello=mars&foo=bar&hello=world&#fragment";

a !== b;
URI(a).equals(b) === true;

// shorthand for comparing to window.location.href:
URI(a).equals();

Parsing URLs

URI.parse(string url)

parses a string into its URI components. returns an object containing the found components

var result = URI.parse("http://example.org/foo.html");
result === {
  protocol: "http",
  username: null,
  password: null,
  hostname: "example.org",
  port: null,
  path: "/foo.html",
  query: null,
  fragment: null
};

URI.parseAuthority(string url, object parts)

parses a string's beginning into its URI components username, password, hostname, port.
        Found components are appended to the parts parameter.
        Remaining string is returned

var parts = {};
var result = URI.parseAuthority("user:pass@example.org:8080/foo.html", parts);
result === "/foo.html";
parts === {
  username: "user",
  password: "pass",
  hostname: "example.org",
  port: "8080"
};

URI.parseUserinfo(string url, object parts)

parses a string's beginning into its URI components username, password.
        Found components are appended to the parts parameter.
        Remaining string is returned

var parts = {};
var result = URI.parseUserinfo("user:pass@example.org:8080/foo.html", parts);
result === "example.org:8080/foo.html";
parts === {
  username: "user",
  password: "pass"
};

URI.parseHost(string url, object parts)

parses a string's beginning into its URI components hostname, port.
        Found components are appended to the parts parameter.
        Remaining string is returned

var parts = {};
var result = URI.parseAuthority("example.org:8080/foo.html", parts);
result === "/foo.html";
parts === {
  hostname: "example.org",
  port: "8080"
};

URI.parseQuery(string querystring)

Parses the passed query string into an object. Returns object {propertyName: propertyValue}

var result = URI.parseQuery("?foo=bar&hello=world&hello=mars&bam=&yup");
result === {
  foo: "bar",
  hello: ["world", "mars"],
  bam: "",
  yup: null
};

Serializing URLs

URI.build(object parts)

serializes the URI components passed in parts into a URI string

var parts = {
  protocol: "http",
  username: null,
  password: null,
  hostname: "example.org",
  port: null,
  path: "/foo.html",
  query: null,
  fragment: null
};
URI.build(parts) === "http://example.org/foo.html";

URI.buildAuthority(object parts)

serializes the URI components username, password, hostname, port passed in parts into a URI string

var parts = {
  username: "user",
  password: "pass",
  hostname: "example.org",
  port: "8080"
};
URI.buildAuthority(parts) === "user:pass@example.org:8080";

URI.buildUserinfo(object parts)

serializes the URI components username, password passed in parts into a URI string

var parts = {
  username: "user",
  password: "pass"
};
URI.buildUserinfo(parts) === "user:pass@";

URI.buildHost(object parts)

serializes the URI components hostname, port passed in parts into a URI string

var parts = {
  hostname: "example.org",
  port: "8080"
};
URI.buildHost(parts) === "example.org:8080";

URI.buildQuery(object data, [boolean duplicateQueryParameters], [boolean escapeQuerySpace])

serializes the query string parameters

var data = {
  foo: "bar",
  hello: ["world", "mars", "mars"],
  bam: "",
  yup: null,
  removed: undefined
};

// Note: duplicate hello=mars is dropped (default behavior!)
URI.buildQuery(data) === "foo=bar&hello=world&hello=mars&bam=&yup";

// Note: duplicate hello=mars is preserved
URI.buildQuery(data, true) === "foo=bar&hello=world&hello=mars&hello=mars&bam=&yup";

To preserve duplicate values, use URI.buildQuery() directly:

var uri = new URI("http://example.org/foo.html?bar=baz");
var data = uri.query(true);

data.some = "new data";
uri.query(URI.buildQuery(data, true));

// you can also use the static URI.addQuery() and URI.removeQuery()
URI.addQuery(data, "hello", "world");
uri.query(URI.buildQuery(data, true));

As of v1.8.0 you can configure query parameter de/duplication:

// make all new URI instances allow duplicates:
URI.duplicateQueryParameters = true; // default is false

// make a specific URI instance allow duplicates:
var withDuplicates = URI("?bar=1&bar=1")
  .duplicateQueryParameters(true)
  .normalizeQuery()
  .toString();

// make a specific URI instance avoid duplicates (default):
var noDuplicates = URI("?bar=1&bar=1")
  .duplicateQueryParameters(false)
  .normalizeQuery()
  .toString();

withDuplicates === "?bar=1&bar=1";
noDuplicates === "?bar=1";

As of v1.11.0 you can configure query space en/decoding:

// prevent all new URI instances from escaping spaces in query strings:
URI.escapeQuerySpace = false; // default is true

// make a specific URI instance escape spaces in query:
var withPlus = URI("?bar=hello+world")
  .escapeQuerySpace(true)
  .query(true).bar;

// make a specific URI instance not escape spaces in query
var withPercent = URI("?bar=hello%20world")
  .escapeQuerySpace(false)
  .query(true).bar;

withPlus === "hello world";
withPercent === "hello world";

Encoding and Decoding URLs

URI.encode()

Encode an URI component with strict compliance to RFC3986

URI.encode("hä lo#w*rl:d!") === "h%C3%A4%20lo%23w%2Arl%3Ad%21";
// vs.
encodeURIComponent("hä lo#w*rl:d!") === "h%C3%A4%20lo%23w*rl%3Ad!";
// not how * and ! were not encoded

URI.decode()

Decode an URI component

URI.encode("h%C3%A4%20lo%23w%2Arl%3Ad%21") === "hä lo#w*rl:d!";
// note:
URI.decode === decodeURIComponent;

URI.encodeReserved()

Encode an URI component whilst preserving reserved characters

URI.encodeReserved("ä:/?#[]@!$&'()*+,;=") === "%C3%A4:/?#[]@!$&'()*+,;=";
// vs.
URI.encode("ä:/?#[]@!$&'()*+,;=") ===
  "%C3%A4%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D";

URI.encodeQuery()

Decode a query string component. Works like encode(), except it handles %20 as + (space) if URI.escapeQuerySpace = true;.

URI.escapeQuerySpace = true; // default
URI.encodeQuery(" ") === "+";

URI.escapeQuerySpace = false;
URI.encodeQuery(" ") === "%20";

// vs.
URI.encode(" ") === "%20";

URI.decodeQuery()

Decode a query string component. Works like decode(), except it handles + as %20 (space) if URI.escapeQuerySpace = true;.

URI.escapeQuerySpace = true; // default
URI.decodeQuery("+") === " ";

URI.escapeQuerySpace = false;
URI.decodeQuery("+") === "+";

// vs.
URI.decode("+") === "+";

Static Helper Functions

URI.noConflict()

removes URI variables from global scope

// restores window.URI to its previous state and returns URI
URI.noConflict();
// restores the global variable to its previous state and returns the object itself
URITemplate.noConflict();
IPv6.noConflict();
SecondLevelDomains.noConflict();

// restore all objects and return them as a map {URI: ..., IPv6: ..., ....}
URI.noConflict(true);

URI.addQuery()

adds data to a map

var data = {};

URI.addQuery(data, "hello", "mars");
data === {hello: "mars"};

URI.addQuery(data, "hello", "world");
data === {hello: ["mars", "world"]};

URI.addQuery(data, {foo: "bar", goodbye : ["world", "mars"]});
data === {hello: ["mars", "world"], foo: "bar", goodbye : ["world", "mars"]};

URI.removeQuery()

removes data from a map

var data === {hello: ["mars", "world"], foo: "bar", goodbye : ["world", "mars"]};

URI.removeQuery(data, "hello");
data === {foo: "bar", goodbye : ["world", "mars"]};

// remove an attribute with value filter
data = {hello: ["world", "mars"], foo: "bar"};
URI.removeQuery(data, "hello", "world");
data === {hello: ["mars"], foo: "bar"} // yes, still an array

// remove multiple values
data = {hello: ["world", "mars"], foo: "bar", mine: "true"}
URI.removeQuery(["hello", "foo"]);
data === {mine: "true"};

// remove multiple values with value filter
data = {hello: ["world", "mars"], foo: "bar", mine: "true", a: ["1", "2", "3"]}
URI.removeQuery({hello: "world", foo: undefined, a: ["1", "3"]});
data === {hello: ["mars"], mine: "true", a: ["2"]}

URI.commonPath()

URI.commonPath() determines the common base directory of two paths.

URI.commonPath("/foo/bar/baz.html", "/foo/bar/world.html");
// returns "/foo/bar/"

URI.commonPath("/foo/bar/baz.html", "/foo/bazz/world.html");
// returns "/foo/"

URI.commonPath("/foo/bar/baz.html", "/other/world.html");
// returns "/"

URI.commonPath("/foo", "bar");
// returns ""

URI.withinString()

URI.withinString() identifies URIs within text, e.g. to translate them to <a>-Tags. (Obviously you'd want to put the urls inside the href-Attribute and escape them properly…)

.withinString() only works on plain text, it will not work with HTML!

var source = "Hello www.example.com,\n"
  + "http://google.com is a search engine, like http://www.bing.com\n"
  + "http://exämple.org/foo.html?baz=la#bumm is an IDN URL,\n"
  + "http://123.123.123.123/foo.html is IPv4 and "
  + "http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html is IPv6.\n"
  + "links can also be in parens (http://example.org) "
  + "or quotes »http://example.org«.";

var result = URI.withinString(source, function(url) {
  // callback needs to return a string
  // feel free to URI(url).normalize().toString() or something
  return "<a>" + url + "</a>";
});

/* result is:
Hello <a>www.example.com</a>,
<a>http://google.com</a> is a search engine, like <a>http://www.bing.com</a>
<a>http://exämple.org/foo.html?baz=la#bumm</a> is an IDN URL,
<a>http://123.123.123.123/foo.html</a> is IPv4 and <a>http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html</a> is IPv6.
links can also be in parens (<a>http://example.org</a>) or quotes »<a>http://example.org</a>«.
*/

// a proper replacement could look like the following:
var escapeHtml = function(string) {
  return string
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
};
var result = URI.withinString(source, function(url) {
  var uri = new URI(url);
  uri.normalize();
  return "<a href="" + escapeHtml(uri) + "">"
    + escapeHtml(uri.readable()) + "</a>";
});

As of URI.js 1.12.0 withinString accepts the following parameters:

var source = "Hello www.example.com.";
var decorate = function(url) {
  return "<code>" + url + "</code>";
};
var result = null;

// access to the original input text from the callback
URI.withinString(source, function(url, start, end, source) {
  source.slice(start, end) === url;
  return url;
});

// ignore certain URLs
source = "Hello www.example.com,\n"
  + "ohgodno://example.org/ is a a protocol we want ignored";
result = URI.withinString(source, decorate, {
  ignore: /^ohgodno:/i
});

/* result is:
Hello <code>www.example.com</code>,
ohgodno://example.org/ is a a protocol we want ignored
*/

// ignore URLs in HTML
source = "Hello www.example.com,\n"
  + '<img src="http://example.org/image.png" alt=""> is HTML,\n'
  + "<a href='http://example.org/target.html'> is HTML</a>,\n"
  + "<a href=http://example.org/target.html> is HTML, too</a>.";
result = URI.withinString(source, decorate, {
  ignoreHtml: true
});

/* result is:
Hello <code>www.example.com</code>,
<img src="http://example.org/image.png" alt=""> is HTML,
<a href='http://example.org/target.html'> is HTML</a>,
<a href=http://example.org/target.html> is HTML, too</a>
*/

// custom URI beginning pattern
source = "That example.com/ is just a domain";
result = URI.withinString(source, decorate, {
  // "scheme://" or "www." or "domain.tld/"
  start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.|[a-z]+\.[a-z]{2,4}\/)/gi
});

/* result is:
That <code>example.com/</code> is just a domain
*/

URI.iso8859()

URI.iso8859() tells URI.js to use the older escape/unescape methods, for backwards compatibility with non-unicode platforms.

URI.iso8859();

var uri = new URI("http://example.org/foo/æ.html");
// http://example.org/foo/%E6.html

URI.unicode()

URI.unicode() restores the default unicode-encoded URLs.

URI.unicode();

var uri = new URI("http://example.org/foo/æ.html");
// http://example.org/foo/%C3%A6.html

URI.expand()

URI.expand() is a convenience wrapper for URITemplate.
        While URITemplate#expand returns a string, URI.expand() returns an URI instance.

URI.expand("/foo/{var}/{iable}", {
  "var": "bar",
  "iable": "hello world.html"
});

// returns URI("/foo/bar/hello%20world.html")


text:
Q: RSS aggregator; how to insert only new items A tutorial here shows how to build an agregator in PHP but I'm having some trouble finding the best way not to insert the same items in my database.
If I were to run the script on http://visualwebsiteoptimizer.com/split-testing-blog/feed/ and then run it again in 5 minutes it'll just insert the same items again.
That tutorial just has an interval time specified in wich it will reload the RSS feed and save all the items.
I was wondering if RSS implement some request header that will only send the items after a certain date. I see here that I could use lastBuildDate and mabe ignore channels that have a date older than last fetched but it doesn't say if that is mandatory.
My question here is: how can I check RSS feeds regularly and insert it in a database without inserting the same item more than once?
I'm thinking the only way to do it is to check if a record already exist using link and only insert if it doesn't exist already. I know link is optional but I won't save items that don't have one anyway. This seems a bit inefficient though; checking before every insert might be fine in the beginning but when the database starts filling up it might get very slow.

A: You might have to use a few different strategies depending on how well the site you are consuming has implemented the spec.
First I would try adding a unique index on the database for the GUID value, GUIDs by there nature should be unique, http://en.wikipedia.org/wiki/Globally_unique_identifier - then depending on which DB you are using you should be able to use syntax like INSERT IGNORE INTO... or INSERT ... ON DUPLICATE KEY UPDATE... and just have the update syntax not really do anything
If some sites don't have a guid field (I am assuming you will end up consuming more than just the example) you could add the unique on the siteId field and the either the time or the title, both are less than ideal of course contacting the site own to get them to implement a guid might work too ;)
You could also run an md5 hash on the post content and store that alongside the post, that should stop duplicates too.
How big are you expecting the DB to get? with proper indexing I would have thought that it would have to be huge before it runs slow; indexes on siteId, guid, time and/or hash and limited to just 1 row and just the rowId should be quick enough, epscialyl if you can get your script to run commandline / on a cron job rather than through a webserver

redpajama_set_name: RedPajamaStackExchange


text:
namespace PubNub.Async.Services.History
{
	public enum HistoryOrder
	{
		Chronological,
		Reverse
	}
}

redpajama_set_name: RedPajamaGithub


FMS Magazine recently hosted its second #BLOGTOBER event, as part of the annual October-long festival at North London's The Finsbury, where we got to present some of our favourite artists – Mellow Gang, Genghis and MIRI.
MIRI kicked off the evening of live music, and immediately captured an attentive audience with her solo performance on keys. Opening her set with the beautifully cut-throat 'Part Time Love' she continued to showcase the skillful arrangements and lyrical depth to her songwriting throughout, with latest track, 'Good Ones', swiftly followed by 'Electric Vibes' and 'Compass' – both from her Sound Vibrations EP (2016).
A touching rendition of Cyndi Lauper's massive hit, 'Girls Just Wanna Have Fun' brought the set to a close, before MIRI returned to her keyboard for one more tune, following the demands of the audience, and finished up with the wonderful 'Barcelona'.
Genghis (aka Toby Spysznyk) took to the stage next, showcasing tracks from his upcoming EP for the first time, with the ridiculously catchy 'Crazy Eyes' and 'High' – an earworm that's been bugging me for days – opening the show and setting the energetic pace of what was to come.
The band – members of The Carnabys – took a chill as Toby performed the beautifully soulful 'I Know', before picking up the pace again as they moved into more familiar territory with 'Oh My' from his debut EP. Genghis wrapped up his impressive set with 'Landslide', the fourth track recently recorded for his upcoming EP.
Mellow Gang made a return to their London home (mid tour) to headline the music for the night, kicking off their set with the dreamy 'Vendetta' from their Play EP, released early this summer, before falling seamlessly into one song after another, including the dark 'My Last', 'Mirrored' and debut single, 'Lagoon'.
Having emerged onto the scene just this year, the enigmatic quartet – completed by Harriet Joseph (lead vocals/guitar), Shen (keys), Nick GK (bass) and Joe Wood (drums) – are enthralling audiences with their lush soundscapes and rich instrumentation, that merge melancholic shoegaze with sparse psychedelia.
#BLOGTOBER kicked off on October 1st with For Folks Sake presenting Roxanne de Bastion, Hattie Whitehead and Gitta de Ridder, with Lost In The Manor wrapping up the month on October 31st with Crooked Teeth and Kylypso.
Big thanks to the equally awesome artists and everyone else who took part in making our event a great success, and thanks also to Mark Saunders who we spotted filming the gig, you can find more of his videos HERE.
Want more? Check out our Q&As with the artists HERE.


I've got a confession to make, I love a kitchen gadget, ironic, being that my wife does most of the cooking in our house.
As good of an idea they might seem to be at the time, unfortunately, most will be shoved in the cupboard only to be forgotten about gathering dust until I find a use for it.
Our slow cooker was one such item, we bought it on a whim one winter's day thinking how nice it would be to have something warming in our bellies.
After doing my research we ended up getting a Morphy Richards Sear and Stew. We bought it and our lives were going to be transformed, sadly they weren't. We found it a lovely little corner in our kitchen and rolled it out once in a while when we fancied a sausage casserole.
Unless you have money to burn, I assume you're not going to be putting in meat from Marks and Spencer or Waitrose in a slow cooker. Cheap cuts do the job just fine and there's nothing wrong with frozen veg either.
You can also get away with bulking up the meal with potatoes and pulses.
You could probably do the same dishes in a conventional oven, but slow cookers are cheaper to run, comparable to leaving a lightbulb on all day apparently, not that I ever would.
Leftovers in our house were once an alien concept, why the hell would you cook more than you need, and if we did I'd just stick it on my plate and ate it anyway. How I had room for a pudding is beyond me.
In our recent cold weather snap having a leftover chilli or stew at work has been fantastic, and everyone knows that food tastes better the next day. The only disadvantage that I've found, is that after having something so heavy I could just do with getting my head down for a quick 30-minute snooze, no such luck, I don't work for a silicon valley tech company with sleep pods.
The Morphy Richards Sear and Stew really is a great bit of kit, it's really one-pot cooking at it's best, no need for an extra pan to sear the meat, stick in the ingredients, turn it on and wait for eating what could be easier. Best of all it's dishwasher safe so if we are really in a mood where we can't be bothered to lift a finger, into the dishwasher it goes.
Time is precious and when I've been at work all day, as strange as it might seem to some people, I actually enjoy spending time with my wife. Yes, we could cook together and on the weekends we often do.
There is only two of us, but there can't be any doubt that they are fantastic for families too, especially those that seem to be on the go non-stop.
Can you believe it, you can actually do more than making a sausage casserole in the slow cooker. What a revelation.
Don't get me wrong we had cookbook after cookbook about on what we could actually make, but these were read when we first got them, well we looked at the pictures and then like our the gadgets before them forgotten about.
Slow cookers lend themselves well to experimentation too, throw in your ingredients leave it and fingers crossed, in a few hours time you'll have an enjoyable meal.
If you do, what's your favourite recipe and what are the benefits you've found in using it? If you don't, what's stopping you?


Deep Orange students use the unique Industrial Origami (IOI) technology to develop the tray concept for housing the high-voltage battery modules.
Close-up view of the solar active region AR9906 on April 21, 2002 with TRACE and RHESSI data. RHESSI observes x-rays from this flare. The red contours represent the 12-25 keV photon energy range and the blue contours represent 50-100 keV.
Develop a positive attitude towards Irish and develop a interest in it. To enjoy learning Irish.


Ustica Island is acclaimed as the scuba diving capital of the Mediterranean Sea.
Diving in Ustica is strictly recommended to water sport lovers because this charming island includes the best dive sites among the Mediterranean. The most important locations in Ustica are Secca della Columbara and Lo Scoglia del Medico. The first offers a colourful, seabed with corals and sponges. The second site Scoglio del Medico (Doctor's Rock) includes a unique cavern deep 70 meters with blue light.
It was created by two young men, Jonathan Cecchinel and Gaetano Lo Nero who were born on the island of Ustica, to open the first diving school to be run by two islanders. The centre offers a wide variety of courses and excursions both at beginners and expert levels. It also provides several scuba-diving trainings and internationally recognised certification courses.
The Other activities offered are: snorkelling, recreational and guided scuba diving outings, tours, rental of diving equipment and sea recovery.
The diving centre Mister Jump is located at the centre of the small town of Ustica, in the middle of an impressive staircase that leads from the port directly to the main street. It has showers, a rising equipment area and a relax corner. The meeting point for the excursions is on the quay. Here every morning you will find your equipment ready to be assembled with the cylinder.
This centre takes you to discover the most beautiful and extraordinary dives, offering solutions for exciting weekends or one-day trips. The resort is specialized in arranging holidays for experienced divers as well as family or groups offering a large variety of courses and tours.
The resort is well organised to allow you to find all you need to start your amazing dive guaranteeing the maximum comfort during all the activities. You can hire all the equipment directly at the Centre. The dives usually take place in the morning from 9.00am to 12.30pm and in the afternoon from 3.00pm to 6.30pm 7 days a week from Easter to October.


text:
BY Travis Diehl in Exhibition Reviews , US Reviews | 05 APR 22
Suzanne Lacy's Orbits Around the Notions of Womanhood
At the Queens Museum, New York, a survey of the artist's career showcases her feminist genre of social practice
The problem of the retrospective – which curators and audiences will face increasingly as more post-1970s artists receive surveys of their performances, happenings and social practice – is the gap between the active, catalytic artwork of the past and the more or less nonreactive present archive. This is absolutely the case with Suzanne Lacy's 'The Medium Is Not the Only Message', a thematic survey at the Queens Museum curated by Sophia Marisa Lucas. The earliest piece on display, Net Construction (1973/2022), comprises a cargo net that served as the armature for a performance in which Lacy tied meat to the ropes, her naked body and the audience. Here, however, we can only extrapolate the experience from the wall text and a black and white photograph, imagining the new jute besmirched by fat or spices, activated by art.
Suzanne Lacy, Immigrants and Survivors, 1983, performance at the Immaculate Heart High School cafeteria, Los Angeles. Courtesy: © the artist; photograph: Cindy Honesto
The exhibition overcomes the basic inertia of document-heavy shows by emphasizing both thematic and formal links between works. The archive itself is one such form. Lacy's practice, which is comprised largely of facilitating intimate discussions between groups of women, is just as focused on documenting the results: sketches, ephemera, videos and participant feedback – all of which are presented here in vitrines and on monitors. So, although visitors to 'The Medium Is Not the Only Message' won't be served dinner, the branching, pre-internet form of International Dinner Party (1979) – in which groups of women around the globe gathered for meals then telegrammed Lacy the details – still carries a charge. Visitors can flip through several binders of these telegrams and, while they may be dry ephemera – much like the map, also on view, covered in red dots denoting the dinner locations – they were, and remain, acts of real-time communication: we, listed below, had dinner together, at this time, in this place.
This idea of 'connecting' or coming together to share experiences lies at the heart of Lacy's practice and continues to work whenever that transfer of experience still occurs. Perhaps with this in mind, a couple of projects with more subdued representations in the main gallery – The Crystal Quilt (1987), in which 430 women sat around tables arranged in a quiltlike pattern, and Freeze Frame: Room for Living Room (1982), whereby 120 women occupied a furniture showroom to discuss their life experiences – have been reprised, ambiently, in the museum's sunken atrium. Several modish living-room sets furnish the floor, with blue velvet couches and club chairs turned inward around vitrines of letters, sketches and plans; a couple of televisions screen videos of past living-room projects. Here, Lacy's simple premise – getting women together in a space to facilitate frank, and in some cases rare, conversation – carries its latent hope into the present.
Suzanne Lacy, Cinderella in a Dragster, 1976, performance at California State College, Dominguez Hills (now California State University, Dominguez Hills), Carson, and University of California, San Diego. Courtesy: © Suzanne Lacy Studio
The show gives the impression that Lacy's projects evolve depending on the input of the women who participate. This seems genuine in a way that numerous more didactic 'participatory' artworks don't. Visceral, cathartic, even triggering at times, Lacy's projects regularly seem to facilitate breakthroughs – in part because the artist meets women where they are, physically and emotionally. In Doing Time (1993), for example, inmates at a maximum-security prison in New York, with Lacy's guidance, designed a series of shrines in old cars representing the realities of domestic violence: an 'abuse' car, scattered with household implements of torture, like a belt or a kettle of scalding water (a few of which are on view); a 'healing' car, the steering wheel garlanded with toys, which the inmates insisted on building. Doing Time calls our attention to the complex truths of chronic domestic abuse and the sometimes-equal horrors of escaping a deadly situation: the parallel sentences of jail and bad marriages. For their part, the women literally learned to use tools: jigsaws, drills and wrenches. It's 'art therapy'. But the soundtrack for Underground (1993), documenting a similar car-sculpture project in Pittsburgh, features many moments of sincere realization and relief, confessed into a special answering machine. Again and again, documentation of Lacy's projects includes the refrain: 'I'm not alone in my pain.'
Suzanne Lacy, De tu Puño y Letra, 2014-15, performance at Plaza Belmote bullring, Quito, Ecuador. Courtesy: © Suzanne Lacy Studio
Lacy invites women to share their distress – with her, with one another. They grow and, maybe, listening in, so do we. But where is the artist? Her work purveys innocent altruism, on the one hand; on the other, it amplifies the convolution of personal relationships, especially sexual ones. The terror of rape and femicide anchor the show in darkness. Its highlight is the communion of women – abused or aging or just worn down – feeling seen and heard. The artist is the third element in this three-body problem of receiving good love and respect. Lacy doesn't command the tendencies and cycles of human interaction: she designs an art-ecosystem in which she enters into other women's orbits or invites them to join hers. Lacy acts as a sort of interdependent, arbitrating or mediating figure in pre-existing relationships: a friend of a friend and her friend in International Dinner Party; a hooker and her john in Prostitution Notes (1974).
Prostitution Notes exemplifies another quality that sets Lacy's work apart in the crowded genre of social practice. For the piece, documented on butcher paper with maps and matchbooks, the artist studied the routines of Hollywood sex workers, interviewing them over lunch or coffee, watching them work the streets. To her credit, Lacy allowed the project to change her biases, realizing in the end that their problems (with men, police, money) weren't so different from hers. These moments of seemingly real vulnerability on the artist's part make it OK for us to enter the compact. Even here, decades later, in the cool room of reason that is the Queens Museum, we can still be moved.
Suzanne Lacy, The Crystal Quilt, 1987, performance at IDS Center Crystal Court, Minneapolis. Courtesy: © Suzanne Lacy Studio
But who is this 'we'? Is Lacy's art, made by a cishet white woman , vulnerable to the intersectional blind spots of second-wave feminists? We have to wonder. We must also bear in mind the second-wave origins of Lacy's thinking, while allowing it to unfold in the present – to be both retrospective and prospective. Even where the projects excluded male participants (usually for the good reason of creating a safe space), their imagined, vaguely defined viewing public includes everyone. A recent project, in fact, turns the question on men: De tu puño y letra (By Your Own Hand, 2014–15/2019) gathers Ecuadorian men in a bull ring, where they read harrowing accounts from women who survived domestic abuse, in an inversion of the machismo that ostensibly fuels such violence. 'I am sharing my great pain with you,' says one man, voicing an anonymous woman. Vertical screens surround the viewer.
The Queens Museum was built as an exhibition and recreation hall for the 1939 World's Fair and served, until 1950, as the temporary home of the fledgling United Nations General Assembly. Lacy's exhibition feels well-sited here. In some ways, her work is locked in the decade when the Twin Towers were first erected; a replica of them still stands in the Queens Museum's intricate Panorama of New York (1964–ongoing). The installation, though – both because of and despite this weight – models the porousness and persistence of history.
Suzanne Lacy's 'The Medium Is Not the Only Message' is on view at the Queens Museum, New York, until 14 August
Main image: Suzanne Lacy, Julia London, Jan Chattler, Joya Cory, Natalia Rivas, Ngoh Spencer, and Carol Szego, Freeze Frame: Room for Living Room, 1982, performance. Courtesy: © Suzanne Lacy Studio; photograph: f-stop Fitzgerald
TRAVIS DIEHL
Travis Diehl is online editor at X-TRA. He is a recipient of the Andy Warhol Foundation Arts Writers Grant and the Rabkin Prize in Visual Arts Journalism.
Suzanne Lacy
Exhibition Reviews , US Reviews
The Top 10 Shows in the US of 2022
From a show of Ukrainian women artists at Fridman Gallery, New York to Kaari Upson's first posthumous exhibition at Sprüth Magers, Los Angeles, here are the best shows of the year
BY Lisa Yin Zhang | 16 DEC 22
Spencer Longo Traces Tabloid Politics
The artist's new show at King's Leap, New York, uses 'TIME' magazine to unpack recent historical narratives
BY Travis Diehl | 14 JUL 22
Laurie Anderson's Chorus of Stories
At the Hirshhorn Musuem, Washington, D.C., the artist presents new and recent works that employ storytelling as its primary medium
BY Logan Lockner | 10 JUN 22
Critic's Guides , Exhibition Reviews , US Reviews
The Best Shows to See in New York During Frieze Week 2022
From Lorraine O'Grady's photocollages from the 1990s at Alexander Gray Associates to Kiyan Williams's deep-fried American Flags at Lyles & King, these are the must-see shows
BY Travis Diehl | 20 MAY 22
'The Bodywork of Hospitality' Sees Communal Care as a Civic Obligation
A group show curated by Sylvie Fortin at the Bemis Center for Contemporary Arts, Omaha, delves into the politics of social health
BY Travis Diehl | 28 FEB 22
Diane Severin Nguyen's Millennial Revolution
At SculptureCenter, New York, the artist's new, K-Pop inspired, video sees revolution as an illness
BY Travis Diehl | 01 DEC 21
Kenjiro Okazaki's Landscapes of Time and Space
The artist speaks on his latest series of small, abstract tableaux, reflecting on the history of landscape painting and why uncertainty can create a sense purpose
BY Travis Diehl AND Kenjiro Okazaki | 03 NOV 21
Janiva Ellis Challenges White Supremacy's Influence on Visual Culture
At the Institute of Contemporary Art, Miami, the artist presents a suite of new works that confronts the notion of 'white angst' in western iconography
Books , Opinion
'The Hard Crowd' Reveals Rachel Kushner's Literary Life Through Death
Her latest book, a collection of 19 essays that spans art criticism, journalism and memoir, is an exhaustive examination of what it means to write
Hugo Crosthwaite Animates Disillusion and Resilience at the US-Mexico Border
At Luis De Jesus, Los Angeles, the artist displays figurines, paintings and animations that draw on the physical, psychological and cultural landscapes of borderlands
BY Armando Pulido | 26 JAN 23
Brad Phillips's Cinematic Myth-Making
At de boer gallery, Los Angeles, the artist plumbs personal documentation and film to create photorealistic paintings that destabilize truthhood
BY Gracie Hadland | 24 JAN 23
Shows to See in the US this January
From a survey of Deana Lawson to an international exhibition foregrounding US imperialism since 1945, here are the top shows to see across the US
BY frieze | 13 JAN 23
Deana Lawson Deftly Subverts Photographic Subgenres
The artist's survey at High Museum of Art, Atlanta, draws from original and reprinted photography to challenge the viewer's expectations of Black photographic subjects
BY Lauren DeLand | 10 JAN 23

redpajama_set_name: RedPajamaCommonCrawl


text:
Residency & Citizenship
Citizenship By Descent
Golden Visa Programs
8 Easiest Citizenships & Passports To Get
How To Get EU Citizenship In 2022
4 Ways To Get A Second Passport
Dual Citizenship In the USA
Puerto Rico Tax Incentives 2022
Passport Ranking
Antigua Citizenship By Investment
Grenada Citizenship By Investment
St Lucia Citizenship By Investment
St Kitts Citizenship By Investment
Turkey Citizenship By Investment
CBI Pricing Calculator
Plan B Resources
Plan B Strategies
Global Explorer – World Map
Cost of Living Index 2022
Cheapest Ways & Places To Buy Gold
4 Powerful Estate Planning Strategies for 2022
Lowest tax states 2022
Tax Reduction
Retirement Planning Guide 2022
Top 10 Retirement Visa Programs 2022
Digital Nomad Visas 2022
Should You Buy Silver In 2022?
Alternative Investment Guide
Our new logo features a sextant, an ancient device used for nautical navigation. Our aim is to help you navigate this uncertain world.
Sovereign Confidential
The 4th Pillar
Speaking Engagement – James
Speaking Engagements – Viktorija
My ridiculous saga to refuse free government PPP money
Bahia Beach, Puerto Rico
A few months ago when the US government announced its 'Paycheck Protection Program' for small businesses, my first thought was, "This is going to be seriously abused."
PPP, as it's called, was the crown jewel in Uncle Sam's efforts to sprinkle trillions of dollars around the US economy; the idea was that the government would guarantee loans to small businesses… and they specifically wrote the legislation for the loans to NOT be paid back.
Anyone who borrowed money under this program could have the debt forgiven as long as they met some very basic requirements.
PPP was abused almost immediately. More than $1 billion was paid out to dead people… and so far the government hasn't even bothered trying to reclaim that money.
There's also been rampant fraud, with countless people creating fake businesses with fictitious employees so that they could rake in all that free money.
And countless more people took money that frankly didn't need it.
I didn't take single penny and never had any interest in doing so. I do very well and, unlike Kanye West and the Church of Scientology, I don't need free money from taxpayers.
But as a matter of curiosity, I applied for a PPP loan back in March because I wanted to see what the process was like.
How much money would they hand out? Would there be any due diligence or lending standards?
A lot of people might not realize this, but it's seriously hard to lend that much money. The government set aside hundreds and hundreds of billions of dollars for PPP… which means they would have to make tens of millions of loans.
That's incredibly difficult to do.
By comparison, giant financial institutions like Bank of America or JP Morgan Chase might do a million loans in an entire year.
The federal government is trying to make tens of millions of loans in a matter of months.
Obviously something is going to slip through the cracks, and I wanted to go through the process to find out where those cracks were.
When I filled out a PPP application for one of my companies, the process was pretty straightforward.
And in a matter of days I received a response: I had been approved, and the government was ready to loan me a six-figure sum.
This was really surprising to me given that the company I applied for is (a) profitable, and (b) has only one employee: me.
But nevertheless, the government saw fit to offer me money at terms that did not require me to pay the money back… ever.
The email included instructions and requested that I sign the loan documents, after which I would receive the money.
Again, I had no intention of borrowing any money from this program… I was just curious about the process. So I ignored the email and did nothing, assuming they would go away.
But a few weeks later I received an email from an employee at the SBA inquiring about the loan documents.
I wrote back and explicitly told them to "terminate the loan application." He replied and confirmed that, yes, they will withdraw my application.
But then, in late June, I received another email from an SBA employee as follows:
I am doing a follow up regarding the closing documents for your business loan. When will you be signing the documents?
Please reply to this email as soon as possible.
Jeez. So I called and left a voicemail stating, yet again, that I would not be moving forward with the loan.
Finally, three days later, I received an official letter from the SBA confirming that my loan application had been terminated.
But that's not the end of the story.
Last week I received another email from the SBA, saying "We are reaching out with good news . . ."
They went on to explain that they wanted to send me up to $10,000 as part of another program, the Economic Injury Disaster Loan program, or EIDL.
And since they already had my bank details, they were going to deposit the funds in my account within three business days.
Amazing. I didn't even have to do anything and they were just going to put money in my account.
Seriously, why is it so hard is to REFUSE free government money?
The email contained instructions about how to opt-out:
If you do not wish to receive the EIDL Advance, please contact us by email within three (3) business days of the date of this communication at EIDLAdvanceOptOut@sba.gov, and reference your 10-digit application number and "Stop EIDL Advance" in the subject line.
So then I had to go and hunt for this 10-digit application number (which wasn't contained in the email) and follow their instructions to refuse the money.
I haven't heard anything back from the SBA confirming that they received my [third] cancellation request. But fortunately no money has been transferred to my account… yet.
It is pretty incredible, though… these people are practically pounding down my door trying to hand me free money, even though I've told them three times that I'm not interested.
And I have to imagine I'm one of millions of others who has had this experience.
There's no way this program isn't being massively abused, and it's ultimately going to cost taxpayers hundreds of billions of dollars, just to start.
What's amazing is how little anyone seems to care.
The national debt in the US has increased by trillions of dollars since the pandemic hit in early March. There's hardly a word about it in the media.
Total public debt in the US is nearly $26.5 trillion, which constitutes 122% of GDP. That's the highest level EVER.
Even during World War II, the national debt in the US peaked at 118% of GDP.
So the US government is in deeper debt now giving people free money from Covid than when they were fighting the Nazis.
Something is seriously wrong with this way of thinking.
Simon Black, as James Hickman is more commonly known, is the Founder of Sovereign Research.
He is an international investor, entrepreneur, and a free man. His daily e-letter, Sovereign Letters, draws on his life, business and travel experiences to help readers gain more freedom, more opportunity, and more prosperity.
Hickman is a lifelong entrepreneur and investor that's traveled to more than 120 countries on all seven continents. In addition, he's started, invested in, or acquired businesses all over the world.
He is a graduate of the United States Military Academy at West Point and served in the US Army as an intelligence officer during Operation Enduring Freedom and Operation Iraqi Freedom.
Hickman founded a South America-based agriculture company that has become one of the leading producers in its industry. A few years ago, he acquired a prominent retail brand in Australia, purchasing the business from the former 1980s era rock star who founded it.
His other business ventures have included starting a boutique, private investment bank that boasts some of the highest levels of liquidity and solvency in the world, and investing in companies from Colombia to Uzbekistan. He also serves on numerous Boards of Directors, and previously served as Chairman of company listed on a major stock exchange.
Writing under the pen name Simon Black, he has also written extensively on business incorporation and tax residency establishment in Puerto Rico, and is a proponent of investing in gold and silver as a hedge against inflation.
He is a also a prolific writer on topics ranging from second residency and citizenship, Golden Visas and portfolio diversification, to estate and retirement planning, asset protection, tax optimization and US Opportunity Zones.
Browse more articles by Simon
James Hickman (aka Simon Black) is an international investor, entrepreneur, and founder of Sovereign Research. His free daily e-letter Notes from the Field is about using the experiences from his life and travels to help you achieve more freedom, make more money, keep more of it, and protect it all from bankrupt governments.
Get our latest strategies delivered
straight to your inbox for free.
Discover our most read content below...
The Eight-State Suicide Pact is advancing quickly…
By the early 300s AD, ancient Rome's population was in significant decline. Modern historians haven't nailed down a precise
So you're telling me there's a chance…
As a member of the Boards of Directors of several companies, I regularly attend board meetings to help oversee
Second Residency
A potential property option for Italy's Golden Visa program in 2023?
When discussing the best places to live or retire in Europe, it's impossible to overlook Italy. Boasting gorgeous landscapes,
KEY COMPANY LOCATIONS
Citizenship By Ancestry
Romanian Citizenship
Greek Citizenship
Serbian Citizenship
Portuguese Citizenship
Portugal D7 Passive Income Visa
How To Get Panama Residency
How To Get Mexican Residency
Top EU Golden Visa Programs
How To Get Spanish Residency
Cheapest Places To Buy Precious Metals
How To Get EU Citizenship
8 Easiest Citizenships To Get
Our premium products
Sovereign Letters (Free)
© 2022 Blacksmith Global Ltd. – All rights reserved
TrustScore 4.8 | +70 reviews