diff --git a/.gitignore b/.gitignore index c39be60e..1ca95717 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -logs/ node_modules/ npm-debug.log diff --git a/Gruntfile.js b/Gruntfile.js index 3855b82a..4eee9155 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,20 +1,27 @@ module.exports = function(grunt) { - // Project configuration. + var files = ["*.js", "lib/*"]; grunt.initConfig({ - pkg: grunt.file.readJSON("package.json"), + jshint: { + files: files + }, uglify: { js: { files: { "client/js/components.min.js": ["client/components/*.js"] } } + }, + watch: { + files: files, + tasks: ["default"] } }); - - // Load and run uglify. - grunt.loadNpmTasks("grunt-contrib-uglify"); + ["jshint", "uglify", "watch"] + .forEach(function(task) { + grunt.loadNpmTasks("grunt-contrib-" + task); + }); grunt.registerTask( "default", - ["uglify"] + ["jshint", "uglify"] ); }; diff --git a/README.md b/README.md deleted file mode 100644 index e69b72ea..00000000 --- a/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Shout - -Run your IRC client on a server and access it from the web browser. This gives you a persistent connection and allows you to chat from multiple devices at the same time. - -### Contributing - -Shout is __open source__ and __open for contributions__. Any sort of help is appreciated! -Come join #shout-irc on Freenode! - -### Screenshots - -![](https://raw.github.com/erming/shout/master/screenshots/shout.png) - -And also.. -[Mobile interface](https://raw.github.com/erming/shout/master/screenshots/shout-mobile.png) - -## Install - -1. Install Node.js and NPM -`sudo apt-get -y install nodejs npm` - -2. Clone the project from GitHub -`git clone http://github.com/erming/shout` - -3. Open folder -`cd shout/` - -4. Install Node packages -`npm install --production` - -5. Run the server -`npm start` or `node index.js` - -6. Open your browser -`http://localhost:9000` - -### Commands - -These are the commands currently implemented: -- [x] /ame -- [x] /amsg -- [x] /close -- [x] /connect -- [x] /deop -- [x] /devoice -- [x] /disconnect -- [x] /invite -- [x] /join -- [x] /kick -- [x] /leave -- [x] /me -- [x] /mode -- [x] /msg -- [x] /nick -- [x] /notice -- [x] /op -- [x] /part -- [x] /partall -- [x] /query -- [x] /quit -- [x] /raw -- [x] /say -- [x] /send -- [x] /server -- [x] /slap -- [x] /topic -- [x] /voice -- [x] /whoami -- [x] /whois - -## Events - -Using [Socket.IO](http://socket.io/) -Events sent from the __server__ to the __browser__: -```javascript -// Event: "join" -// Sent when joining a new channel/query. -socket.emit("join", { - id: 0, - chan: { - id: 0, - name: "", - type: "", - messages: [], - users: [], - } -}); - -// Event: "messages" -// Sent after the server receives a "fetch" request from client. -socket.emit("messages", { - id: 0, - msg: [] -}); - -// Event: "msg" -// Sent when receiving a message. -socket.emit("msg", { - id: 0, - msg: { - time: "", - type: "", - from: "", - text: "", - } -}); - -// Event: "networks" -// Sent upon connecting to the server. -socket.emit("networks", { - networks: [{ - id: 0, - host: "", - name: "", - channels: [], - }] -}); - -// Event: "part" -// Sent when leaving a channel/query. -socket.emit("part", { - id: 0 -}); - -// Event: "users" -// Sent whenever the list of users changes. -socket.emit("users", { - id: 0, - users: [{ - mode: "", - name: "", - }] -}); -``` - -## License - -Available under [the MIT license](http://mths.be/mit). diff --git a/client/audio/pop.ogg b/client/audio/pop.ogg deleted file mode 100644 index 1fe623f5..00000000 Binary files a/client/audio/pop.ogg and /dev/null differ diff --git a/client/components/URI.js b/client/components/URI.js deleted file mode 100644 index f13ddeac..00000000 --- a/client/components/URI.js +++ /dev/null @@ -1,2006 +0,0 @@ -/*! - * URI.js - Mutating URLs - * - * Version: 1.13.2 - * - * Author: Rodney Rehm - * Web: http://medialize.github.io/URI.js/ - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * GPL v3 http://opensource.org/licenses/GPL-3.0 - * - */ -(function (root, factory) { - 'use strict'; - // https://github.com/umdjs/umd/blob/master/returnExports.js - if (typeof exports === 'object') { - // Node - module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains')); - } else if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['./punycode', './IPv6', './SecondLevelDomains'], factory); - } else { - // Browser globals (root is window) - root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root); - } -}(this, function (punycode, IPv6, SLD, root) { - 'use strict'; - /*global location, escape, unescape */ - // FIXME: v2.0.0 renamce non-camelCase properties to uppercase - /*jshint camelcase: false */ - - // save current URI variable, if any - var _URI = root && root.URI; - - function URI(url, base) { - // Allow instantiation without the 'new' keyword - if (!(this instanceof URI)) { - return new URI(url, base); - } - - if (url === undefined) { - if (typeof location !== 'undefined') { - url = location.href + ''; - } else { - url = ''; - } - } - - this.href(url); - - // resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor - if (base !== undefined) { - return this.absoluteTo(base); - } - - return this; - } - - URI.version = '1.13.2'; - - var p = URI.prototype; - var hasOwn = Object.prototype.hasOwnProperty; - - function escapeRegEx(string) { - // https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963 - return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); - } - - function getType(value) { - // IE8 doesn't return [Object Undefined] but [Object Object] for undefined value - if (value === undefined) { - return 'Undefined'; - } - - return String(Object.prototype.toString.call(value)).slice(8, -1); - } - - function isArray(obj) { - return getType(obj) === 'Array'; - } - - function filterArrayValues(data, value) { - var lookup = {}; - var i, length; - - if (isArray(value)) { - for (i = 0, length = value.length; i < length; i++) { - lookup[value[i]] = true; - } - } else { - lookup[value] = true; - } - - for (i = 0, length = data.length; i < length; i++) { - if (lookup[data[i]] !== undefined) { - data.splice(i, 1); - length--; - i--; - } - } - - return data; - } - - function arrayContains(list, value) { - var i, length; - - // value may be string, number, array, regexp - if (isArray(value)) { - // Note: this can be optimized to O(n) (instead of current O(m * n)) - for (i = 0, length = value.length; i < length; i++) { - if (!arrayContains(list, value[i])) { - return false; - } - } - - return true; - } - - var _type = getType(value); - for (i = 0, length = list.length; i < length; i++) { - if (_type === 'RegExp') { - if (typeof list[i] === 'string' && list[i].match(value)) { - return true; - } - } else if (list[i] === value) { - return true; - } - } - - return false; - } - - function arraysEqual(one, two) { - if (!isArray(one) || !isArray(two)) { - return false; - } - - // arrays can't be equal if they have different amount of content - if (one.length !== two.length) { - return false; - } - - one.sort(); - two.sort(); - - for (var i = 0, l = one.length; i < l; i++) { - if (one[i] !== two[i]) { - return false; - } - } - - return true; - } - - URI._parts = function() { - return { - protocol: null, - username: null, - password: null, - hostname: null, - urn: null, - port: null, - path: null, - query: null, - fragment: null, - // state - duplicateQueryParameters: URI.duplicateQueryParameters, - escapeQuerySpace: URI.escapeQuerySpace - }; - }; - // state: allow duplicate query parameters (a=1&a=1) - URI.duplicateQueryParameters = false; - // state: replaces + with %20 (space in query strings) - URI.escapeQuerySpace = true; - // static properties - URI.protocol_expression = /^[a-z][a-z0-9.+-]*$/i; - URI.idn_expression = /[^a-z0-9\.-]/i; - URI.punycode_expression = /(xn--)/i; - // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care? - URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; - // credits to Rich Brown - // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096 - // specification: http://www.ietf.org/rfc/rfc4291.txt - URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; - // expression used is "gruber revised" (@gruber v2) determined to be the - // best solution in a regex-golf we did a couple of ages ago at - // * http://mathiasbynens.be/demo/url-regex - // * http://rodneyrehm.de/t/url-regex.html - URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig; - URI.findUri = { - // valid "scheme://" or "www." - start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi, - // everything up to the next whitespace - end: /[\s\r\n]|$/, - // trim trailing punctuation captured by end RegExp - trim: /[`!()\[\]{};:'".,<>?«»“”„‘’]+$/ - }; - // http://www.iana.org/assignments/uri-schemes.html - // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports - URI.defaultPorts = { - http: '80', - https: '443', - ftp: '21', - gopher: '70', - ws: '80', - wss: '443' - }; - // allowed hostname characters according to RFC 3986 - // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded - // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . - - URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/; - // map DOM Elements to their URI attribute - URI.domAttributes = { - 'a': 'href', - 'blockquote': 'cite', - 'link': 'href', - 'base': 'href', - 'script': 'src', - 'form': 'action', - 'img': 'src', - 'area': 'href', - 'iframe': 'src', - 'embed': 'src', - 'source': 'src', - 'track': 'src', - 'input': 'src' // but only if type="image" - }; - URI.getDomAttribute = function(node) { - if (!node || !node.nodeName) { - return undefined; - } - - var nodeName = node.nodeName.toLowerCase(); - // should only expose src for type="image" - if (nodeName === 'input' && node.type !== 'image') { - return undefined; - } - - return URI.domAttributes[nodeName]; - }; - - function escapeForDumbFirefox36(value) { - // https://github.com/medialize/URI.js/issues/91 - return escape(value); - } - - // encoding / decoding according to RFC3986 - function strictEncodeURIComponent(string) { - // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent - return encodeURIComponent(string) - .replace(/[!'()*]/g, escapeForDumbFirefox36) - .replace(/\*/g, '%2A'); - } - URI.encode = strictEncodeURIComponent; - URI.decode = decodeURIComponent; - URI.iso8859 = function() { - URI.encode = escape; - URI.decode = unescape; - }; - URI.unicode = function() { - URI.encode = strictEncodeURIComponent; - URI.decode = decodeURIComponent; - }; - URI.characters = { - pathname: { - encode: { - // RFC3986 2.1: For consistency, URI producers and normalizers should - // use uppercase hexadecimal digits for all percent-encodings. - expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig, - map: { - // -._~!'()* - '%24': '$', - '%26': '&', - '%2B': '+', - '%2C': ',', - '%3B': ';', - '%3D': '=', - '%3A': ':', - '%40': '@' - } - }, - decode: { - expression: /[\/\?#]/g, - map: { - '/': '%2F', - '?': '%3F', - '#': '%23' - } - } - }, - reserved: { - encode: { - // RFC3986 2.1: For consistency, URI producers and normalizers should - // use uppercase hexadecimal digits for all percent-encodings. - expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig, - map: { - // gen-delims - '%3A': ':', - '%2F': '/', - '%3F': '?', - '%23': '#', - '%5B': '[', - '%5D': ']', - '%40': '@', - // sub-delims - '%21': '!', - '%24': '$', - '%26': '&', - '%27': '\'', - '%28': '(', - '%29': ')', - '%2A': '*', - '%2B': '+', - '%2C': ',', - '%3B': ';', - '%3D': '=' - } - } - } - }; - URI.encodeQuery = function(string, escapeQuerySpace) { - var escaped = URI.encode(string + ''); - if (escapeQuerySpace === undefined) { - escapeQuerySpace = URI.escapeQuerySpace; - } - - return escapeQuerySpace ? escaped.replace(/%20/g, '+') : escaped; - }; - URI.decodeQuery = function(string, escapeQuerySpace) { - string += ''; - if (escapeQuerySpace === undefined) { - escapeQuerySpace = URI.escapeQuerySpace; - } - - try { - return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string); - } catch(e) { - // we're not going to mess with weird encodings, - // give up and return the undecoded original string - // see https://github.com/medialize/URI.js/issues/87 - // see https://github.com/medialize/URI.js/issues/92 - return string; - } - }; - URI.recodePath = function(string) { - var segments = (string + '').split('/'); - for (var i = 0, length = segments.length; i < length; i++) { - segments[i] = URI.encodePathSegment(URI.decode(segments[i])); - } - - return segments.join('/'); - }; - URI.decodePath = function(string) { - var segments = (string + '').split('/'); - for (var i = 0, length = segments.length; i < length; i++) { - segments[i] = URI.decodePathSegment(segments[i]); - } - - return segments.join('/'); - }; - // generate encode/decode path functions - var _parts = {'encode':'encode', 'decode':'decode'}; - var _part; - var generateAccessor = function(_group, _part) { - return function(string) { - return URI[_part](string + '').replace(URI.characters[_group][_part].expression, function(c) { - return URI.characters[_group][_part].map[c]; - }); - }; - }; - - for (_part in _parts) { - URI[_part + 'PathSegment'] = generateAccessor('pathname', _parts[_part]); - } - - URI.encodeReserved = generateAccessor('reserved', 'encode'); - - URI.parse = function(string, parts) { - var pos; - if (!parts) { - parts = {}; - } - // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment] - - // extract fragment - pos = string.indexOf('#'); - if (pos > -1) { - // escaping? - parts.fragment = string.substring(pos + 1) || null; - string = string.substring(0, pos); - } - - // extract query - pos = string.indexOf('?'); - if (pos > -1) { - // escaping? - parts.query = string.substring(pos + 1) || null; - string = string.substring(0, pos); - } - - // extract protocol - if (string.substring(0, 2) === '//') { - // relative-scheme - parts.protocol = null; - string = string.substring(2); - // extract "user:pass@host:port" - string = URI.parseAuthority(string, parts); - } else { - pos = string.indexOf(':'); - if (pos > -1) { - parts.protocol = string.substring(0, pos) || null; - if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) { - // : may be within the path - parts.protocol = undefined; - } else if (parts.protocol === 'file') { - // the file scheme: does not contain an authority - string = string.substring(pos + 3); - } else if (string.substring(pos + 1, pos + 3) === '//') { - string = string.substring(pos + 3); - - // extract "user:pass@host:port" - string = URI.parseAuthority(string, parts); - } else { - string = string.substring(pos + 1); - parts.urn = true; - } - } - } - - // what's left must be the path - parts.path = string; - - // and we're done - return parts; - }; - URI.parseHost = function(string, parts) { - // extract host:port - var pos = string.indexOf('/'); - var bracketPos; - var t; - - if (pos === -1) { - pos = string.length; - } - - if (string.charAt(0) === '[') { - // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6 - // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts - // IPv6+port in the format [2001:db8::1]:80 (for the time being) - bracketPos = string.indexOf(']'); - parts.hostname = string.substring(1, bracketPos) || null; - parts.port = string.substring(bracketPos + 2, pos) || null; - if (parts.port === '/') { - parts.port = null; - } - } else if (string.indexOf(':') !== string.lastIndexOf(':')) { - // IPv6 host contains multiple colons - but no port - // this notation is actually not allowed by RFC 3986, but we're a liberal parser - parts.hostname = string.substring(0, pos) || null; - parts.port = null; - } else { - t = string.substring(0, pos).split(':'); - parts.hostname = t[0] || null; - parts.port = t[1] || null; - } - - if (parts.hostname && string.substring(pos).charAt(0) !== '/') { - pos++; - string = '/' + string; - } - - return string.substring(pos) || '/'; - }; - URI.parseAuthority = function(string, parts) { - string = URI.parseUserinfo(string, parts); - return URI.parseHost(string, parts); - }; - URI.parseUserinfo = function(string, parts) { - // extract username:password - var firstSlash = string.indexOf('/'); - /*jshint laxbreak: true */ - var pos = firstSlash > -1 - ? string.lastIndexOf('@', firstSlash) - : string.indexOf('@'); - /*jshint laxbreak: false */ - var t; - - // authority@ must come before /path - if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) { - t = string.substring(0, pos).split(':'); - parts.username = t[0] ? URI.decode(t[0]) : null; - t.shift(); - parts.password = t[0] ? URI.decode(t.join(':')) : null; - string = string.substring(pos + 1); - } else { - parts.username = null; - parts.password = null; - } - - return string; - }; - URI.parseQuery = function(string, escapeQuerySpace) { - if (!string) { - return {}; - } - - // throw out the funky business - "?"[name"="value"&"]+ - string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, ''); - - if (!string) { - return {}; - } - - var items = {}; - var splits = string.split('&'); - var length = splits.length; - var v, name, value; - - for (var i = 0; i < length; i++) { - v = splits[i].split('='); - name = URI.decodeQuery(v.shift(), escapeQuerySpace); - // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters - value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null; - - if (items[name]) { - if (typeof items[name] === 'string') { - items[name] = [items[name]]; - } - - items[name].push(value); - } else { - items[name] = value; - } - } - - return items; - }; - - URI.build = function(parts) { - var t = ''; - - if (parts.protocol) { - t += parts.protocol + ':'; - } - - if (!parts.urn && (t || parts.hostname)) { - t += '//'; - } - - t += (URI.buildAuthority(parts) || ''); - - if (typeof parts.path === 'string') { - if (parts.path.charAt(0) !== '/' && typeof parts.hostname === 'string') { - t += '/'; - } - - t += parts.path; - } - - if (typeof parts.query === 'string' && parts.query) { - t += '?' + parts.query; - } - - if (typeof parts.fragment === 'string' && parts.fragment) { - t += '#' + parts.fragment; - } - return t; - }; - URI.buildHost = function(parts) { - var t = ''; - - if (!parts.hostname) { - return ''; - } else if (URI.ip6_expression.test(parts.hostname)) { - t += '[' + parts.hostname + ']'; - } else { - t += parts.hostname; - } - - if (parts.port) { - t += ':' + parts.port; - } - - return t; - }; - URI.buildAuthority = function(parts) { - return URI.buildUserinfo(parts) + URI.buildHost(parts); - }; - URI.buildUserinfo = function(parts) { - var t = ''; - - if (parts.username) { - t += URI.encode(parts.username); - - if (parts.password) { - t += ':' + URI.encode(parts.password); - } - - t += '@'; - } - - return t; - }; - URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) { - // according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html - // being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed - // the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax! - // URI.js treats the query string as being application/x-www-form-urlencoded - // see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type - - var t = ''; - var unique, key, i, length; - for (key in data) { - if (hasOwn.call(data, key) && key) { - if (isArray(data[key])) { - unique = {}; - for (i = 0, length = data[key].length; i < length; i++) { - if (data[key][i] !== undefined && unique[data[key][i] + ''] === undefined) { - t += '&' + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace); - if (duplicateQueryParameters !== true) { - unique[data[key][i] + ''] = true; - } - } - } - } else if (data[key] !== undefined) { - t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace); - } - } - } - - return t.substring(1); - }; - URI.buildQueryParameter = function(name, value, escapeQuerySpace) { - // http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded - // don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization - return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? '=' + URI.encodeQuery(value, escapeQuerySpace) : ''); - }; - - URI.addQuery = function(data, name, value) { - if (typeof name === 'object') { - for (var key in name) { - if (hasOwn.call(name, key)) { - URI.addQuery(data, key, name[key]); - } - } - } else if (typeof name === 'string') { - if (data[name] === undefined) { - data[name] = value; - return; - } else if (typeof data[name] === 'string') { - data[name] = [data[name]]; - } - - if (!isArray(value)) { - value = [value]; - } - - data[name] = data[name].concat(value); - } else { - throw new TypeError('URI.addQuery() accepts an object, string as the name parameter'); - } - }; - URI.removeQuery = function(data, name, value) { - var i, length, key; - - if (isArray(name)) { - for (i = 0, length = name.length; i < length; i++) { - data[name[i]] = undefined; - } - } else if (typeof name === 'object') { - for (key in name) { - if (hasOwn.call(name, key)) { - URI.removeQuery(data, key, name[key]); - } - } - } else if (typeof name === 'string') { - if (value !== undefined) { - if (data[name] === value) { - data[name] = undefined; - } else if (isArray(data[name])) { - data[name] = filterArrayValues(data[name], value); - } - } else { - data[name] = undefined; - } - } else { - throw new TypeError('URI.addQuery() accepts an object, string as the first parameter'); - } - }; - URI.hasQuery = function(data, name, value, withinArray) { - if (typeof name === 'object') { - for (var key in name) { - if (hasOwn.call(name, key)) { - if (!URI.hasQuery(data, key, name[key])) { - return false; - } - } - } - - return true; - } else if (typeof name !== 'string') { - throw new TypeError('URI.hasQuery() accepts an object, string as the name parameter'); - } - - switch (getType(value)) { - case 'Undefined': - // true if exists (but may be empty) - return name in data; // data[name] !== undefined; - - case 'Boolean': - // true if exists and non-empty - var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]); - return value === _booly; - - case 'Function': - // allow complex comparison - return !!value(data[name], name, data); - - case 'Array': - if (!isArray(data[name])) { - return false; - } - - var op = withinArray ? arrayContains : arraysEqual; - return op(data[name], value); - - case 'RegExp': - if (!isArray(data[name])) { - return Boolean(data[name] && data[name].match(value)); - } - - if (!withinArray) { - return false; - } - - return arrayContains(data[name], value); - - case 'Number': - value = String(value); - /* falls through */ - case 'String': - if (!isArray(data[name])) { - return data[name] === value; - } - - if (!withinArray) { - return false; - } - - return arrayContains(data[name], value); - - default: - throw new TypeError('URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter'); - } - }; - - - URI.commonPath = function(one, two) { - var length = Math.min(one.length, two.length); - var pos; - - // find first non-matching character - for (pos = 0; pos < length; pos++) { - if (one.charAt(pos) !== two.charAt(pos)) { - pos--; - break; - } - } - - if (pos < 1) { - return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : ''; - } - - // revert to last / - if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') { - pos = one.substring(0, pos).lastIndexOf('/'); - } - - return one.substring(0, pos + 1); - }; - - URI.withinString = function(string, callback, options) { - options || (options = {}); - var _start = options.start || URI.findUri.start; - var _end = options.end || URI.findUri.end; - var _trim = options.trim || URI.findUri.trim; - var _attributeOpen = /[a-z0-9-]=["']?$/i; - - _start.lastIndex = 0; - while (true) { - var match = _start.exec(string); - if (!match) { - break; - } - - var start = match.index; - if (options.ignoreHtml) { - // attribut(e=["']?$) - var attributeOpen = string.slice(Math.max(start - 3, 0), start); - if (attributeOpen && _attributeOpen.test(attributeOpen)) { - continue; - } - } - - var end = start + string.slice(start).search(_end); - var slice = string.slice(start, end).replace(_trim, ''); - if (options.ignore && options.ignore.test(slice)) { - continue; - } - - end = start + slice.length; - var result = callback(slice, start, end, string); - string = string.slice(0, start) + result + string.slice(end); - _start.lastIndex = start + result.length; - } - - _start.lastIndex = 0; - return string; - }; - - URI.ensureValidHostname = function(v) { - // Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986) - // they are not part of DNS and therefore ignored by URI.js - - if (v.match(URI.invalid_hostname_characters)) { - // test punycode - if (!punycode) { - throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-] and Punycode.js is not available'); - } - - if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) { - throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]'); - } - } - }; - - // noConflict - URI.noConflict = function(removeAll) { - if (removeAll) { - var unconflicted = { - URI: this.noConflict() - }; - - if (root.URITemplate && typeof root.URITemplate.noConflict === 'function') { - unconflicted.URITemplate = root.URITemplate.noConflict(); - } - - if (root.IPv6 && typeof root.IPv6.noConflict === 'function') { - unconflicted.IPv6 = root.IPv6.noConflict(); - } - - if (root.SecondLevelDomains && typeof root.SecondLevelDomains.noConflict === 'function') { - unconflicted.SecondLevelDomains = root.SecondLevelDomains.noConflict(); - } - - return unconflicted; - } else if (root.URI === this) { - root.URI = _URI; - } - - return this; - }; - - p.build = function(deferBuild) { - if (deferBuild === true) { - this._deferred_build = true; - } else if (deferBuild === undefined || this._deferred_build) { - this._string = URI.build(this._parts); - this._deferred_build = false; - } - - return this; - }; - - p.clone = function() { - return new URI(this); - }; - - p.valueOf = p.toString = function() { - return this.build(false)._string; - }; - - // generate simple accessors - _parts = {protocol: 'protocol', username: 'username', password: 'password', hostname: 'hostname', port: 'port'}; - generateAccessor = function(_part){ - return function(v, build) { - if (v === undefined) { - return this._parts[_part] || ''; - } else { - this._parts[_part] = v || null; - this.build(!build); - return this; - } - }; - }; - - for (_part in _parts) { - p[_part] = generateAccessor(_parts[_part]); - } - - // generate accessors with optionally prefixed input - _parts = {query: '?', fragment: '#'}; - generateAccessor = function(_part, _key){ - return function(v, build) { - if (v === undefined) { - return this._parts[_part] || ''; - } else { - if (v !== null) { - v = v + ''; - if (v.charAt(0) === _key) { - v = v.substring(1); - } - } - - this._parts[_part] = v; - this.build(!build); - return this; - } - }; - }; - - for (_part in _parts) { - p[_part] = generateAccessor(_part, _parts[_part]); - } - - // generate accessors with prefixed output - _parts = {search: ['?', 'query'], hash: ['#', 'fragment']}; - generateAccessor = function(_part, _key){ - return function(v, build) { - var t = this[_part](v, build); - return typeof t === 'string' && t.length ? (_key + t) : t; - }; - }; - - for (_part in _parts) { - p[_part] = generateAccessor(_parts[_part][1], _parts[_part][0]); - } - - p.pathname = function(v, build) { - if (v === undefined || v === true) { - var res = this._parts.path || (this._parts.hostname ? '/' : ''); - return v ? URI.decodePath(res) : res; - } else { - this._parts.path = v ? URI.recodePath(v) : '/'; - this.build(!build); - return this; - } - }; - p.path = p.pathname; - p.href = function(href, build) { - var key; - - if (href === undefined) { - return this.toString(); - } - - this._string = ''; - this._parts = URI._parts(); - - var _URI = href instanceof URI; - var _object = typeof href === 'object' && (href.hostname || href.path || href.pathname); - if (href.nodeName) { - var attribute = URI.getDomAttribute(href); - href = href[attribute] || ''; - _object = false; - } - - // window.location is reported to be an object, but it's not the sort - // of object we're looking for: - // * location.protocol ends with a colon - // * location.query != object.search - // * location.hash != object.fragment - // simply serializing the unknown object should do the trick - // (for location, not for everything...) - if (!_URI && _object && href.pathname !== undefined) { - href = href.toString(); - } - - if (typeof href === 'string') { - this._parts = URI.parse(href, this._parts); - } else if (_URI || _object) { - var src = _URI ? href._parts : href; - for (key in src) { - if (hasOwn.call(this._parts, key)) { - this._parts[key] = src[key]; - } - } - } else { - throw new TypeError('invalid input'); - } - - this.build(!build); - return this; - }; - - // identification accessors - p.is = function(what) { - var ip = false; - var ip4 = false; - var ip6 = false; - var name = false; - var sld = false; - var idn = false; - var punycode = false; - var relative = !this._parts.urn; - - if (this._parts.hostname) { - relative = false; - ip4 = URI.ip4_expression.test(this._parts.hostname); - ip6 = URI.ip6_expression.test(this._parts.hostname); - ip = ip4 || ip6; - name = !ip; - sld = name && SLD && SLD.has(this._parts.hostname); - idn = name && URI.idn_expression.test(this._parts.hostname); - punycode = name && URI.punycode_expression.test(this._parts.hostname); - } - - switch (what.toLowerCase()) { - case 'relative': - return relative; - - case 'absolute': - return !relative; - - // hostname identification - case 'domain': - case 'name': - return name; - - case 'sld': - return sld; - - case 'ip': - return ip; - - case 'ip4': - case 'ipv4': - case 'inet4': - return ip4; - - case 'ip6': - case 'ipv6': - case 'inet6': - return ip6; - - case 'idn': - return idn; - - case 'url': - return !this._parts.urn; - - case 'urn': - return !!this._parts.urn; - - case 'punycode': - return punycode; - } - - return null; - }; - - // component specific input validation - var _protocol = p.protocol; - var _port = p.port; - var _hostname = p.hostname; - - p.protocol = function(v, build) { - if (v !== undefined) { - if (v) { - // accept trailing :// - v = v.replace(/:(\/\/)?$/, ''); - - if (!v.match(URI.protocol_expression)) { - throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]'); - } - } - } - return _protocol.call(this, v, build); - }; - p.scheme = p.protocol; - p.port = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v !== undefined) { - if (v === 0) { - v = null; - } - - if (v) { - v += ''; - if (v.charAt(0) === ':') { - v = v.substring(1); - } - - if (v.match(/[^0-9]/)) { - throw new TypeError('Port "' + v + '" contains characters other than [0-9]'); - } - } - } - return _port.call(this, v, build); - }; - p.hostname = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v !== undefined) { - var x = {}; - URI.parseHost(v, x); - v = x.hostname; - } - return _hostname.call(this, v, build); - }; - - // compound accessors - p.host = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined) { - return this._parts.hostname ? URI.buildHost(this._parts) : ''; - } else { - URI.parseHost(v, this._parts); - this.build(!build); - return this; - } - }; - p.authority = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined) { - return this._parts.hostname ? URI.buildAuthority(this._parts) : ''; - } else { - URI.parseAuthority(v, this._parts); - this.build(!build); - return this; - } - }; - p.userinfo = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined) { - if (!this._parts.username) { - return ''; - } - - var t = URI.buildUserinfo(this._parts); - return t.substring(0, t.length -1); - } else { - if (v[v.length-1] !== '@') { - v += '@'; - } - - URI.parseUserinfo(v, this._parts); - this.build(!build); - return this; - } - }; - p.resource = function(v, build) { - var parts; - - if (v === undefined) { - return this.path() + this.search() + this.hash(); - } - - parts = URI.parse(v); - this._parts.path = parts.path; - this._parts.query = parts.query; - this._parts.fragment = parts.fragment; - this.build(!build); - return this; - }; - - // fraction accessors - p.subdomain = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - // convenience, return "www" from "www.example.org" - if (v === undefined) { - if (!this._parts.hostname || this.is('IP')) { - return ''; - } - - // grab domain and add another segment - var end = this._parts.hostname.length - this.domain().length - 1; - return this._parts.hostname.substring(0, end) || ''; - } else { - var e = this._parts.hostname.length - this.domain().length; - var sub = this._parts.hostname.substring(0, e); - var replace = new RegExp('^' + escapeRegEx(sub)); - - if (v && v.charAt(v.length - 1) !== '.') { - v += '.'; - } - - if (v) { - URI.ensureValidHostname(v); - } - - this._parts.hostname = this._parts.hostname.replace(replace, v); - this.build(!build); - return this; - } - }; - p.domain = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (typeof v === 'boolean') { - build = v; - v = undefined; - } - - // convenience, return "example.org" from "www.example.org" - if (v === undefined) { - if (!this._parts.hostname || this.is('IP')) { - return ''; - } - - // if hostname consists of 1 or 2 segments, it must be the domain - var t = this._parts.hostname.match(/\./g); - if (t && t.length < 2) { - return this._parts.hostname; - } - - // grab tld and add another segment - var end = this._parts.hostname.length - this.tld(build).length - 1; - end = this._parts.hostname.lastIndexOf('.', end -1) + 1; - return this._parts.hostname.substring(end) || ''; - } else { - if (!v) { - throw new TypeError('cannot set domain empty'); - } - - URI.ensureValidHostname(v); - - if (!this._parts.hostname || this.is('IP')) { - this._parts.hostname = v; - } else { - var replace = new RegExp(escapeRegEx(this.domain()) + '$'); - this._parts.hostname = this._parts.hostname.replace(replace, v); - } - - this.build(!build); - return this; - } - }; - p.tld = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (typeof v === 'boolean') { - build = v; - v = undefined; - } - - // return "org" from "www.example.org" - if (v === undefined) { - if (!this._parts.hostname || this.is('IP')) { - return ''; - } - - var pos = this._parts.hostname.lastIndexOf('.'); - var tld = this._parts.hostname.substring(pos + 1); - - if (build !== true && SLD && SLD.list[tld.toLowerCase()]) { - return SLD.get(this._parts.hostname) || tld; - } - - return tld; - } else { - var replace; - - if (!v) { - throw new TypeError('cannot set TLD empty'); - } else if (v.match(/[^a-zA-Z0-9-]/)) { - if (SLD && SLD.is(v)) { - replace = new RegExp(escapeRegEx(this.tld()) + '$'); - this._parts.hostname = this._parts.hostname.replace(replace, v); - } else { - throw new TypeError('TLD "' + v + '" contains characters other than [A-Z0-9]'); - } - } else if (!this._parts.hostname || this.is('IP')) { - throw new ReferenceError('cannot set TLD on non-domain host'); - } else { - replace = new RegExp(escapeRegEx(this.tld()) + '$'); - this._parts.hostname = this._parts.hostname.replace(replace, v); - } - - this.build(!build); - return this; - } - }; - p.directory = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined || v === true) { - if (!this._parts.path && !this._parts.hostname) { - return ''; - } - - if (this._parts.path === '/') { - return '/'; - } - - var end = this._parts.path.length - this.filename().length - 1; - var res = this._parts.path.substring(0, end) || (this._parts.hostname ? '/' : ''); - - return v ? URI.decodePath(res) : res; - - } else { - var e = this._parts.path.length - this.filename().length; - var directory = this._parts.path.substring(0, e); - var replace = new RegExp('^' + escapeRegEx(directory)); - - // fully qualifier directories begin with a slash - if (!this.is('relative')) { - if (!v) { - v = '/'; - } - - if (v.charAt(0) !== '/') { - v = '/' + v; - } - } - - // directories always end with a slash - if (v && v.charAt(v.length - 1) !== '/') { - v += '/'; - } - - v = URI.recodePath(v); - this._parts.path = this._parts.path.replace(replace, v); - this.build(!build); - return this; - } - }; - p.filename = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined || v === true) { - if (!this._parts.path || this._parts.path === '/') { - return ''; - } - - var pos = this._parts.path.lastIndexOf('/'); - var res = this._parts.path.substring(pos+1); - - return v ? URI.decodePathSegment(res) : res; - } else { - var mutatedDirectory = false; - - if (v.charAt(0) === '/') { - v = v.substring(1); - } - - if (v.match(/\.?\//)) { - mutatedDirectory = true; - } - - var replace = new RegExp(escapeRegEx(this.filename()) + '$'); - v = URI.recodePath(v); - this._parts.path = this._parts.path.replace(replace, v); - - if (mutatedDirectory) { - this.normalizePath(build); - } else { - this.build(!build); - } - - return this; - } - }; - p.suffix = function(v, build) { - if (this._parts.urn) { - return v === undefined ? '' : this; - } - - if (v === undefined || v === true) { - if (!this._parts.path || this._parts.path === '/') { - return ''; - } - - var filename = this.filename(); - var pos = filename.lastIndexOf('.'); - var s, res; - - if (pos === -1) { - return ''; - } - - // suffix may only contain alnum characters (yup, I made this up.) - s = filename.substring(pos+1); - res = (/^[a-z0-9%]+$/i).test(s) ? s : ''; - return v ? URI.decodePathSegment(res) : res; - } else { - if (v.charAt(0) === '.') { - v = v.substring(1); - } - - var suffix = this.suffix(); - var replace; - - if (!suffix) { - if (!v) { - return this; - } - - this._parts.path += '.' + URI.recodePath(v); - } else if (!v) { - replace = new RegExp(escapeRegEx('.' + suffix) + '$'); - } else { - replace = new RegExp(escapeRegEx(suffix) + '$'); - } - - if (replace) { - v = URI.recodePath(v); - this._parts.path = this._parts.path.replace(replace, v); - } - - this.build(!build); - return this; - } - }; - p.segment = function(segment, v, build) { - var separator = this._parts.urn ? ':' : '/'; - var path = this.path(); - var absolute = path.substring(0, 1) === '/'; - var segments = path.split(separator); - - if (segment !== undefined && typeof segment !== 'number') { - build = v; - v = segment; - segment = undefined; - } - - if (segment !== undefined && typeof segment !== 'number') { - throw new Error('Bad segment "' + segment + '", must be 0-based integer'); - } - - if (absolute) { - segments.shift(); - } - - if (segment < 0) { - // allow negative indexes to address from the end - segment = Math.max(segments.length + segment, 0); - } - - if (v === undefined) { - /*jshint laxbreak: true */ - return segment === undefined - ? segments - : segments[segment]; - /*jshint laxbreak: false */ - } else if (segment === null || segments[segment] === undefined) { - if (isArray(v)) { - segments = []; - // collapse empty elements within array - for (var i=0, l=v.length; i < l; i++) { - if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) { - continue; - } - - if (segments.length && !segments[segments.length -1].length) { - segments.pop(); - } - - segments.push(v[i]); - } - } else if (v || (typeof v === 'string')) { - if (segments[segments.length -1] === '') { - // empty trailing elements have to be overwritten - // to prevent results such as /foo//bar - segments[segments.length -1] = v; - } else { - segments.push(v); - } - } - } else { - if (v || (typeof v === 'string' && v.length)) { - segments[segment] = v; - } else { - segments.splice(segment, 1); - } - } - - if (absolute) { - segments.unshift(''); - } - - return this.path(segments.join(separator), build); - }; - p.segmentCoded = function(segment, v, build) { - var segments, i, l; - - if (typeof segment !== 'number') { - build = v; - v = segment; - segment = undefined; - } - - if (v === undefined) { - segments = this.segment(segment, v, build); - if (!isArray(segments)) { - segments = segments !== undefined ? URI.decode(segments) : undefined; - } else { - for (i = 0, l = segments.length; i < l; i++) { - segments[i] = URI.decode(segments[i]); - } - } - - return segments; - } - - if (!isArray(v)) { - v = typeof v === 'string' ? URI.encode(v) : v; - } else { - for (i = 0, l = v.length; i < l; i++) { - v[i] = URI.decode(v[i]); - } - } - - return this.segment(segment, v, build); - }; - - // mutating query string - var q = p.query; - p.query = function(v, build) { - if (v === true) { - return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - } else if (typeof v === 'function') { - var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - var result = v.call(this, data); - this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); - this.build(!build); - return this; - } else if (v !== undefined && typeof v !== 'string') { - this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); - this.build(!build); - return this; - } else { - return q.call(this, v, build); - } - }; - p.setQuery = function(name, value, build) { - var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - - if (typeof name === 'object') { - for (var key in name) { - if (hasOwn.call(name, key)) { - data[key] = name[key]; - } - } - } else if (typeof name === 'string') { - data[name] = value !== undefined ? value : null; - } else { - throw new TypeError('URI.addQuery() accepts an object, string as the name parameter'); - } - - this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); - if (typeof name !== 'string') { - build = value; - } - - this.build(!build); - return this; - }; - p.addQuery = function(name, value, build) { - var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - URI.addQuery(data, name, value === undefined ? null : value); - this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); - if (typeof name !== 'string') { - build = value; - } - - this.build(!build); - return this; - }; - p.removeQuery = function(name, value, build) { - var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - URI.removeQuery(data, name, value); - this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); - if (typeof name !== 'string') { - build = value; - } - - this.build(!build); - return this; - }; - p.hasQuery = function(name, value, withinArray) { - var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); - return URI.hasQuery(data, name, value, withinArray); - }; - p.setSearch = p.setQuery; - p.addSearch = p.addQuery; - p.removeSearch = p.removeQuery; - p.hasSearch = p.hasQuery; - - // sanitizing URLs - p.normalize = function() { - if (this._parts.urn) { - return this - .normalizeProtocol(false) - .normalizeQuery(false) - .normalizeFragment(false) - .build(); - } - - return this - .normalizeProtocol(false) - .normalizeHostname(false) - .normalizePort(false) - .normalizePath(false) - .normalizeQuery(false) - .normalizeFragment(false) - .build(); - }; - p.normalizeProtocol = function(build) { - if (typeof this._parts.protocol === 'string') { - this._parts.protocol = this._parts.protocol.toLowerCase(); - this.build(!build); - } - - return this; - }; - p.normalizeHostname = function(build) { - if (this._parts.hostname) { - if (this.is('IDN') && punycode) { - this._parts.hostname = punycode.toASCII(this._parts.hostname); - } else if (this.is('IPv6') && IPv6) { - this._parts.hostname = IPv6.best(this._parts.hostname); - } - - this._parts.hostname = this._parts.hostname.toLowerCase(); - this.build(!build); - } - - return this; - }; - p.normalizePort = function(build) { - // remove port of it's the protocol's default - if (typeof this._parts.protocol === 'string' && this._parts.port === URI.defaultPorts[this._parts.protocol]) { - this._parts.port = null; - this.build(!build); - } - - return this; - }; - p.normalizePath = function(build) { - if (this._parts.urn) { - return this; - } - - if (!this._parts.path || this._parts.path === '/') { - return this; - } - - var _was_relative; - var _path = this._parts.path; - var _leadingParents = ''; - var _parent, _pos; - - // handle relative paths - if (_path.charAt(0) !== '/') { - _was_relative = true; - _path = '/' + _path; - } - - // resolve simples - _path = _path - .replace(/(\/(\.\/)+)|(\/\.$)/g, '/') - .replace(/\/{2,}/g, '/'); - - // remember leading parents - if (_was_relative) { - _leadingParents = _path.substring(1).match(/^(\.\.\/)+/) || ''; - if (_leadingParents) { - _leadingParents = _leadingParents[0]; - } - } - - // resolve parents - while (true) { - _parent = _path.indexOf('/..'); - if (_parent === -1) { - // no more ../ to resolve - break; - } else if (_parent === 0) { - // top level cannot be relative, skip it - _path = _path.substring(3); - continue; - } - - _pos = _path.substring(0, _parent).lastIndexOf('/'); - if (_pos === -1) { - _pos = _parent; - } - _path = _path.substring(0, _pos) + _path.substring(_parent + 3); - } - - // revert to relative - if (_was_relative && this.is('relative')) { - _path = _leadingParents + _path.substring(1); - } - - _path = URI.recodePath(_path); - this._parts.path = _path; - this.build(!build); - return this; - }; - p.normalizePathname = p.normalizePath; - p.normalizeQuery = function(build) { - if (typeof this._parts.query === 'string') { - if (!this._parts.query.length) { - this._parts.query = null; - } else { - this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace)); - } - - this.build(!build); - } - - return this; - }; - p.normalizeFragment = function(build) { - if (!this._parts.fragment) { - this._parts.fragment = null; - this.build(!build); - } - - return this; - }; - p.normalizeSearch = p.normalizeQuery; - p.normalizeHash = p.normalizeFragment; - - p.iso8859 = function() { - // expect unicode input, iso8859 output - var e = URI.encode; - var d = URI.decode; - - URI.encode = escape; - URI.decode = decodeURIComponent; - this.normalize(); - URI.encode = e; - URI.decode = d; - return this; - }; - - p.unicode = function() { - // expect iso8859 input, unicode output - var e = URI.encode; - var d = URI.decode; - - URI.encode = strictEncodeURIComponent; - URI.decode = unescape; - this.normalize(); - URI.encode = e; - URI.decode = d; - return this; - }; - - p.readable = function() { - var uri = this.clone(); - // removing username, password, because they shouldn't be displayed according to RFC 3986 - uri.username('').password('').normalize(); - var t = ''; - if (uri._parts.protocol) { - t += uri._parts.protocol + '://'; - } - - if (uri._parts.hostname) { - if (uri.is('punycode') && punycode) { - t += punycode.toUnicode(uri._parts.hostname); - if (uri._parts.port) { - t += ':' + uri._parts.port; - } - } else { - t += uri.host(); - } - } - - if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') { - t += '/'; - } - - t += uri.path(true); - if (uri._parts.query) { - var q = ''; - for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) { - var kv = (qp[i] || '').split('='); - q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace) - .replace(/&/g, '%26'); - - if (kv[1] !== undefined) { - q += '=' + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace) - .replace(/&/g, '%26'); - } - } - t += '?' + q.substring(1); - } - - t += URI.decodeQuery(uri.hash(), true); - return t; - }; - - // resolving relative and absolute URLs - p.absoluteTo = function(base) { - var resolved = this.clone(); - var properties = ['protocol', 'username', 'password', 'hostname', 'port']; - var basedir, i, p; - - if (this._parts.urn) { - throw new Error('URNs do not have any generally defined hierarchical components'); - } - - if (!(base instanceof URI)) { - base = new URI(base); - } - - if (!resolved._parts.protocol) { - resolved._parts.protocol = base._parts.protocol; - } - - if (this._parts.hostname) { - return resolved; - } - - for (i = 0; (p = properties[i]); i++) { - resolved._parts[p] = base._parts[p]; - } - - if (!resolved._parts.path) { - resolved._parts.path = base._parts.path; - if (!resolved._parts.query) { - resolved._parts.query = base._parts.query; - } - } else if (resolved._parts.path.substring(-2) === '..') { - resolved._parts.path += '/'; - } - - if (resolved.path().charAt(0) !== '/') { - basedir = base.directory(); - resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path; - resolved.normalizePath(); - } - - resolved.build(); - return resolved; - }; - p.relativeTo = function(base) { - var relative = this.clone().normalize(); - var relativeParts, baseParts, common, relativePath, basePath; - - if (relative._parts.urn) { - throw new Error('URNs do not have any generally defined hierarchical components'); - } - - base = new URI(base).normalize(); - relativeParts = relative._parts; - baseParts = base._parts; - relativePath = relative.path(); - basePath = base.path(); - - if (relativePath.charAt(0) !== '/') { - throw new Error('URI is already relative'); - } - - if (basePath.charAt(0) !== '/') { - throw new Error('Cannot calculate a URI relative to another relative URI'); - } - - if (relativeParts.protocol === baseParts.protocol) { - relativeParts.protocol = null; - } - - if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) { - return relative.build(); - } - - if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) { - return relative.build(); - } - - if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) { - relativeParts.hostname = null; - relativeParts.port = null; - } else { - return relative.build(); - } - - if (relativePath === basePath) { - relativeParts.path = ''; - return relative.build(); - } - - // determine common sub path - common = URI.commonPath(relative.path(), base.path()); - - // If the paths have nothing in common, return a relative URL with the absolute path. - if (!common) { - return relative.build(); - } - - var parents = baseParts.path - .substring(common.length) - .replace(/[^\/]*$/, '') - .replace(/.*?\//g, '../'); - - relativeParts.path = parents + relativeParts.path.substring(common.length); - - return relative.build(); - }; - - // comparing URIs - p.equals = function(uri) { - var one = this.clone(); - var two = new URI(uri); - var one_map = {}; - var two_map = {}; - var checked = {}; - var one_query, two_query, key; - - one.normalize(); - two.normalize(); - - // exact match - if (one.toString() === two.toString()) { - return true; - } - - // extract query string - one_query = one.query(); - two_query = two.query(); - one.query(''); - two.query(''); - - // definitely not equal if not even non-query parts match - if (one.toString() !== two.toString()) { - return false; - } - - // query parameters have the same length, even if they're permuted - if (one_query.length !== two_query.length) { - return false; - } - - one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace); - two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace); - - for (key in one_map) { - if (hasOwn.call(one_map, key)) { - if (!isArray(one_map[key])) { - if (one_map[key] !== two_map[key]) { - return false; - } - } else if (!arraysEqual(one_map[key], two_map[key])) { - return false; - } - - checked[key] = true; - } - } - - for (key in two_map) { - if (hasOwn.call(two_map, key)) { - if (!checked[key]) { - // two contains a parameter not present in one - return false; - } - } - } - - return true; - }; - - // state - p.duplicateQueryParameters = function(v) { - this._parts.duplicateQueryParameters = !!v; - return this; - }; - - p.escapeQuerySpace = function(v) { - this._parts.escapeQuerySpace = !!v; - return this; - }; - - return URI; -})); diff --git a/client/components/favico.js b/client/components/favico.js deleted file mode 100644 index 53b45c02..00000000 --- a/client/components/favico.js +++ /dev/null @@ -1,806 +0,0 @@ -/** - * @license MIT - * @fileOverview Favico animations - * @author Miroslav Magda, http://blog.ejci.net - * @version 0.3.4 - */ - -/** - * Create new favico instance - * @param {Object} Options - * @return {Object} Favico object - * @example - * var favico = new Favico({ - * bgColor : '#d00', - * textColor : '#fff', - * fontFamily : 'sans-serif', - * fontStyle : 'bold', - * position : 'down', - * type : 'circle', - * animation : 'slide', - * }); - */ -(function() { - - var Favico = (function(opt) {'use strict'; - opt = (opt) ? opt : {}; - var _def = { - bgColor : '#d00', - textColor : '#fff', - fontFamily : 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,... - fontStyle : 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900 - type : 'circle', - position : 'down', // down, up, left, leftup (upleft) - animation : 'slide', - elementId : false - }; - var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser; - - _browser = {}; - _browser.ff = (/firefox/i.test(navigator.userAgent.toLowerCase())); - _browser.chrome = (/chrome/i.test(navigator.userAgent.toLowerCase())); - _browser.opera = (/opera/i.test(navigator.userAgent.toLowerCase())); - _browser.ie = (/msie/i.test(navigator.userAgent.toLowerCase())) || (/trident/i.test(navigator.userAgent.toLowerCase())); - _browser.supported = (_browser.chrome || _browser.ff || _browser.opera); - - var _queue = []; - _readyCb = function() { - }; - _ready = _stop = false; - /** - * Initialize favico - */ - var init = function() { - //merge initial options - _opt = merge(_def, opt); - _opt.bgColor = hexToRgb(_opt.bgColor); - _opt.textColor = hexToRgb(_opt.textColor); - _opt.position = _opt.position.toLowerCase(); - _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation; - - var isUp = _opt.position.indexOf('up') > -1; - var isLeft = _opt.position.indexOf('left') > -1; - - //transform animation - if (isUp || isLeft) { - for (var i = 0; i < animation.types['' + _opt.animation].length; i++) { - var step = animation.types['' + _opt.animation][i]; - - if (isUp) { - if (step.y < 0.6) { - step.y = step.y - 0.4; - } else { - step.y = step.y - 2 * step.y + (1 - step.w); - } - } - - if (isLeft) { - if (step.x < 0.6) { - step.x = step.x - 0.4; - } else { - step.x = step.x - 2 * step.x + (1 - step.h); - } - } - - animation.types['' + _opt.animation][i] = step; - } - } - _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type; - try { - _orig = link.getIcon(); - //create temp canvas - _canvas = document.createElement('canvas'); - //create temp image - _img = document.createElement('img'); - if (_orig.hasAttribute('href')) { - _img.setAttribute('src', _orig.getAttribute('href')); - //get width/height - _img.onload = function() { - _h = (_img.height > 0) ? _img.height : 32; - _w = (_img.width > 0) ? _img.width : 32; - _canvas.height = _h; - _canvas.width = _w; - _context = _canvas.getContext('2d'); - icon.ready(); - }; - } else { - _img.setAttribute('src', ''); - _h = 32; - _w = 32; - _img.height = _h; - _img.width = _w; - _canvas.height = _h; - _canvas.width = _w; - _context = _canvas.getContext('2d'); - icon.ready(); - } - } catch(e) { - throw 'Error initializing favico. Message: ' + e.message; - } - - }; - /** - * Icon namespace - */ - var icon = {}; - /** - * Icon is ready (reset icon) and start animation (if ther is any) - */ - icon.ready = function() { - _ready = true; - icon.reset(); - _readyCb(); - }; - /** - * Reset icon to default state - */ - icon.reset = function() { - //reset - _queue = []; - _lastBadge = false; - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - //_stop=true; - link.setIcon(_canvas); - //webcam('stop'); - //video('stop'); - }; - /** - * Start animation - */ - icon.start = function() { - if (!_ready || _running) { - return; - } - var finished = function() { - _lastBadge = _queue[0]; - _running = false; - if (_queue.length > 0) { - _queue.shift(); - icon.start(); - } else { - - } - }; - if (_queue.length > 0) { - _running = true; - if (_lastBadge) { - animation.run(_lastBadge.options, function() { - animation.run(_queue[0].options, function() { - finished(); - }, false); - }, true); - } else { - animation.run(_queue[0].options, function() { - finished(); - }, false); - } - } - }; - - /** - * Badge types - */ - var type = {}; - var options = function(opt) { - opt.n = ((typeof opt.n)==='number') ? Math.abs(opt.n|0) : opt.n; - opt.x = _w * opt.x; - opt.y = _h * opt.y; - opt.w = _w * opt.w; - opt.h = _h * opt.h; - opt.len = ("" + opt.n).length; - return opt; - }; - /** - * Generate circle - * @param {Object} opt Badge options - */ - type.circle = function(opt) { - opt = options(opt); - var more = false; - if (opt.len === 2) { - opt.x = opt.x - opt.w * 0.4; - opt.w = opt.w * 1.4; - more = true; - } else if (opt.len >= 3) { - opt.x = opt.x - opt.w * 0.65; - opt.w = opt.w * 1.65; - more = true; - } - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - _context.beginPath(); - _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily; - _context.textAlign = 'center'; - if (more) { - _context.moveTo(opt.x + opt.w / 2, opt.y); - _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y); - _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2); - _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2); - _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h); - _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h); - _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2); - _context.lineTo(opt.x, opt.y + opt.h / 2); - _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y); - } else { - _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI); - } - _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; - _context.fill(); - _context.closePath(); - _context.beginPath(); - _context.stroke(); - _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; - //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - if ((typeof opt.n)==='number' && opt.n > 999) { - _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000) ) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); - } else { - _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - } - _context.closePath(); - }; - /** - * Generate rectangle - * @param {Object} opt Badge options - */ - type.rectangle = function(opt) { - opt = options(opt); - var more = false; - if (opt.len === 2) { - opt.x = opt.x - opt.w * 0.4; - opt.w = opt.w * 1.4; - more = true; - } else if (opt.len >= 3) { - opt.x = opt.x - opt.w * 0.65; - opt.w = opt.w * 1.65; - more = true; - } - _context.clearRect(0, 0, _w, _h); - _context.drawImage(_img, 0, 0, _w, _h); - _context.beginPath(); - _context.font = "bold " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px sans-serif"; - _context.textAlign = 'center'; - _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; - _context.fillRect(opt.x, opt.y, opt.w, opt.h); - _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; - //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - if ((typeof opt.n)==='number' && opt.len > 3) { - _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000) ) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); - } else { - _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); - } - _context.closePath(); - }; - - /** - * Set badge - */ - var badge = function(number, animType) { - _readyCb = function() { - try { - if (typeof(number)==='number' ? (number > 0) : (number !== '')) { - if (animation.types['' + animType]) { - _opt.animation = animType; - } - _queue.push({ - type : 'badge', - options : { - n : number - } - }); - if (_queue.length > 100) { - throw 'Too many badges requests in queue.'; - } - icon.start(); - } else { - icon.reset(); - } - } catch(e) { - throw 'Error setting badge. Message: ' + e.message; - } - }; - if (_ready) { - _readyCb(); - } - }; - - /** - * Set image as icon - */ - var image = function(imageElement) { - _readyCb = function() { - try { - var w = imageElement.width; - var h = imageElement.height; - var newImg = document.createElement('img'); - var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); - newImg.setAttribute('src', imageElement.getAttribute('src')); - newImg.height = (h / ratio); - newImg.width = (w / ratio); - _context.clearRect(0, 0, _w, _h); - _context.drawImage(newImg, 0, 0, _w, _h); - link.setIcon(_canvas); - } catch(e) { - throw 'Error setting image. Message: ' + e.message; - } - }; - if (_ready) { - _readyCb(); - } - }; - /** - * Set video as icon - */ - var video = function(videoElement) { - _readyCb = function() { - try { - if (videoElement === 'stop') { - _stop = true; - icon.reset(); - _stop = false; - return; - } - //var w = videoElement.width; - //var h = videoElement.height; - //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); - videoElement.addEventListener('play', function() { - drawVideo(this); - }, false); - - } catch(e) { - throw 'Error setting video. Message: ' + e.message; - } - }; - if (_ready) { - _readyCb(); - } - }; - /** - * Set video as icon - */ - var webcam = function(action) { - //UR - if (!window.URL || !window.URL.createObjectURL) { - window.URL = window.URL || {}; - window.URL.createObjectURL = function(obj) { - return obj; - }; - } - if (_browser.supported) { - var newVideo = false; - navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; - _readyCb = function() { - try { - if (action === 'stop') { - _stop = true; - icon.reset(); - _stop = false; - return; - } - newVideo = document.createElement('video'); - newVideo.width = _w; - newVideo.height = _h; - navigator.getUserMedia({ - video : true, - audio : false - }, function(stream) { - newVideo.src = URL.createObjectURL(stream); - newVideo.play(); - drawVideo(newVideo); - }, function() { - }); - } catch(e) { - throw 'Error setting webcam. Message: ' + e.message; - } - }; - if (_ready) { - _readyCb(); - } - } - - }; - - /** - * Draw video to context and repeat :) - */ - function drawVideo(video) { - if (video.paused || video.ended || _stop) { - return false; - } - //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl) - try { - _context.clearRect(0, 0, _w, _h); - _context.drawImage(video, 0, 0, _w, _h); - } catch(e) { - - } - setTimeout(drawVideo, animation.duration, video); - link.setIcon(_canvas); - } - - var link = {}; - /** - * Get icon from HEAD tag or create a new element - */ - link.getIcon = function() { - var elm = false; - var url = ''; - //get link element - var getLink = function() { - var link = document.getElementsByTagName('head')[0].getElementsByTagName('link'); - for (var l = link.length, i = (l - 1); i >= 0; i--) { - if ((/icon/i).test(link[i].getAttribute('rel'))) { - return link[i]; - } - } - return false; - }; - if (_opt.elementId) { - //if img element identified by elementId - elm = document.getElementById(_opt.elementId); - elm.setAttribute('href', elm.getAttribute('src')); - } else { - //if link element - elm = getLink(); - if (elm === false) { - elm = document.createElement('link'); - elm.setAttribute('rel', 'icon'); - document.getElementsByTagName('head')[0].appendChild(elm); - } - } - //check if image and link url is on same domain. if not raise error - url = (_opt.elementId) ? elm.src : elm.href; - if (url.indexOf(document.location.hostname) === -1) { - throw new Error('Error setting favicon. Favicon image is on different domain (Icon: ' + url + ', Domain: ' + document.location.hostname + ')'); - } - elm.setAttribute('type', 'image/png'); - return elm; - }; - link.setIcon = function(canvas) { - var url = canvas.toDataURL('image/png'); - if (_opt.elementId) { - //if is attached to element (image) - document.getElementById(_opt.elementId).setAttribute('src', url); - } else { - //if is attached to fav icon - if (_browser.ff || _browser.opera) { - //for FF we need to "recreate" element, atach to dom and remove old - //var originalType = _orig.getAttribute('rel'); - var old = _orig; - _orig = document.createElement('link'); - //_orig.setAttribute('rel', originalType); - if (_browser.opera) { - _orig.setAttribute('rel', 'icon'); - } - _orig.setAttribute('rel', 'icon'); - _orig.setAttribute('type', 'image/png'); - document.getElementsByTagName('head')[0].appendChild(_orig); - _orig.setAttribute('href', url); - if (old.parentNode) { - old.parentNode.removeChild(old); - } - } else { - _orig.setAttribute('href', url); - } - } - }; - - //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139 - //HEX to RGB convertor - function hexToRgb(hex) { - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function(m, r, g, b) { - return r + r + g + g + b + b; - }); - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r : parseInt(result[1], 16), - g : parseInt(result[2], 16), - b : parseInt(result[3], 16) - } : false; - } - - /** - * Merge options - */ - function merge(def, opt) { - var mergedOpt = {}; - var attrname; - for (attrname in def) { - mergedOpt[attrname] = def[attrname]; - } - for (attrname in opt) { - mergedOpt[attrname] = opt[attrname]; - } - return mergedOpt; - } - - /** - * Cross-browser page visibility shim - * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible - */ - function isPageHidden() { - return document.hidden || document.msHidden || document.webkitHidden || document.mozHidden; - } - - /** - * @namespace animation - */ - var animation = {}; - /** - * Animation "frame" duration - */ - animation.duration = 40; - /** - * Animation types (none,fade,pop,slide) - */ - animation.types = {}; - animation.types.fade = [{ - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.0 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.1 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.2 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.3 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.4 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.5 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.6 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.7 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.8 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 0.9 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 1.0 - }]; - animation.types.none = [{ - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 1 - }]; - animation.types.pop = [{ - x : 1, - y : 1, - w : 0, - h : 0, - o : 1 - }, { - x : 0.9, - y : 0.9, - w : 0.1, - h : 0.1, - o : 1 - }, { - x : 0.8, - y : 0.8, - w : 0.2, - h : 0.2, - o : 1 - }, { - x : 0.7, - y : 0.7, - w : 0.3, - h : 0.3, - o : 1 - }, { - x : 0.6, - y : 0.6, - w : 0.4, - h : 0.4, - o : 1 - }, { - x : 0.5, - y : 0.5, - w : 0.5, - h : 0.5, - o : 1 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 1 - }]; - animation.types.popFade = [{ - x : 0.75, - y : 0.75, - w : 0, - h : 0, - o : 0 - }, { - x : 0.65, - y : 0.65, - w : 0.1, - h : 0.1, - o : 0.2 - }, { - x : 0.6, - y : 0.6, - w : 0.2, - h : 0.2, - o : 0.4 - }, { - x : 0.55, - y : 0.55, - w : 0.3, - h : 0.3, - o : 0.6 - }, { - x : 0.50, - y : 0.50, - w : 0.4, - h : 0.4, - o : 0.8 - }, { - x : 0.45, - y : 0.45, - w : 0.5, - h : 0.5, - o : 0.9 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 1 - }]; - animation.types.slide = [{ - x : 0.4, - y : 1, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.9, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.9, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.8, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.7, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.6, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.5, - w : 0.6, - h : 0.6, - o : 1 - }, { - x : 0.4, - y : 0.4, - w : 0.6, - h : 0.6, - o : 1 - }]; - /** - * Run animation - * @param {Object} opt Animation options - * @param {Object} cb Callabak after all steps are done - * @param {Object} revert Reverse order? true|false - * @param {Object} step Optional step number (frame bumber) - */ - animation.run = function(opt, cb, revert, step) { - var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation]; - if (revert === true) { - step = ( typeof step !== 'undefined') ? step : animationType.length - 1; - } else { - step = ( typeof step !== 'undefined') ? step : 0; - } - cb = (cb) ? cb : function() { - }; - if ((step < animationType.length) && (step >= 0)) { - type[_opt.type](merge(opt, animationType[step])); - setTimeout(function() { - if (revert) { - step = step - 1; - } else { - step = step + 1; - } - animation.run(opt, cb, revert, step); - }, animation.duration); - - link.setIcon(_canvas); - } else { - cb(); - return; - } - }; - //auto init - init(); - return { - badge : badge, - video : video, - image : image, - webcam : webcam, - reset : icon.reset - }; - }); - - // AMD / RequireJS - if ( typeof define !== 'undefined' && define.amd) { - define([], function() { - return Favico; - }); - } - // CommonJS - else if ( typeof module !== 'undefined' && module.exports) { - module.exports = Favico; - } - // included directly via - - - - - - - - - - - - - - - - - + + + + + + + + + Shout + + + + + + + + + + + +
+ +
+
+
+
+
+ 00:00 + + + + + join + +
+
+ 00:00 + + + + + + Hello, world! + +
+
+
+ +
+ + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/client/js/chat.js b/client/js/chat.js deleted file mode 100644 index cb3dabe1..00000000 --- a/client/js/chat.js +++ /dev/null @@ -1,508 +0,0 @@ -$(function() { - var chat = $("#chat"); - var sidebar = $("#sidebar"); - - var commands = [ - "/ame", - "/amsg", - "/close", - "/connect", - "/deop", - "/devoice", - "/disconnect", - "/invite", - "/join", - "/kick", - "/leave", - "/mode", - "/msg", - "/nick", - "/notice", - "/op", - "/part", - "/partall", - "/query", - "/quit", - "/raw", - "/say", - "/send", - "/server", - "/slap", - "/topic", - "/voice", - "/whoami", - "/whois", - ]; - - var socket = io.connect(""); - var events = [ - "auth", - "debug", - "join", - "messages", - "msg", - "networks", - "nick", - "part", - "users", - ].forEach(function(e) { - socket.on(e, function(data) { - event(e, data); - }); - }); - - var tpl = []; - function render(name, data) { - tpl[name] = tpl[name] || Handlebars.compile($("#templates ." + name).html()); - return tpl[name](data); - } - - function event(e, data) { - switch (e) { - case "auth": - $("#networks").add(chat).empty(); - $("#login").trigger("click"); - $.cookie("current", null); - break; - - case "debug": - console.log(data); - break; - - case "join": - chat.append(render("windows", {windows: [data.chan]})) - .find(".window") - .last() - .find(".input") - .tabcomplete(complete, {hint: false}) - .history({submit: true}) - .end() - .find(".chat") - .sticky(); - - $("#network-" + data.id) - .append(render("channels", {channels: [data.chan]})) - .find("a") - .last() - .trigger("click"); - break; - - case "messages": - case "msg": - var target = (data.id ? $("#window-" + data.id) : $("#chat .active")) - .find(".messages"); - var html = render( - "messages", - {messages: toArray(data.msg)} - ); - switch (e) { - case "messages": - target.prepend(html); - break; - - case "msg": - target.append(html); - break; - } - break; - - case "networks": - var channels = $.map(data.networks, function(n) { return n.channels; }); - chat.html(render("windows", {windows: channels})) - .find(".input") - .tabcomplete(complete, {hint: false}) - .history({submit: true}) - .end() - .find(".chat") - .sticky(); - - sidebar.addClass("signed-in"); - $("#logout").on("click", function(e) { - e.stopPropagation(); - }); - - var networks = render("networks", {networks: data.networks}); - var current = $("#networks") - .html(networks) - .closest("#sidebar") - .find("a[href='" + $.cookie("current") + "']") - .trigger("click"); - if (!current.length) { - $("#networks") - .find("a") - .last() - .trigger("click"); - } - break; - - case "part": - var chan = $("#channel-" + data.id).add("#window-" + data.id).remove(); - if (!chan.hasClass("active")) { - break; - } - - var next = null; - var z = 0; - $("#main .window").each(function() { - var index = parseInt($(this).css("zIndex")); - if (index > z) { - z = index; - next = this; - } - }); - if (typeof next !== "undefined") { - $("#channel-" + $(next).data("id")).trigger("click"); - } - break; - - case "users": - var target = $("#window-" + data.id); - var json = {name: target.find("h1").html(), users: data.users}; - target.find(".meta") - .replaceWith(render("meta", json)) - .end(); - target.find(".users") - .html(render("users", json)) - .end(); - break; - } - } - - $.cookie.json = true; - $.cookie("settings", $.cookie("settings") || options); - - var settings = $("#settings"); - var options = $.extend({ - join: true, - mode: true, - nick: true, - notification: true, - part: true, - quit: true, - }, $.cookie("settings")); - - for (var i in options) { - if (options[i]) { - settings.find("input[name=" + i + "]").prop("checked", true); - } - } - - settings.on("change", "input", function() { - settings.find("input").each(function() { - var input = $(this); - var name = input.attr("name"); - options[name] = input.prop("checked"); - $.cookie("settings", options); - if ([ - "join", - "nick", - "part", - "mode", - "quit", - ].indexOf(name) !== -1) { - chat.toggleClass("hide-" + name, !input.prop("checked")); - } - }); - }).find("input") - .first() - .trigger("change"); - - setTimeout(function() { - // Enable transitions. - $("body").removeClass("preload"); - }, 500); - - var pop = new Audio(); - pop.src = "/audio/pop.ogg"; - - var favicon = new Favico({ - animation: "none" - }); - - document.addEventListener("visibilitychange", function() { - if (sidebar.find(".highlight").length == 0) { - favicon.badge(""); - } - }); - - var viewport = $("#viewport"); - var touchDevice = (window.screen.width <= 768); - - var z = 1; - sidebar.on("click", "a", function(e) { - e.preventDefault(); - var link = $(this); - var target = link.attr("href"); - if (!target) { - return; - } - - viewport.removeClass(); - sidebar.find(".active").removeClass("active"); - link.addClass("active") - .find(".badge") - .removeClass("highlight") - .empty(); - - var title = link.data("name"); - var header = $("#header"); - header.find("h1").html(title).end().find("#rt").toggle(title.charAt(0) == "#"); - - $.cookie("current", target); - document.title = title + " — Shout"; - - if (sidebar.find(".highlight").length == 0) { - favicon.badge(""); - } - - $("#main .active").removeClass("active"); - var window = $(target) - .css("z-index", z++) - .addClass("active") - .find(".chat") - .scrollToBottom() - .end(); - - if (!touchDevice) { - if (window.is("#sign-in")) { - window.find("input[name='user']").focus(); - } else { - window.find("input:last").focus(); - } - } - }); - - sidebar.on("click", ".close", function() { - var channel = $(this).closest("a"); - var id = parseInt(channel.attr("id").split("-")[1]); - var cmd = "/close"; - if (channel.hasClass("lobby")) { - cmd = "/quit"; - var server = channel - .clone() - .remove("span") - .text() - .trim(); - if (!confirm("Disconnect from " + server + "?")) { - return false; - } - } - socket.emit("input", { - id: id, - text: cmd, - }); - channel.css({ - transition: "none", - opacity: .4 - }); - return false; - }); - - chat.on("append", ".messages", function() { - var messages = $(this); - var id = messages.closest(".window").find(".form").data("target"); - var last = messages.find(".row:last-child"); - if (last.hasClass("highlight")) { - if ($.cookie("settings").notification) { - pop.play(); - } - if (document.hidden) { - favicon.badge("!"); - } - } - - var link = $("#channel-" + id + ":not(.active)"); - if (link.length == 0) { - return; - } - - link.addClass("pulse"); - setTimeout(function() { - link.removeClass("pulse"); - }, 500); - - var type = last.attr("class"); - var ignore = [ - "join", - "part", - "quit", - "nick", - ]; - for (var i = 0; i < ignore.length; i++) { - if (type.indexOf(ignore[i]) !== -1) { - return; - } - } - - var badge = link.find(".badge"); - if (badge.length != 0) { - var i = (parseInt(badge.html()) || 0) + 1; - badge.html(i); - if (last.hasClass("highlight")) { - badge.addClass("highlight"); - } - } - }); - - chat.on("click", ".show-more", function() { - var btn = $(this); - var messages = btn.closest(".chat").find(".messages").children(); - socket.emit("fetch", { - id: btn.data("id"), - count: messages.length, - }); - btn.attr("disabled", true); - }); - - chat.on("click", ".user", function(e) { - e.preventDefault(); - var user = $(this); - var id = user - .closest(".window") - .data("id"); - - // Strip modes. - var name = user.html().trim().replace(/[+%@~]/, ""); - if (name.match(/[#.]|-!-/) != null) { - return; - } - - socket.emit("input", { - id: id, - text: "/whois " + name, - }); - }); - - chat.on("focus", ".input", function() { - $(this).closest(".window").find(".chat").scrollToBottom(); - }); - - chat.on("submit", "form", function(e) { - e.preventDefault(); - var form = $(this); - var input = form.find(".input:not(.hint)"); - var text = input.val(); - if (text == "") { - return; - } - input.val(""); - input.prev(".hint").val(""); - socket.emit("input", { - id: form.data("target"), - text: text, - }); - }); - - chat.on("click", ".toggle", function() { - var chat = $(this) - .toggleClass("open") - .closest(".chat") - .toggleClass($(this).data("type")) - .end(); - }); - - var toggle = "click"; - if (touchDevice) { - toggle = "touchstart"; - } - - $("#rt, #lt").on(toggle, function(e) { - var btn = $(this); - viewport.toggleClass(btn.attr("id").replace("#", "")); - if (viewport.is(".lt, .rt")) { - e.stopPropagation(); - chat.find(".chat").one(toggle, function(e) { - viewport.removeClass("lt rt"); - }); - } - }); - - $("#sign-in-form").on("submit", function(e) { - e.preventDefault(); - var user = $(this).find(".name").val(); - var password = $(this).find(".password").val(); - $.cookie("user", user); - $("#logout").removeClass("hidden"); - socket.emit("auth", { - user: user, - password: password - }); - }).find(".name") - .val($.cookie("user") || ""); - - $("#notification").on("click", function() { - pop.play(); - }); - - function complete(word) { - var words = commands.slice(); - var users = $(this).closest(".window") - .find(".users .user") - .each(function() { - words.push(this.getAttribute("href").slice(1)); - }); - return $.grep( - words, - function(w) { - return !w.toLowerCase().indexOf(word.toLowerCase()); - } - ); - } - - function toArray(val) { - return Array.isArray(val) ? val : [val]; - } - - function escape(text) { - var e = { - "<": "<", - ">": ">" - }; - return text.replace(/[<>]/g, function (c) { - return e[c]; - }); - } - - Handlebars.registerHelper( - "uri", function(text) { - var urls = []; - text = URI.withinString(text, function(url) { - urls.push(url); - return "{" + (urls.length - 1) + "}"; - }); - text = escape(text); - for (var i in urls) { - var url = escape(urls[i]); - text = text.replace( - "{" + i + "}", - "" + url + "" - ); - } - return text; - } - ); - - Handlebars.registerHelper( - "partial", function(id) { - return new Handlebars.SafeString(render(id, this)); - } - ); - - Handlebars.registerHelper( - "equal", function(a, b, opt) { - a = a.toString(); - b = b.toString(); - if (a == b) { - return opt.fn(this); - } - } - ); - - Handlebars.registerHelper( - "contains", function(a, b, opt) { - if (a.indexOf(b) !== -1) { - return opt.fn(this); - } - } - ); -}); diff --git a/client/js/components.min.js b/client/js/components.min.js index 79d9c535..dea6055e 100644 --- a/client/js/components.min.js +++ b/client/js/components.min.js @@ -1,7 +1,3 @@ -!function(a,b){"use strict";"object"==typeof exports?module.exports=b(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"==typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],b):a.URI=b(a.punycode,a.IPv6,a.SecondLevelDomains,a)}(this,function(a,b,c,d){"use strict";function e(a,b){return this instanceof e?(void 0===a&&(a="undefined"!=typeof location?location.href+"":""),this.href(a),void 0!==b?this.absoluteTo(b):this):new e(a,b)}function f(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}function g(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function h(a){return"Array"===g(a)}function i(a,b){var c,d,e={};if(h(b))for(c=0,d=b.length;d>c;c++)e[b[c]]=!0;else e[b]=!0;for(c=0,d=a.length;d>c;c++)void 0!==e[a[c]]&&(a.splice(c,1),d--,c--);return a}function j(a,b){var c,d;if(h(b)){for(c=0,d=b.length;d>c;c++)if(!j(a,b[c]))return!1;return!0}var e=g(b);for(c=0,d=a.length;d>c;c++)if("RegExp"===e){if("string"==typeof a[c]&&a[c].match(b))return!0}else if(a[c]===b)return!0;return!1}function k(a,b){if(!h(a)||!h(b))return!1;if(a.length!==b.length)return!1;a.sort(),b.sort();for(var c=0,d=a.length;d>c;c++)if(a[c]!==b[c])return!1;return!0}function l(a){return escape(a)}function m(a){return encodeURIComponent(a).replace(/[!'()*]/g,l).replace(/\*/g,"%2A")}var n=d&&d.URI;e.version="1.13.2";var o=e.prototype,p=Object.prototype.hasOwnProperty;e._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:e.duplicateQueryParameters,escapeQuerySpace:e.escapeQuerySpace}},e.duplicateQueryParameters=!1,e.escapeQuerySpace=!0,e.protocol_expression=/^[a-z][a-z0-9.+-]*$/i,e.idn_expression=/[^a-z0-9\.-]/i,e.punycode_expression=/(xn--)/i,e.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,e.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,e.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi,e.findUri={start:/\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,end:/[\s\r\n]|$/,trim:/[`!()\[\]{};:'".,<>?«»“”„‘’]+$/},e.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"},e.invalid_hostname_characters=/[^a-zA-Z0-9\.-]/,e.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",iframe:"src",embed:"src",source:"src",track:"src",input:"src"},e.getDomAttribute=function(a){if(!a||!a.nodeName)return void 0;var b=a.nodeName.toLowerCase();return"input"===b&&"image"!==a.type?void 0:e.domAttributes[b]},e.encode=m,e.decode=decodeURIComponent,e.iso8859=function(){e.encode=escape,e.decode=unescape},e.unicode=function(){e.encode=m,e.decode=decodeURIComponent},e.characters={pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/gi,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/gi,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%3B":";","%3D":"="}}}},e.encodeQuery=function(a,b){var c=e.encode(a+"");return void 0===b&&(b=e.escapeQuerySpace),b?c.replace(/%20/g,"+"):c},e.decodeQuery=function(a,b){a+="",void 0===b&&(b=e.escapeQuerySpace);try{return e.decode(b?a.replace(/\+/g,"%20"):a)}catch(c){return a}},e.recodePath=function(a){for(var b=(a+"").split("/"),c=0,d=b.length;d>c;c++)b[c]=e.encodePathSegment(e.decode(b[c]));return b.join("/")},e.decodePath=function(a){for(var b=(a+"").split("/"),c=0,d=b.length;d>c;c++)b[c]=e.decodePathSegment(b[c]);return b.join("/")};var q,r={encode:"encode",decode:"decode"},s=function(a,b){return function(c){return e[b](c+"").replace(e.characters[a][b].expression,function(c){return e.characters[a][b].map[c]})}};for(q in r)e[q+"PathSegment"]=s("pathname",r[q]);e.encodeReserved=s("reserved","encode"),e.parse=function(a,b){var c;return b||(b={}),c=a.indexOf("#"),c>-1&&(b.fragment=a.substring(c+1)||null,a=a.substring(0,c)),c=a.indexOf("?"),c>-1&&(b.query=a.substring(c+1)||null,a=a.substring(0,c)),"//"===a.substring(0,2)?(b.protocol=null,a=a.substring(2),a=e.parseAuthority(a,b)):(c=a.indexOf(":"),c>-1&&(b.protocol=a.substring(0,c)||null,b.protocol&&!b.protocol.match(e.protocol_expression)?b.protocol=void 0:"file"===b.protocol?a=a.substring(c+3):"//"===a.substring(c+1,c+3)?(a=a.substring(c+3),a=e.parseAuthority(a,b)):(a=a.substring(c+1),b.urn=!0))),b.path=a,b},e.parseHost=function(a,b){var c,d,e=a.indexOf("/");return-1===e&&(e=a.length),"["===a.charAt(0)?(c=a.indexOf("]"),b.hostname=a.substring(1,c)||null,b.port=a.substring(c+2,e)||null,"/"===b.port&&(b.port=null)):a.indexOf(":")!==a.lastIndexOf(":")?(b.hostname=a.substring(0,e)||null,b.port=null):(d=a.substring(0,e).split(":"),b.hostname=d[0]||null,b.port=d[1]||null),b.hostname&&"/"!==a.substring(e).charAt(0)&&(e++,a="/"+a),a.substring(e)||"/"},e.parseAuthority=function(a,b){return a=e.parseUserinfo(a,b),e.parseHost(a,b)},e.parseUserinfo=function(a,b){var c,d=a.indexOf("/"),f=d>-1?a.lastIndexOf("@",d):a.indexOf("@");return f>-1&&(-1===d||d>f)?(c=a.substring(0,f).split(":"),b.username=c[0]?e.decode(c[0]):null,c.shift(),b.password=c[0]?e.decode(c.join(":")):null,a=a.substring(f+1)):(b.username=null,b.password=null),a},e.parseQuery=function(a,b){if(!a)return{};if(a=a.replace(/&+/g,"&").replace(/^\?*&*|&+$/g,""),!a)return{};for(var c,d,f,g={},h=a.split("&"),i=h.length,j=0;i>j;j++)c=h[j].split("="),d=e.decodeQuery(c.shift(),b),f=c.length?e.decodeQuery(c.join("="),b):null,g[d]?("string"==typeof g[d]&&(g[d]=[g[d]]),g[d].push(f)):g[d]=f;return g},e.build=function(a){var b="";return a.protocol&&(b+=a.protocol+":"),a.urn||!b&&!a.hostname||(b+="//"),b+=e.buildAuthority(a)||"","string"==typeof a.path&&("/"!==a.path.charAt(0)&&"string"==typeof a.hostname&&(b+="/"),b+=a.path),"string"==typeof a.query&&a.query&&(b+="?"+a.query),"string"==typeof a.fragment&&a.fragment&&(b+="#"+a.fragment),b},e.buildHost=function(a){var b="";return a.hostname?(b+=e.ip6_expression.test(a.hostname)?"["+a.hostname+"]":a.hostname,a.port&&(b+=":"+a.port),b):""},e.buildAuthority=function(a){return e.buildUserinfo(a)+e.buildHost(a)},e.buildUserinfo=function(a){var b="";return a.username&&(b+=e.encode(a.username),a.password&&(b+=":"+e.encode(a.password)),b+="@"),b},e.buildQuery=function(a,b,c){var d,f,g,i,j="";for(f in a)if(p.call(a,f)&&f)if(h(a[f]))for(d={},g=0,i=a[f].length;i>g;g++)void 0!==a[f][g]&&void 0===d[a[f][g]+""]&&(j+="&"+e.buildQueryParameter(f,a[f][g],c),b!==!0&&(d[a[f][g]+""]=!0));else void 0!==a[f]&&(j+="&"+e.buildQueryParameter(f,a[f],c));return j.substring(1)},e.buildQueryParameter=function(a,b,c){return e.encodeQuery(a,c)+(null!==b?"="+e.encodeQuery(b,c):"")},e.addQuery=function(a,b,c){if("object"==typeof b)for(var d in b)p.call(b,d)&&e.addQuery(a,d,b[d]);else{if("string"!=typeof b)throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");if(void 0===a[b])return void(a[b]=c);"string"==typeof a[b]&&(a[b]=[a[b]]),h(c)||(c=[c]),a[b]=a[b].concat(c)}},e.removeQuery=function(a,b,c){var d,f,g;if(h(b))for(d=0,f=b.length;f>d;d++)a[b[d]]=void 0;else if("object"==typeof b)for(g in b)p.call(b,g)&&e.removeQuery(a,g,b[g]);else{if("string"!=typeof b)throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");void 0!==c?a[b]===c?a[b]=void 0:h(a[b])&&(a[b]=i(a[b],c)):a[b]=void 0}},e.hasQuery=function(a,b,c,d){if("object"==typeof b){for(var f in b)if(p.call(b,f)&&!e.hasQuery(a,f,b[f]))return!1;return!0}if("string"!=typeof b)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");switch(g(c)){case"Undefined":return b in a;case"Boolean":var i=Boolean(h(a[b])?a[b].length:a[b]);return c===i;case"Function":return!!c(a[b],b,a);case"Array":if(!h(a[b]))return!1;var l=d?j:k;return l(a[b],c);case"RegExp":return h(a[b])?d?j(a[b],c):!1:Boolean(a[b]&&a[b].match(c));case"Number":c=String(c);case"String":return h(a[b])?d?j(a[b],c):!1:a[b]===c;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter")}},e.commonPath=function(a,b){var c,d=Math.min(a.length,b.length);for(c=0;d>c;c++)if(a.charAt(c)!==b.charAt(c)){c--;break}return 1>c?a.charAt(0)===b.charAt(0)&&"/"===a.charAt(0)?"/":"":(("/"!==a.charAt(c)||"/"!==b.charAt(c))&&(c=a.substring(0,c).lastIndexOf("/")),a.substring(0,c+1))},e.withinString=function(a,b,c){c||(c={});var d=c.start||e.findUri.start,f=c.end||e.findUri.end,g=c.trim||e.findUri.trim,h=/[a-z0-9-]=["']?$/i;for(d.lastIndex=0;;){var i=d.exec(a);if(!i)break;var j=i.index;if(c.ignoreHtml){var k=a.slice(Math.max(j-3,0),j);if(k&&h.test(k))continue}var l=j+a.slice(j).search(f),m=a.slice(j,l).replace(g,"");if(!c.ignore||!c.ignore.test(m)){l=j+m.length;var n=b(m,j,l,a);a=a.slice(0,j)+n+a.slice(l),d.lastIndex=j+n.length}}return d.lastIndex=0,a},e.ensureValidHostname=function(b){if(b.match(e.invalid_hostname_characters)){if(!a)throw new TypeError('Hostname "'+b+'" contains characters other than [A-Z0-9.-] and Punycode.js is not available');if(a.toASCII(b).match(e.invalid_hostname_characters))throw new TypeError('Hostname "'+b+'" contains characters other than [A-Z0-9.-]')}},e.noConflict=function(a){if(a){var b={URI:this.noConflict()};return d.URITemplate&&"function"==typeof d.URITemplate.noConflict&&(b.URITemplate=d.URITemplate.noConflict()),d.IPv6&&"function"==typeof d.IPv6.noConflict&&(b.IPv6=d.IPv6.noConflict()),d.SecondLevelDomains&&"function"==typeof d.SecondLevelDomains.noConflict&&(b.SecondLevelDomains=d.SecondLevelDomains.noConflict()),b}return d.URI===this&&(d.URI=n),this},o.build=function(a){return a===!0?this._deferred_build=!0:(void 0===a||this._deferred_build)&&(this._string=e.build(this._parts),this._deferred_build=!1),this},o.clone=function(){return new e(this)},o.valueOf=o.toString=function(){return this.build(!1)._string},r={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"},s=function(a){return function(b,c){return void 0===b?this._parts[a]||"":(this._parts[a]=b||null,this.build(!c),this)}};for(q in r)o[q]=s(r[q]);r={query:"?",fragment:"#"},s=function(a,b){return function(c,d){return void 0===c?this._parts[a]||"":(null!==c&&(c+="",c.charAt(0)===b&&(c=c.substring(1))),this._parts[a]=c,this.build(!d),this)}};for(q in r)o[q]=s(q,r[q]);r={search:["?","query"],hash:["#","fragment"]},s=function(a,b){return function(c,d){var e=this[a](c,d);return"string"==typeof e&&e.length?b+e:e}};for(q in r)o[q]=s(r[q][1],r[q][0]);o.pathname=function(a,b){if(void 0===a||a===!0){var c=this._parts.path||(this._parts.hostname?"/":"");return a?e.decodePath(c):c}return this._parts.path=a?e.recodePath(a):"/",this.build(!b),this},o.path=o.pathname,o.href=function(a,b){var c;if(void 0===a)return this.toString();this._string="",this._parts=e._parts();var d=a instanceof e,f="object"==typeof a&&(a.hostname||a.path||a.pathname);if(a.nodeName){var g=e.getDomAttribute(a);a=a[g]||"",f=!1}if(!d&&f&&void 0!==a.pathname&&(a=a.toString()),"string"==typeof a)this._parts=e.parse(a,this._parts);else{if(!d&&!f)throw new TypeError("invalid input");var h=d?a._parts:a;for(c in h)p.call(this._parts,c)&&(this._parts[c]=h[c])}return this.build(!b),this},o.is=function(a){var b=!1,d=!1,f=!1,g=!1,h=!1,i=!1,j=!1,k=!this._parts.urn;switch(this._parts.hostname&&(k=!1,d=e.ip4_expression.test(this._parts.hostname),f=e.ip6_expression.test(this._parts.hostname),b=d||f,g=!b,h=g&&c&&c.has(this._parts.hostname),i=g&&e.idn_expression.test(this._parts.hostname),j=g&&e.punycode_expression.test(this._parts.hostname)),a.toLowerCase()){case"relative":return k;case"absolute":return!k;case"domain":case"name":return g;case"sld":return h;case"ip":return b;case"ip4":case"ipv4":case"inet4":return d;case"ip6":case"ipv6":case"inet6":return f;case"idn":return i;case"url":return!this._parts.urn;case"urn":return!!this._parts.urn;case"punycode":return j}return null};var t=o.protocol,u=o.port,v=o.hostname;o.protocol=function(a,b){if(void 0!==a&&a&&(a=a.replace(/:(\/\/)?$/,""),!a.match(e.protocol_expression)))throw new TypeError('Protocol "'+a+"\" contains characters other than [A-Z0-9.+-] or doesn't start with [A-Z]");return t.call(this,a,b)},o.scheme=o.protocol,o.port=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError('Port "'+a+'" contains characters other than [0-9]');return u.call(this,a,b)},o.hostname=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var c={};e.parseHost(a,c),a=c.hostname}return v.call(this,a,b)},o.host=function(a,b){return this._parts.urn?void 0===a?"":this:void 0===a?this._parts.hostname?e.buildHost(this._parts):"":(e.parseHost(a,this._parts),this.build(!b),this)},o.authority=function(a,b){return this._parts.urn?void 0===a?"":this:void 0===a?this._parts.hostname?e.buildAuthority(this._parts):"":(e.parseAuthority(a,this._parts),this.build(!b),this)},o.userinfo=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var c=e.buildUserinfo(this._parts);return c.substring(0,c.length-1)}return"@"!==a[a.length-1]&&(a+="@"),e.parseUserinfo(a,this._parts),this.build(!b),this},o.resource=function(a,b){var c;return void 0===a?this.path()+this.search()+this.hash():(c=e.parse(a),this._parts.path=c.path,this._parts.query=c.query,this._parts.fragment=c.fragment,this.build(!b),this)},o.subdomain=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.length-this.domain().length-1;return this._parts.hostname.substring(0,c)||""}var d=this._parts.hostname.length-this.domain().length,g=this._parts.hostname.substring(0,d),h=new RegExp("^"+f(g));return a&&"."!==a.charAt(a.length-1)&&(a+="."),a&&e.ensureValidHostname(a),this._parts.hostname=this._parts.hostname.replace(h,a),this.build(!b),this},o.domain=function(a,b){if(this._parts.urn)return void 0===a?"":this;if("boolean"==typeof a&&(b=a,a=void 0),void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.match(/\./g);if(c&&c.length<2)return this._parts.hostname;var d=this._parts.hostname.length-this.tld(b).length-1;return d=this._parts.hostname.lastIndexOf(".",d-1)+1,this._parts.hostname.substring(d)||""}if(!a)throw new TypeError("cannot set domain empty");if(e.ensureValidHostname(a),!this._parts.hostname||this.is("IP"))this._parts.hostname=a;else{var g=new RegExp(f(this.domain())+"$");this._parts.hostname=this._parts.hostname.replace(g,a)}return this.build(!b),this},o.tld=function(a,b){if(this._parts.urn)return void 0===a?"":this;if("boolean"==typeof a&&(b=a,a=void 0),void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.lastIndexOf("."),e=this._parts.hostname.substring(d+1);return b!==!0&&c&&c.list[e.toLowerCase()]?c.get(this._parts.hostname)||e:e}var g;if(!a)throw new TypeError("cannot set TLD empty");if(a.match(/[^a-zA-Z0-9-]/)){if(!c||!c.is(a))throw new TypeError('TLD "'+a+'" contains characters other than [A-Z0-9]');g=new RegExp(f(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(g,a)}else{if(!this._parts.hostname||this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");g=new RegExp(f(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(g,a)}return this.build(!b),this},o.directory=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||a===!0){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var c=this._parts.path.length-this.filename().length-1,d=this._parts.path.substring(0,c)||(this._parts.hostname?"/":"");return a?e.decodePath(d):d}var g=this._parts.path.length-this.filename().length,h=this._parts.path.substring(0,g),i=new RegExp("^"+f(h));return this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a)),a&&"/"!==a.charAt(a.length-1)&&(a+="/"),a=e.recodePath(a),this._parts.path=this._parts.path.replace(i,a),this.build(!b),this},o.filename=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||a===!0){if(!this._parts.path||"/"===this._parts.path)return"";var c=this._parts.path.lastIndexOf("/"),d=this._parts.path.substring(c+1);return a?e.decodePathSegment(d):d}var g=!1;"/"===a.charAt(0)&&(a=a.substring(1)),a.match(/\.?\//)&&(g=!0);var h=new RegExp(f(this.filename())+"$");return a=e.recodePath(a),this._parts.path=this._parts.path.replace(h,a),g?this.normalizePath(b):this.build(!b),this},o.suffix=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||a===!0){if(!this._parts.path||"/"===this._parts.path)return"";var c,d,g=this.filename(),h=g.lastIndexOf(".");return-1===h?"":(c=g.substring(h+1),d=/^[a-z0-9%]+$/i.test(c)?c:"",a?e.decodePathSegment(d):d)}"."===a.charAt(0)&&(a=a.substring(1));var i,j=this.suffix();if(j)i=new RegExp(a?f(j)+"$":f("."+j)+"$");else{if(!a)return this;this._parts.path+="."+e.recodePath(a)}return i&&(a=e.recodePath(a),this._parts.path=this._parts.path.replace(i,a)),this.build(!b),this},o.segment=function(a,b,c){var d=this._parts.urn?":":"/",e=this.path(),f="/"===e.substring(0,1),g=e.split(d);if(void 0!==a&&"number"!=typeof a&&(c=b,b=a,a=void 0),void 0!==a&&"number"!=typeof a)throw new Error('Bad segment "'+a+'", must be 0-based integer');if(f&&g.shift(),0>a&&(a=Math.max(g.length+a,0)),void 0===b)return void 0===a?g:g[a];if(null===a||void 0===g[a])if(h(b)){g=[];for(var i=0,j=b.length;j>i;i++)(b[i].length||g.length&&g[g.length-1].length)&&(g.length&&!g[g.length-1].length&&g.pop(),g.push(b[i]))}else(b||"string"==typeof b)&&(""===g[g.length-1]?g[g.length-1]=b:g.push(b));else b||"string"==typeof b&&b.length?g[a]=b:g.splice(a,1);return f&&g.unshift(""),this.path(g.join(d),c)},o.segmentCoded=function(a,b,c){var d,f,g;if("number"!=typeof a&&(c=b,b=a,a=void 0),void 0===b){if(d=this.segment(a,b,c),h(d))for(f=0,g=d.length;g>f;f++)d[f]=e.decode(d[f]);else d=void 0!==d?e.decode(d):void 0;return d}if(h(b))for(f=0,g=b.length;g>f;f++)b[f]=e.decode(b[f]);else b="string"==typeof b?e.encode(b):b;return this.segment(a,b,c)};var w=o.query;return o.query=function(a,b){if(a===!0)return e.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("function"==typeof a){var c=e.parseQuery(this._parts.query,this._parts.escapeQuerySpace),d=a.call(this,c);return this._parts.query=e.buildQuery(d||c,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!b),this}return void 0!==a&&"string"!=typeof a?(this._parts.query=e.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!b),this):w.call(this,a,b)},o.setQuery=function(a,b,c){var d=e.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("object"==typeof a)for(var f in a)p.call(a,f)&&(d[f]=a[f]);else{if("string"!=typeof a)throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");d[a]=void 0!==b?b:null}return this._parts.query=e.buildQuery(d,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),"string"!=typeof a&&(c=b),this.build(!c),this},o.addQuery=function(a,b,c){var d=e.parseQuery(this._parts.query,this._parts.escapeQuerySpace);return e.addQuery(d,a,void 0===b?null:b),this._parts.query=e.buildQuery(d,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),"string"!=typeof a&&(c=b),this.build(!c),this},o.removeQuery=function(a,b,c){var d=e.parseQuery(this._parts.query,this._parts.escapeQuerySpace);return e.removeQuery(d,a,b),this._parts.query=e.buildQuery(d,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),"string"!=typeof a&&(c=b),this.build(!c),this},o.hasQuery=function(a,b,c){var d=e.parseQuery(this._parts.query,this._parts.escapeQuerySpace);return e.hasQuery(d,a,b,c)},o.setSearch=o.setQuery,o.addSearch=o.addQuery,o.removeSearch=o.removeQuery,o.hasSearch=o.hasQuery,o.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()},o.normalizeProtocol=function(a){return"string"==typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),this.build(!a)),this},o.normalizeHostname=function(c){return this._parts.hostname&&(this.is("IDN")&&a?this._parts.hostname=a.toASCII(this._parts.hostname):this.is("IPv6")&&b&&(this._parts.hostname=b.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!c)),this},o.normalizePort=function(a){return"string"==typeof this._parts.protocol&&this._parts.port===e.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a)),this},o.normalizePath=function(a){if(this._parts.urn)return this;if(!this._parts.path||"/"===this._parts.path)return this;var b,c,d,f=this._parts.path,g="";for("/"!==f.charAt(0)&&(b=!0,f="/"+f),f=f.replace(/(\/(\.\/)+)|(\/\.$)/g,"/").replace(/\/{2,}/g,"/"),b&&(g=f.substring(1).match(/^(\.\.\/)+/)||"",g&&(g=g[0]));;){if(c=f.indexOf("/.."),-1===c)break;0!==c?(d=f.substring(0,c).lastIndexOf("/"),-1===d&&(d=c),f=f.substring(0,d)+f.substring(c+3)):f=f.substring(3)}return b&&this.is("relative")&&(f=g+f.substring(1)),f=e.recodePath(f),this._parts.path=f,this.build(!a),this},o.normalizePathname=o.normalizePath,o.normalizeQuery=function(a){return"string"==typeof this._parts.query&&(this._parts.query.length?this.query(e.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a)),this},o.normalizeFragment=function(a){return this._parts.fragment||(this._parts.fragment=null,this.build(!a)),this},o.normalizeSearch=o.normalizeQuery,o.normalizeHash=o.normalizeFragment,o.iso8859=function(){var a=e.encode,b=e.decode;return e.encode=escape,e.decode=decodeURIComponent,this.normalize(),e.encode=a,e.decode=b,this},o.unicode=function(){var a=e.encode,b=e.decode;return e.encode=m,e.decode=unescape,this.normalize(),e.encode=a,e.decode=b,this},o.readable=function(){var b=this.clone();b.username("").password("").normalize();var c="";if(b._parts.protocol&&(c+=b._parts.protocol+"://"),b._parts.hostname&&(b.is("punycode")&&a?(c+=a.toUnicode(b._parts.hostname),b._parts.port&&(c+=":"+b._parts.port)):c+=b.host()),b._parts.hostname&&b._parts.path&&"/"!==b._parts.path.charAt(0)&&(c+="/"),c+=b.path(!0),b._parts.query){for(var d="",f=0,g=b._parts.query.split("&"),h=g.length;h>f;f++){var i=(g[f]||"").split("=");d+="&"+e.decodeQuery(i[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"),void 0!==i[1]&&(d+="="+e.decodeQuery(i[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}c+="?"+d.substring(1)}return c+=e.decodeQuery(b.hash(),!0)},o.absoluteTo=function(a){var b,c,d,f=this.clone(),g=["protocol","username","password","hostname","port"];if(this._parts.urn)throw new Error("URNs do not have any generally defined hierarchical components");if(a instanceof e||(a=new e(a)),f._parts.protocol||(f._parts.protocol=a._parts.protocol),this._parts.hostname)return f;for(c=0;d=g[c];c++)f._parts[d]=a._parts[d];return f._parts.path?".."===f._parts.path.substring(-2)&&(f._parts.path+="/"):(f._parts.path=a._parts.path,f._parts.query||(f._parts.query=a._parts.query)),"/"!==f.path().charAt(0)&&(b=a.directory(),f._parts.path=(b?b+"/":"")+f._parts.path,f.normalizePath()),f.build(),f},o.relativeTo=function(a){var b,c,d,f,g,h=this.clone().normalize();if(h._parts.urn)throw new Error("URNs do not have any generally defined hierarchical components");if(a=new e(a).normalize(),b=h._parts,c=a._parts,f=h.path(),g=a.path(),"/"!==f.charAt(0))throw new Error("URI is already relative");if("/"!==g.charAt(0))throw new Error("Cannot calculate a URI relative to another relative URI");if(b.protocol===c.protocol&&(b.protocol=null),b.username!==c.username||b.password!==c.password)return h.build();if(null!==b.protocol||null!==b.username||null!==b.password)return h.build();if(b.hostname!==c.hostname||b.port!==c.port)return h.build();if(b.hostname=null,b.port=null,f===g)return b.path="",h.build();if(d=e.commonPath(h.path(),a.path()),!d)return h.build();var i=c.path.substring(d.length).replace(/[^\/]*$/,"").replace(/.*?\//g,"../");return b.path=i+b.path.substring(d.length),h.build()},o.equals=function(a){var b,c,d,f=this.clone(),g=new e(a),i={},j={},l={};if(f.normalize(),g.normalize(),f.toString()===g.toString())return!0;if(b=f.query(),c=g.query(),f.query(""),g.query(""),f.toString()!==g.toString())return!1;if(b.length!==c.length)return!1;i=e.parseQuery(b,this._parts.escapeQuerySpace),j=e.parseQuery(c,this._parts.escapeQuerySpace);for(d in i)if(p.call(i,d)){if(h(i[d])){if(!k(i[d],j[d]))return!1}else if(i[d]!==j[d])return!1;l[d]=!0}for(d in j)if(p.call(j,d)&&!l[d])return!1;return!0},o.duplicateQueryParameters=function(a){return this._parts.duplicateQueryParameters=!!a,this},o.escapeQuerySpace=function(a){return this._parts.escapeQuerySpace=!!a,this},e}),function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b=a.length,c=_.type(a);return"function"===c||_.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}function d(a,b,c){if(_.isFunction(b))return _.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return _.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(hb.test(b))return _.filter(b,a,c);b=_.filter(b,a)}return _.grep(a,function(a){return U.call(b,a)>=0!==c})}function e(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function f(a){var b=ob[a]={};return _.each(a.match(nb)||[],function(a,c){b[c]=!0}),b}function g(){Z.removeEventListener("DOMContentLoaded",g,!1),a.removeEventListener("load",g,!1),_.ready()}function h(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=_.expando+Math.random()}function i(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(ub,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:tb.test(c)?_.parseJSON(c):c}catch(e){}sb.set(a,b,c)}else c=void 0;return c}function j(){return!0}function k(){return!1}function l(){try{return Z.activeElement}catch(a){}}function m(a,b){return _.nodeName(a,"table")&&_.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function n(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function o(a){var b=Kb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function p(a,b){for(var c=0,d=a.length;d>c;c++)rb.set(a[c],"globalEval",!b||rb.get(b[c],"globalEval"))}function q(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(rb.hasData(a)&&(f=rb.access(a),g=rb.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)_.event.add(b,e,j[e][c])}sb.hasData(a)&&(h=sb.access(a),i=_.extend({},h),sb.set(b,i))}}function r(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&_.nodeName(a,b)?_.merge([a],c):c}function s(a,b){var c=b.nodeName.toLowerCase();"input"===c&&yb.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function t(b,c){var d,e=_(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:_.css(e[0],"display");return e.detach(),f}function u(a){var b=Z,c=Ob[a];return c||(c=t(a,b),"none"!==c&&c||(Nb=(Nb||_("