Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2 from isaacs/master

Ported to JS
  • Loading branch information...
commit 4ed759d640870258ce1616bfdf05c725231e7c98 2 parents b256a3e + 2fc3d2f
@federomero federomero authored
View
31 .gitignore
@@ -1,31 +0,0 @@
-## OSX
-.DS_Store
-
-# Thumbnails
-._*
-
-# Files that might appear on external disk
-.Spotlight-V100
-.Trashes
-
-## vim
-.*.sw[a-z]
-*.un~
-Session.vim
-
-## Node
-lib-cov
-*.seed
-*.log
-*.csv
-*.dat
-*.out
-*.pid
-*.gz
-
-pids
-logs
-results
-
-node_modules
-npm-debug.log
View
27 LICENSE
@@ -0,0 +1,27 @@
+Original "Negotiator" program Copyright Federico Romero
+Port to JavaScript Copyright Isaac Z. Schlueter
+
+All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
View
27 examples/accept.coffee
@@ -1,27 +0,0 @@
-Negotiator = require('../lib/negotiator').Negotiator
-http = require('http')
-
-representations =
- 'text/html': '<h1>Hello world!</h1>'
- 'text/plain': 'Hello World!'
- 'application/json': JSON.stringify({hello: 'world!'})
-
-availableMediaTypes = (key for key, val of representations)
-
-server = http.createServer (req, res) ->
- negotiator = new Negotiator(req)
-
- console.log "Accept: #{req.headers['accept']}"
- console.log "Preferred: #{negotiator.preferredMediaTypes()}"
- console.log "Possible: #{negotiator.preferredMediaTypes(availableMediaTypes)}"
- mediaType = negotiator.preferredMediaType(availableMediaTypes)
- console.log "Selected: #{mediaType}"
-
- if mediaType
- res.writeHead(200, {'Content-Type': mediaType})
- res.end(representations[mediaType])
- else
- res.writeHead(406)
- res.end()
-
-server.listen(8080)
View
47 examples/accept.js
@@ -0,0 +1,47 @@
+(function() {
+ var Negotiator, availableMediaTypes, http, key, representations, server, val;
+
+ Negotiator = require('../lib/negotiator').Negotiator;
+
+ http = require('http');
+
+ representations = {
+ 'text/html': '<h1>Hello world!</h1>',
+ 'text/plain': 'Hello World!',
+ 'application/json': JSON.stringify({
+ hello: 'world!'
+ })
+ };
+
+ availableMediaTypes = (function() {
+ var _results;
+ _results = [];
+ for (key in representations) {
+ val = representations[key];
+ _results.push(key);
+ }
+ return _results;
+ })();
+
+ server = http.createServer(function(req, res) {
+ var mediaType, negotiator;
+ negotiator = new Negotiator(req);
+ console.log("Accept: " + req.headers['accept']);
+ console.log("Preferred: " + (negotiator.preferredMediaTypes()));
+ console.log("Possible: " + (negotiator.preferredMediaTypes(availableMediaTypes)));
+ mediaType = negotiator.preferredMediaType(availableMediaTypes);
+ console.log("Selected: " + mediaType);
+ if (mediaType) {
+ res.writeHead(200, {
+ 'Content-Type': mediaType
+ });
+ return res.end(representations[mediaType]);
+ } else {
+ res.writeHead(406);
+ return res.end();
+ }
+ });
+
+ server.listen(8080);
+
+}).call(this);
View
31 examples/charset.coffee
@@ -1,31 +0,0 @@
-Negotiator = require('../lib/negotiator').Negotiator
-http = require('http')
-
-Buffer = require('buffer').Buffer
-Iconv = require('iconv').Iconv
-
-iconv = new Iconv('UTF-8', 'ISO-8859-1')
-message = "ë"
-messages =
- 'utf-8': message
- 'iso-8859-1': iconv.convert(new Buffer(message))
-
-availableCharsets = (key for key, val of messages)
-
-server = http.createServer (req, res) ->
- negotiator = new Negotiator(req )
-
- console.log "Accept-Charset: #{req.headers['accept-charset']}"
- console.log "Preferred: #{negotiator.preferredCharsets()}"
- console.log "Possible: #{negotiator.preferredCharsets(availableCharsets)}"
- charset = negotiator.preferredCharset(availableCharsets)
- console.log "Selected: #{charset}"
-
- if charset
- res.writeHead(200, {'Content-Type': "text/html; charset=#{charset}"})
- res.end(messages[charset])
- else
- res.writeHead(406)
- res.end()
-
-server.listen(8080)
View
52 examples/charset.js
@@ -0,0 +1,52 @@
+(function() {
+ var Buffer, Iconv, Negotiator, availableCharsets, http, iconv, key, message, messages, server, val;
+
+ Negotiator = require('../lib/negotiator').Negotiator;
+
+ http = require('http');
+
+ Buffer = require('buffer').Buffer;
+
+ Iconv = require('iconv').Iconv;
+
+ iconv = new Iconv('UTF-8', 'ISO-8859-1');
+
+ message = "ë";
+
+ messages = {
+ 'utf-8': message,
+ 'iso-8859-1': iconv.convert(new Buffer(message))
+ };
+
+ availableCharsets = (function() {
+ var _results;
+ _results = [];
+ for (key in messages) {
+ val = messages[key];
+ _results.push(key);
+ }
+ return _results;
+ })();
+
+ server = http.createServer(function(req, res) {
+ var charset, negotiator;
+ negotiator = new Negotiator(req);
+ console.log("Accept-Charset: " + req.headers['accept-charset']);
+ console.log("Preferred: " + (negotiator.preferredCharsets()));
+ console.log("Possible: " + (negotiator.preferredCharsets(availableCharsets)));
+ charset = negotiator.preferredCharset(availableCharsets);
+ console.log("Selected: " + charset);
+ if (charset) {
+ res.writeHead(200, {
+ 'Content-Type': "text/html; charset=" + charset
+ });
+ return res.end(messages[charset]);
+ } else {
+ res.writeHead(406);
+ return res.end();
+ }
+ });
+
+ server.listen(8080);
+
+}).call(this);
View
31 examples/encoding.coffee
@@ -1,31 +0,0 @@
-Negotiator = require('../lib/negotiator').Negotiator
-http = require('http')
-
-gbuf = require('gzip-buffer')
-
-messages =
- identity: 'Hello World'
-
-gbuf.gzip messages.identity, (zipped) ->
- messages.gzip = zipped
-
- availableEncodings = (key for key, val of messages)
- console.log availableEncodings
-
- server = http.createServer (req, res) ->
- negotiator = new Negotiator(req)
-
- console.log "Accept-Encoding: #{req.headers['accept-encoding']}"
- console.log "Preferred: #{negotiator.preferredEncodings()}"
- console.log "Possible: #{negotiator.preferredEncodings(availableEncodings)}"
- encoding = negotiator.preferredEncoding(availableEncodings)
- console.log "Selected: #{encoding}"
-
- if encoding
- res.writeHead(200, {'Content-Encoding': encoding})
- res.end(messages[encoding])
- else
- res.writeHead(406)
- res.end()
-
- server.listen(8080)
View
48 examples/encoding.js
@@ -0,0 +1,48 @@
+(function() {
+ var Negotiator, gbuf, http, messages;
+
+ Negotiator = require('../lib/negotiator').Negotiator;
+
+ http = require('http');
+
+ gbuf = require('gzip-buffer');
+
+ messages = {
+ identity: 'Hello World'
+ };
+
+ gbuf.gzip(messages.identity, function(zipped) {
+ var availableEncodings, key, server, val;
+ messages.gzip = zipped;
+ availableEncodings = (function() {
+ var _results;
+ _results = [];
+ for (key in messages) {
+ val = messages[key];
+ _results.push(key);
+ }
+ return _results;
+ })();
+ console.log(availableEncodings);
+ server = http.createServer(function(req, res) {
+ var encoding, negotiator;
+ negotiator = new Negotiator(req);
+ console.log("Accept-Encoding: " + req.headers['accept-encoding']);
+ console.log("Preferred: " + (negotiator.preferredEncodings()));
+ console.log("Possible: " + (negotiator.preferredEncodings(availableEncodings)));
+ encoding = negotiator.preferredEncoding(availableEncodings);
+ console.log("Selected: " + encoding);
+ if (encoding) {
+ res.writeHead(200, {
+ 'Content-Encoding': encoding
+ });
+ return res.end(messages[encoding]);
+ } else {
+ res.writeHead(406);
+ return res.end();
+ }
+ });
+ return server.listen(8080);
+ });
+
+}).call(this);
View
26 examples/language.coffee
@@ -1,26 +0,0 @@
-Negotiator = require('../lib/negotiator').Negotiator
-http = require('http')
-
-messages =
- es: "¡Hola Mundo!",
- en: "Hello World!"
-
-availableLanguages = (key for key, val of messages)
-
-server = http.createServer (req, res) ->
- negotiator = new Negotiator(req )
-
- console.log "Accept-Language: #{req.headers['accept-language']}"
- console.log "Preferred: #{negotiator.preferredLanguages()}"
- console.log "Possible: #{negotiator.preferredLanguages(availableLanguages)}"
- language = negotiator.preferredLanguage(availableLanguages)
- console.log "Selected: #{language}"
-
- if language
- res.writeHead(200, {'Content-Language': language})
- res.end(messages[language])
- else
- res.writeHead(406)
- res.end()
-
-server.listen(8080)
View
44 examples/language.js
@@ -0,0 +1,44 @@
+(function() {
+ var Negotiator, availableLanguages, http, key, messages, server, val;
+
+ Negotiator = require('../lib/negotiator').Negotiator;
+
+ http = require('http');
+
+ messages = {
+ es: "¡Hola Mundo!",
+ en: "Hello World!"
+ };
+
+ availableLanguages = (function() {
+ var _results;
+ _results = [];
+ for (key in messages) {
+ val = messages[key];
+ _results.push(key);
+ }
+ return _results;
+ })();
+
+ server = http.createServer(function(req, res) {
+ var language, negotiator;
+ negotiator = new Negotiator(req);
+ console.log("Accept-Language: " + req.headers['accept-language']);
+ console.log("Preferred: " + (negotiator.preferredLanguages()));
+ console.log("Possible: " + (negotiator.preferredLanguages(availableLanguages)));
+ language = negotiator.preferredLanguage(availableLanguages);
+ console.log("Selected: " + language);
+ if (language) {
+ res.writeHead(200, {
+ 'Content-Language': language
+ });
+ return res.end(messages[language]);
+ } else {
+ res.writeHead(406);
+ return res.end();
+ }
+ });
+
+ server.listen(8080);
+
+}).call(this);
View
43 lib/charset.coffee
@@ -1,43 +0,0 @@
-_ = require('underscore')
-
-parseAcceptCharset = (accept) ->
- acceptableCharsets = accept.split(',').map (e) -> parseCharset(e.trim())
- _.select(acceptableCharsets, (e) -> e && e.q > 0)
-
-parseCharset = (s) ->
- match = /^\s*(\S+?)\s*(?:;(.*))?$/.exec(s)
- if !match
- null
- else
- charset = match[1]
- params = {}
- q = 1
- if match[2]
- for p in match[2].split(';').map((s) -> s.trim().split('='))
- params[p[0]] = p[1]
- if params.q?
- q = parseFloat(params.q)
- delete params.q
- {charset: charset, q: q}
-
-getCharsetPriority = (charset, accepted) ->
- specs = accepted.map (a) -> specify(charset, a)
- specs = _.select(specs, (x) -> x)
- _.max(specs, (spec) -> spec.q)?.q || 0
-
-specify = (charset, spec) ->
- spec if spec.charset == '*' || spec.charset == charset
-
-preferredCharsets = (accept, provided) ->
- accept = parseAcceptCharset(accept || '')
- if provided
- _.chain(provided)
- .map((type) -> [type, getCharsetPriority(type, accept)])
- .select((pair) -> pair[1] > 0)
- .sortBy((pair) -> pair[1] * -1)
- .map((pair) -> pair[0])
- .value()
- else
- accept.map((type) -> type.charset)
-
-exports.preferredCharsets = preferredCharsets
View
68 lib/charset.js
@@ -0,0 +1,68 @@
+module.exports = preferredCharsets;
+preferredCharsets.preferredCharsets = preferredCharsets;
+
+function parseAcceptCharset(accept) {
+ return accept.split(',').map(function(e) {
+ return parseCharset(e.trim());
+ }).filter(function(e) {
+ return e && e.q > 0;
+ });
+}
+
+function parseCharset(s) {
+ var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/);
+ if (!match) return null;
+
+ var charset = match[1];
+ var q = 1;
+ if (match[2]) {
+ var params = match[2].split(';')
+ for (var i = 0; i < params.length; i ++) {
+ var p = params[i].trim().split('=');
+ if (p[0] === 'q') {
+ q = parseFloat(p[1]);
+ break;
+ }
+ }
+ }
+
+ return {
+ charset: charset,
+ q: q
+ };
+}
+
+function getCharsetPriority(charset, accepted) {
+ return (accepted.filter(function(a) {
+ return specify(charset, a);
+ }).sort(function (a, b) {
+ // revsort
+ return a.q === b.q ? 0 : a.q > b.q ? -1 : 1;
+ })[0] || {q:0}).q;
+}
+
+function specify(charset, spec) {
+ if (spec.charset === '*' || spec.charset === charset) {
+ return spec;
+ }
+};
+
+function preferredCharsets(accept, provided) {
+ accept = parseAcceptCharset(accept || '');
+ if (provided) {
+ return provided.map(function(type) {
+ return [type, getCharsetPriority(type, accept)];
+ }).filter(function(pair) {
+ return pair[1] > 0;
+ }).sort(function(a, b) {
+ // revsort
+ return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1;
+ }).map(function(pair) {
+ return pair[0];
+ });
+ } else {
+ return accept.map(function(type) {
+ return type.charset;
+ });
+ }
+}
View
47 lib/encoding.coffee
@@ -1,47 +0,0 @@
-_ = require('underscore')
-
-parseAcceptEncoding = (accept) ->
- acceptableEncodings = if accept
- accept.split(',').map (e) -> parseEncoding(e.trim())
- else
- []
- acceptableEncodings.push({encoding: 'identity', q: 0.1}) unless _.find(acceptableEncodings, (e) -> e.encoding == 'identity')
- _.select(acceptableEncodings, (e) -> e && e.q > 0)
-
-parseEncoding = (s) ->
- match = /^\s*(\S+?)\s*(?:;(.*))?$/.exec(s)
- if !match
- null
- else
- encoding = match[1]
- params = {}
- q = 1
- if match[2]
- for p in match[2].split(';').map((s) -> s.trim().split('='))
- params[p[0]] = p[1]
- if params.q?
- q = parseFloat(params.q)
- delete params.q
- {encoding: encoding, q: q}
-
-getEncodingPriority = (encoding, accepted) ->
- specs = accepted.map (a) -> specify(encoding, a)
- specs = _.select(specs, (x) -> x)
- _.max(specs, (spec) -> spec.q)?.q || 0
-
-specify = (encoding, spec) ->
- spec if spec.encoding == '*' || spec.encoding == encoding
-
-preferredEncodings = (accept, provided) ->
- accept = parseAcceptEncoding(accept || '')
- if provided
- _.chain(provided)
- .map((type) -> [type, getEncodingPriority(type, accept)])
- .select((pair) -> pair[1] > 0)
- .sortBy((pair) -> pair[1] * -1)
- .map((pair) -> pair[0])
- .value()
- else
- accept.map((type) -> type.encoding)
-
-exports.preferredEncodings = preferredEncodings
View
86 lib/encoding.js
@@ -0,0 +1,86 @@
+module.exports = preferredEncodings;
+preferredEncodings.preferredEncodings = preferredEncodings;
+
+function parseAcceptEncoding(accept) {
+ var acceptableEncodings;
+
+ if (accept) {
+ acceptableEncodings = accept.split(',').map(function(e) {
+ return parseEncoding(e.trim());
+ });
+ } else {
+ acceptableEncodings = [];
+ }
+
+ if (!acceptableEncodings.some(function(e) {
+ return e.encoding === 'identity';
+ })) {
+ acceptableEncodings.push({
+ encoding: 'identity',
+ q: 0.1
+ });
+ }
+
+ return acceptableEncodings.filter(function(e) {
+ return e && e.q > 0;
+ });
+}
+
+function parseEncoding(s) {
+ var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/);
+
+ if (!match) return null;
+
+ var encoding = match[1];
+ var q = 1;
+ if (match[2]) {
+ var params = match[2].split(';');
+ for (var i = 0; i < params.length; i ++) {
+ var p = params[i].trim().split('=');
+ if (p[0] === 'q') {
+ q = parseFloat(p[1]);
+ break;
+ }
+ }
+ }
+
+ return {
+ encoding: encoding,
+ q: q
+ };
+}
+
+function getEncodingPriority(encoding, accepted) {
+ return (accepted.filter(function(a) {
+ return specify(encoding, a);
+ }).sort(function (a, b) {
+ // revsort
+ return a.q === b.q ? 0 : a.q > b.q ? -1 : 1;
+ })[0] || {q:0}).q;
+}
+
+function specify(encoding, spec) {
+ if (spec.encoding === '*' || spec.encoding === encoding) {
+ return spec;
+ }
+}
+
+function preferredEncodings(accept, provided) {
+ accept = parseAcceptEncoding(accept || '');
+ if (provided) {
+ return provided.map(function(type) {
+ return [type, getEncodingPriority(type, accept)];
+ }).filter(function(pair) {
+ return pair[1] > 0;
+ }).sort(function(a, b) {
+ // revsort
+ return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1;
+ }).map(function(pair) {
+ return pair[0];
+ });
+ } else {
+ return accept.map(function(type) {
+ return type.encoding;
+ });
+ }
+}
View
53 lib/language.coffee
@@ -1,53 +0,0 @@
-_ = require('underscore')
-
-parseAcceptLanguage = (accept) ->
- acceptableLanguages = accept.split(',').map (e) -> parseLanguage(e.trim())
- _.select(acceptableLanguages, (e) -> e && e.q > 0)
-
-parseLanguage = (s) ->
- match = /^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/.exec(s)
- if !match
- null
- else
- prefix = match[1]
- suffix = match[2]
- full = prefix
- full += "-#{suffix}" if suffix
- params = {}
- q = 1
- if match[3]
- for p in match[3].split(';').map((s) -> s.trim().split('='))
- params[p[0]] = p[1]
- if params.q?
- q = parseFloat(params.q)
- delete params.q
- {prefix: prefix, suffix: suffix, params: params, q: q, full: full}
-
-getLanguagePriority = (language, accepted) ->
- match = getClosestMatch(language, accepted)
- match?.q || 0
-
-getClosestMatch = (language, accepted) ->
- parsed = parseLanguage(language)
- matches = _.select(accepted, (a) -> (a.full == parsed.full))
- return matches[0] unless matches.length == 0
- matches = _.select(accepted, (a) -> a.prefix == parsed.prefix && !a.suffix)
- return matches[0] unless matches.length == 0
- matches = _.select(accepted, (a) -> a.prefix == parsed.prefix)
- return matches[0] unless matches.length == 0
- matches = _.select(accepted, (a) -> a.prefix == '*')
- return matches[0]
-
-preferredLanguages = (accept, provided) ->
- accept = parseAcceptLanguage(accept || '')
- if provided
- _.chain(provided)
- .map((type) -> [type, getLanguagePriority(type, accept)])
- .select((pair) -> pair[1] > 0)
- .sortBy((pair) -> pair[1] * -1)
- .map((pair) -> pair[0])
- .value()
- else
- accept.map((type) -> type.full)
-
-exports.preferredLanguages = preferredLanguages
View
89 lib/language.js
@@ -0,0 +1,89 @@
+module.exports = preferredLanguages;
+preferredLanguages.preferredLanguages = preferredLanguages;
+
+function parseAcceptLanguage(accept) {
+ return accept.split(',').map(function(e) {
+ return parseLanguage(e.trim());
+ }).filter(function(e) {
+ return e && e.q > 0;
+ });
+}
+
+function parseLanguage(s) {
+ var match = s.match(/^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/);
+ if (!match) return null;
+
+ var prefix = match[1],
+ suffix = match[2],
+ full = prefix;
+
+ if (suffix) full += "-" + suffix;
+
+ var q = 1;
+ if (match[3]) {
+ var params = match[3].split(';')
+ for (var i = 0; i < params.length; i ++) {
+ var p = params[i].split('=');
+ if (p[0] === 'q') q = parseFloat(p[1]);
+ }
+ }
+
+ return {
+ prefix: prefix,
+ suffix: suffix,
+ q: q,
+ full: full
+ };
+}
+
+function getLanguagePriority(language, accepted) {
+ var match = getClosestMatch(language, accepted);
+ return match ? match.q : 0;
+}
+
+function getClosestMatch(language, accepted) {
+ var parsed = parseLanguage(language);
+
+ var matches = accepted.filter(function(a) {
+ return a.full === parsed.full;
+ });
+ if (matches.length) return matches[0];
+
+ matches = accepted.filter(function(a) {
+ return a.prefix === parsed.prefix && !a.suffix;
+ });
+ if (matches.length) return matches[0];
+
+ matches = accepted.filter(function(a) {
+ return a.prefix === parsed.prefix;
+ });
+ if (matches.length) return matches[0];
+
+ matches = accepted.filter(function(a) {
+ return a.prefix === '*';
+ });
+ return matches[0];
+}
+
+function preferredLanguages(accept, provided) {
+ accept = parseAcceptLanguage(accept || '');
+ if (provided) {
+
+ var ret = provided.map(function(type) {
+ return [type, getLanguagePriority(type, accept)];
+ }).filter(function(pair) {
+ return pair[1] > 0;
+ }).sort(function(a, b) {
+ // revsort
+ return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1;
+ }).map(function(pair) {
+ return pair[0];
+ });
+ return ret;
+
+ } else {
+ return accept.map(function(type) {
+ return type.full;
+ });
+ }
+}
View
51 lib/mediaType.coffee
@@ -1,51 +0,0 @@
-_ = require('underscore')
-
-parseAccept = (accept) ->
- acceptableTypes = accept.split(',').map (e) -> parseMediaType(e.trim())
- _.select(acceptableTypes, (e) -> e && e.q > 0)
-
-parseMediaType = (s) ->
- match = /\s*(\S+)\/([^;\s]+)\s*(?:;(.*))?/.exec(s)
- if !match
- null
- else
- type = match[1]
- subtype = match[2]
- full = "#{type}/#{subtype}"
- params = {}
- q = 1
- if match[3]
- for p in match[3].split(';').map((s) -> s.trim().split('='))
- params[p[0]] = p[1]
- if params.q?
- q = parseFloat(params.q)
- delete params.q
- {type: type, subtype: subtype, params: params, q: q, full: full}
-
-getMediaTypePriority = (type, accepted) ->
- specs = accepted.map (a) -> specify(type, a)
- specs = _.select(specs, (x) -> x)
- _.max(specs, (spec) -> spec.q)?.q || 0
-
-specifies = (spec, type) ->
- spec == '*' || spec == type
-
-specify = (type, spec) ->
- p = parseMediaType(type)
- spec if specifies(spec.type, p.type) &&
- specifies(spec.subtype, p.subtype) &&
- _.all(spec.params, (k, v) -> specifies(v, p.params[k]))
-
-preferredMediaTypes = (accept, provided) ->
- accept = parseAccept(accept || '')
- if provided
- _.chain(provided)
- .map((type) -> [type, getMediaTypePriority(type, accept)])
- .select((pair) -> pair[1] > 0)
- .sortBy((pair) -> pair[1] * -1)
- .map((pair) -> pair[0])
- .value()
- else
- accept.map((type) -> type.full)
-
-exports.preferredMediaTypes = preferredMediaTypes
View
98 lib/mediaType.js
@@ -0,0 +1,98 @@
+module.exports = preferredMediaTypes;
+preferredMediaTypes.preferredMediaTypes = preferredMediaTypes;
+
+function parseAccept(accept) {
+ return accept.split(',').map(function(e) {
+ return parseMediaType(e.trim());
+ }).filter(function(e) {
+ return e && e.q > 0;
+ });
+};
+
+function parseMediaType(s) {
+ match = s.match(/\s*(\S+)\/([^;\s]+)\s*(?:;(.*))?/);
+ if (!match) return null;
+
+ var type = match[1],
+ subtype = match[2],
+ full = "" + type + "/" + subtype,
+ params = {},
+ q = 1;
+
+ if (match[3]) {
+ params = match[3].split(';').map(function(s) {
+ return s.trim().split('=');
+ }).reduce(function (set, p) {
+ set[p[0]] = p[1];
+ return set
+ }, params);
+
+ if (params.q != null) {
+ q = parseFloat(params.q);
+ delete params.q;
+ }
+ }
+
+ return {
+ type: type,
+ subtype: subtype,
+ params: params,
+ q: q,
+ full: full
+ };
+}
+
+function getMediaTypePriority(type, accepted) {
+ return (accepted.filter(function(a) {
+ return specify(type, a);
+ }).sort(function (a, b) {
+ // revsort
+ return a.q > b.q ? -1 : 1;
+ })[0] || {q:0}).q;
+}
+
+function specifies(spec, type) {
+ return spec === '*' || spec === type;
+}
+
+function specify(type, spec) {
+ var p = parseMediaType(type);
+
+ if (spec.params) {
+ var keys = Object.keys(spec.params);
+ if (keys.some(function (k) {
+ return !specifies(spec.params[k], p.params[k]);
+ })) {
+ // some didn't specify.
+ return null;
+ }
+ }
+
+ if (specifies(spec.type, p.type) &&
+ specifies(spec.subtype, p.subtype)) {
+ return spec;
+ }
+}
+
+function preferredMediaTypes(accept, provided) {
+ accept = parseAccept(accept || '');
+ if (provided) {
+ return provided.map(function(type) {
+ return [type, getMediaTypePriority(type, accept)];
+ }).filter(function(pair) {
+ return pair[1] > 0;
+ }).sort(function(a, b) {
+ // revsort
+ return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1;
+ }).map(function(pair) {
+ return pair[0];
+ });
+
+ } else {
+ return accept.map(function(type) {
+ return type.full;
+ });
+ }
+}
+
+
View
33 lib/negotiator.coffee
@@ -1,33 +0,0 @@
-preferredLanguages = require('./language').preferredLanguages
-preferredMediaTypes = require('./mediaType').preferredMediaTypes
-preferredCharsets = require('./charset').preferredCharsets
-preferredEncodings = require('./encoding').preferredEncodings
-
-class Negotiator
- constructor: (@request) ->
-
- preferredLanguages: (availableLanguages) ->
- preferredLanguages(@request.headers['accept-language'], availableLanguages)
-
- preferredLanguage: (availableLanguages) ->
- @preferredLanguages(availableLanguages)?[0]
-
- preferredMediaTypes: (availableMediaTypes) ->
- preferredMediaTypes(@request.headers['accept'], availableMediaTypes)
-
- preferredMediaType: (availableMediaTypes) ->
- @preferredMediaTypes(availableMediaTypes)?[0]
-
- preferredCharsets: (availableCharsets) ->
- preferredCharsets(@request.headers['accept-charset'], availableCharsets)
-
- preferredCharset: (availableCharsets) ->
- @preferredCharsets(availableCharsets)?[0]
-
- preferredEncodings: (availableEncodings) ->
- preferredEncodings(@request.headers['accept-encoding'], availableEncodings)
-
- preferredEncoding: (availableEncodings) ->
- @preferredEncodings(availableEncodings)?[0]
-
-exports.Negotiator = Negotiator
View
28 lib/negotiator.js
@@ -0,0 +1,28 @@
+module.exports = Negotiator;
+Negotiator.Negotiator = Negotiator;
+
+function Negotiator(request) {
+ this.request = request;
+}
+
+var set = { preferredCharset: [require('./charset.js'), 'accept-charset'],
+ preferredEncoding: [require('./encoding.js'), 'accept-encoding'],
+ preferredLanguage: [require('./language.js'), 'accept-language'],
+ preferredMediaType: [require('./mediaType.js'), 'accept'] };
+
+Object.keys(set).forEach(function (k) {
+ var mh = set[k],
+ method = mh[0],
+ header = mh[1],
+ singular = k,
+ plural = k + 's';
+
+ Negotiator.prototype[plural] = function (available) {
+ return method(this.request.headers[header], available);
+ };
+
+ Negotiator.prototype[singular] = function() {
+ var set = this[plural]();
+ if (set) return set[0];
+ };
+})
View
35 package.json
@@ -1,23 +1,30 @@
{
- "name": "negotiator",
- "description": "HTTP content negotiation",
- "version": "0.1.0",
- "author": "Federico Romero <federico.romero@outboxlabs.com>",
+ "name": "negotiator.js",
+ "description": "HTTP content negotiation (js fork of negotiator)",
+ "version": "0.2.2",
"repository": {
"type": "git",
- "url": "https://github.com/federomero/negotiator.git"
+ "url": "git://github.com/isaacs/negotiator.js.git"
},
- "keywords": ["http", "content negotiation", "accept", "accept-language", "accept-encoding", "accept-charset"],
- "engine": "node > 0.6",
+ "keywords": [
+ "http",
+ "content negotiation",
+ "accept",
+ "accept-language",
+ "accept-encoding",
+ "accept-charset"
+ ],
+ "engine": "node >= 0.6",
"license": "MIT",
"devDependencies": {
- "nodeunit": "0.6.x",
- "iconv": "1.1.x",
- "gzip-buffer": "x.x.x"
+ "nodeunit": "0.6.x"
},
- "dependencies": {
- "underscore": "1.2.x",
- "coffee-script": "1.2.x"
+ "scripts": {
+ "test": "nodeunit test"
},
- "scripts": { "test": "nodeunit test" }
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "main": "lib/negotiator.js"
}
View
22 readme.md
@@ -1,17 +1,19 @@
# Negotiator
-An HTTP content negotiator for node.js written in coffeescript.
+An HTTP content negotiator for node.js written in javascript.
+
+This is a port to JavaScript of <https://github.com/federomero/negotiator>.
# Accept Negotiation
- Negotiator = require('negotiator').Negotiator
+ Negotiator = require('negotiator')
availableMediaTypes = ['text/html', 'text/plain', 'application/json']
# The negotiator constructor receives a request object
negotiator = new Negotiator(request)
- # Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8'
+ # Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg';q=0.8
negotiator.preferredMediaTypes()
# -> ['text/html', 'application/*', 'image/jpeg']
@@ -22,7 +24,7 @@ An HTTP content negotiator for node.js written in coffeescript.
negotiator.preferredLanguage(availableMediaTypes)
# -> 'text/html'
-You can check a working example at `examples/accept.coffee`.
+You can check a working example at `examples/accept.js`.
## Methods
@@ -36,7 +38,7 @@ Returns the top preferred media type from a list of available media types.
# Accept-Language Negotiation
- Negotiator = require('negotiator').Negotiator
+ Negotiator = require('negotiator')
negotiator = new Negotiator(request)
@@ -53,7 +55,7 @@ Returns the top preferred media type from a list of available media types.
language = negotiator.preferredLanguage(availableLanguages)
# -> 'es'
-You can check a working example at `examples/language.coffee`.
+You can check a working example at `examples/language.js`.
## Methods
@@ -67,7 +69,7 @@ Returns the top preferred language from a list of available languages.
# Accept-Charset Negotiation
- Negotiator = require('negotiator').Negotiator
+ Negotiator = require('negotiator')
availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5']
@@ -84,7 +86,7 @@ Returns the top preferred language from a list of available languages.
negotiator.preferredCharset(availableCharsets)
# -> 'utf-8'
-You can check a working example at `examples/charset.coffee`.
+You can check a working example at `examples/charset.js`.
## Methods
@@ -115,7 +117,7 @@ Returns the top preferred charset from a list of available charsets.
negotiator.preferredEncoding(availableEncodings)
# -> 'gzip'
-You can check a working example at `examples/encoding.coffee`.
+You can check a working example at `examples/encoding.js`.
## Methods
@@ -129,4 +131,4 @@ Returns the top preferred encoding from a list of available encodings.
# License
-[MIT](http://www.opensource.org/licenses/MIT)
+MIT
View
46 test/charset.coffee
@@ -1,46 +0,0 @@
-preferredCharsets = require('../lib/charset').preferredCharsets
-
-@["Should not return a charset when no charset is provided"] = (test) ->
- test.deepEqual(preferredCharsets('*', []), [])
- test.done()
-
-@["Should not return a charset when no charset is acceptable"] = (test) ->
- test.deepEqual(preferredCharsets('ISO-8859-1', ['utf-8']), [])
- test.done()
-
-@["Should not return a charset with q = 0"] = (test) ->
- test.deepEqual(preferredCharsets('utf-8;q=0', ['utf-8']), [])
- test.done()
-
-testCorrectCharset = (c) =>
- @["Should return #{c.selected} for accept-charset header #{c.accept} with provided charset #{c.provided}"] = (test) ->
- test.deepEqual(preferredCharsets(c.accept, c.provided), c.selected)
- test.done()
-
-testConfigurations =
- [{
- accept: 'utf-8'
- provided: ['utf-8']
- selected: ['utf-8']
- },{
- accept: '*'
- provided: ['utf-8']
- selected: ['utf-8']
- },{
- accept: 'utf-8'
- provided: ['utf-8', 'ISO-8859-1']
- selected: ['utf-8']
- },{
- accept: 'utf-8, ISO-8859-1'
- provided: ['utf-8']
- selected: ['utf-8']
- },{
- accept: 'utf-8;q=0.8, ISO-8859-1'
- provided: ['utf-8', 'ISO-8859-1']
- selected:[ 'ISO-8859-1', 'utf-8']
- }]
-
-
-for configuration in testConfigurations
- testCorrectCharset(configuration)
-
View
58 test/charset.js
@@ -0,0 +1,58 @@
+(function() {
+ var configuration, preferredCharsets, testConfigurations, testCorrectCharset, _i, _len,
+ _this = this;
+
+ preferredCharsets = require('../lib/charset').preferredCharsets;
+
+ this["Should not return a charset when no charset is provided"] = function(test) {
+ test.deepEqual(preferredCharsets('*', []), []);
+ return test.done();
+ };
+
+ this["Should not return a charset when no charset is acceptable"] = function(test) {
+ test.deepEqual(preferredCharsets('ISO-8859-1', ['utf-8']), []);
+ return test.done();
+ };
+
+ this["Should not return a charset with q = 0"] = function(test) {
+ test.deepEqual(preferredCharsets('utf-8;q=0', ['utf-8']), []);
+ return test.done();
+ };
+
+ testCorrectCharset = function(c) {
+ return _this["Should return " + c.selected + " for accept-charset header " + c.accept + " with provided charset " + c.provided] = function(test) {
+ test.deepEqual(preferredCharsets(c.accept, c.provided), c.selected);
+ return test.done();
+ };
+ };
+
+ testConfigurations = [
+ {
+ accept: 'utf-8',
+ provided: ['utf-8'],
+ selected: ['utf-8']
+ }, {
+ accept: '*',
+ provided: ['utf-8'],
+ selected: ['utf-8']
+ }, {
+ accept: 'utf-8',
+ provided: ['utf-8', 'ISO-8859-1'],
+ selected: ['utf-8']
+ }, {
+ accept: 'utf-8, ISO-8859-1',
+ provided: ['utf-8'],
+ selected: ['utf-8']
+ }, {
+ accept: 'utf-8;q=0.8, ISO-8859-1',
+ provided: ['utf-8', 'ISO-8859-1'],
+ selected: ['ISO-8859-1', 'utf-8']
+ }
+ ];
+
+ for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) {
+ configuration = testConfigurations[_i];
+ testCorrectCharset(configuration);
+ }
+
+}).call(this);
View
50 test/encoding.coffee
@@ -1,50 +0,0 @@
-preferredEncodings = require('../lib/encoding').preferredEncodings
-
-@["Should return identity encoding when no encoding is provided"] = (test) ->
- test.deepEqual(preferredEncodings(null), ['identity'])
- test.done()
-
-@["Should include the identity encoding even if not explicity listed"] = (test) ->
- test.ok(preferredEncodings('gzip').indexOf('identity') != -1)
- test.done()
-
-@["Should not return identity encoding if q = 0"] = (test) ->
- test.ok(preferredEncodings('identity;q=0').indexOf('identity') == -1)
- test.done()
-
-testCorrectEncoding = (c) =>
- @["Should return #{c.selected} for accept-encoding header #{c.accept} with provided encoding #{c.provided}"] = (test) ->
- test.deepEqual(preferredEncodings(c.accept, c.provided), c.selected)
- test.done()
-
-testConfigurations =
- [{
- accept: 'gzip'
- provided: ['identity', 'gzip']
- selected: ['gzip', 'identity']
- },{
- accept: 'gzip, compress'
- provided: ['compress']
- selected: ['compress']
- },{
- accept: '*'
- provided: ['identity', 'gzip']
- selected: ['identity', 'gzip']
- },{
- accept: 'gzip, compress'
- provided: ['compress', 'identity']
- selected: ['compress', 'identity']
- },{
- accept: 'gzip;q=0.8, identity;q=0.5, *;q=0.3'
- provided: ['identity', 'gzip', 'compress']
- selected: ['gzip', 'identity', 'compress']
- },{
- accept: 'gzip;q=0.8, compress'
- provided: ['gzip', 'compress']
- selected: ['compress', 'gzip']
- }]
-
-
-for configuration in testConfigurations
- testCorrectEncoding(configuration)
-
View
62 test/encoding.js
@@ -0,0 +1,62 @@
+(function() {
+ var configuration, preferredEncodings, testConfigurations, testCorrectEncoding, _i, _len,
+ _this = this;
+
+ preferredEncodings = require('../lib/encoding').preferredEncodings;
+
+ this["Should return identity encoding when no encoding is provided"] = function(test) {
+ test.deepEqual(preferredEncodings(null), ['identity']);
+ return test.done();
+ };
+
+ this["Should include the identity encoding even if not explicity listed"] = function(test) {
+ test.ok(preferredEncodings('gzip').indexOf('identity') !== -1);
+ return test.done();
+ };
+
+ this["Should not return identity encoding if q = 0"] = function(test) {
+ test.ok(preferredEncodings('identity;q=0').indexOf('identity') === -1);
+ return test.done();
+ };
+
+ testCorrectEncoding = function(c) {
+ return _this["Should return " + c.selected + " for accept-encoding header " + c.accept + " with provided encoding " + c.provided] = function(test) {
+ test.deepEqual(preferredEncodings(c.accept, c.provided), c.selected);
+ return test.done();
+ };
+ };
+
+ testConfigurations = [
+ {
+ accept: 'gzip',
+ provided: ['identity', 'gzip'],
+ selected: ['gzip', 'identity']
+ }, {
+ accept: 'gzip, compress',
+ provided: ['compress'],
+ selected: ['compress']
+ }, {
+ accept: '*',
+ provided: ['identity', 'gzip'],
+ selected: ['identity', 'gzip']
+ }, {
+ accept: 'gzip, compress',
+ provided: ['compress', 'identity'],
+ selected: ['compress', 'identity']
+ }, {
+ accept: 'gzip;q=0.8, identity;q=0.5, *;q=0.3',
+ provided: ['identity', 'gzip', 'compress'],
+ selected: ['gzip', 'identity', 'compress']
+ }, {
+ accept: 'gzip;q=0.8, compress',
+ provided: ['gzip', 'compress'],
+ selected: ['compress', 'gzip']
+ }
+ ];
+
+ for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) {
+ configuration = testConfigurations[_i];
+ testCorrectEncoding(configuration);
+ }
+
+}).call(this);
View
55 test/language.coffee
@@ -1,55 +0,0 @@
-preferredLanguages = require('../lib/language').preferredLanguages
-
-@["Should not return a language when no is provided"] = (test) ->
- test.deepEqual(preferredLanguages('*', []), [])
- test.done()
-
-@["Should not return a language when no language is acceptable"] = (test) ->
- test.deepEqual(preferredLanguages('en', ['es']), [])
- test.done()
-
-@["Should not return a language with q = 0"] = (test) ->
- test.deepEqual(preferredLanguages('en;q=0', ['en']), [])
- test.done()
-
-
-testCorrectType = (c) =>
- @["Should return #{c.selected} for accept-language header #{c.accept} with provided language #{c.provided}"] = (test) ->
- test.deepEqual(preferredLanguages(c.accept, c.provided), c.selected)
- test.done()
-
-testConfigurations =
- [{
- accept: 'en'
- provided: ['en']
- selected: ['en']
- },{
- accept: '*'
- provided: ['en']
- selected: ['en']
- },{
- accept: 'en-US, en;q=0.8'
- provided: ['en-US', 'en-GB']
- selected: ['en-US', 'en-GB']
- },{
- accept: 'en-US, en-GB'
- provided: ['en-US']
- selected: ['en-US']
- },{
- accept: 'en'
- provided: ['en-US']
- selected: ['en-US']
- },{
- accept: 'en;q=0.8, es'
- provided: ['en', 'es']
- selected: ['es', 'en']
- },{
- accept: 'en-US;q=0.8, es'
- provided: ['en', 'es']
- selected: ['es', 'en']
- }]
-
-
-for configuration in testConfigurations
- testCorrectType(configuration)
-
View
66 test/language.js
@@ -0,0 +1,66 @@
+(function() {
+ var configuration, preferredLanguages, testConfigurations, testCorrectType, _i, _len,
+ _this = this;
+
+ preferredLanguages = require('../lib/language').preferredLanguages;
+
+ this["Should not return a language when no is provided"] = function(test) {
+ test.deepEqual(preferredLanguages('*', []), []);
+ return test.done();
+ };
+
+ this["Should not return a language when no language is acceptable"] = function(test) {
+ test.deepEqual(preferredLanguages('en', ['es']), []);
+ return test.done();
+ };
+
+ this["Should not return a language with q = 0"] = function(test) {
+ test.deepEqual(preferredLanguages('en;q=0', ['en']), []);
+ return test.done();
+ };
+
+ testCorrectType = function(c) {
+ return _this["Should return " + c.selected + " for accept-language header " + c.accept + " with provided language " + c.provided] = function(test) {
+ test.deepEqual(preferredLanguages(c.accept, c.provided), c.selected);
+ return test.done();
+ };
+ };
+
+ testConfigurations = [
+ {
+ accept: 'en',
+ provided: ['en'],
+ selected: ['en']
+ }, {
+ accept: '*',
+ provided: ['en'],
+ selected: ['en']
+ }, {
+ accept: 'en-US, en;q=0.8',
+ provided: ['en-US', 'en-GB'],
+ selected: ['en-US', 'en-GB']
+ }, {
+ accept: 'en-US, en-GB',
+ provided: ['en-US'],
+ selected: ['en-US']
+ }, {
+ accept: 'en',
+ provided: ['en-US'],
+ selected: ['en-US']
+ }, {
+ accept: 'en;q=0.8, es',
+ provided: ['en', 'es'],
+ selected: ['es', 'en']
+ }, {
+ accept: 'en-US;q=0.8, es',
+ provided: ['en', 'es'],
+ selected: ['es', 'en']
+ }
+ ];
+
+ for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) {
+ configuration = testConfigurations[_i];
+ testCorrectType(configuration);
+ }
+
+}).call(this);
View
55 test/mediaType.coffee
@@ -1,55 +0,0 @@
-preferredMediaTypes = require('../lib/mediaType').preferredMediaTypes
-
-@["Should not return a media type when no media type provided"] = (test) ->
- test.deepEqual(preferredMediaTypes('*/*', []), [])
- test.done()
-
-@["Should not return a media type when no media type is acceptable"] = (test) ->
- test.deepEqual(preferredMediaTypes('application/json', ['text/html']), [])
- test.done()
-
-@["Should not return a media type with q = 0"] = (test) ->
- test.deepEqual(preferredMediaTypes('text/html;q=0', ['text/html']), [])
- test.done()
-
-
-testCorrectType = (c) =>
- @["Should return #{c.selected} for access header #{c.accept} with provided types #{c.provided}"] = (test) ->
- test.deepEqual(preferredMediaTypes(c.accept, c.provided), c.selected)
- test.done()
-
-testConfigurations =
- [{
- accept: 'text/html'
- provided: ['text/html']
- selected: ['text/html']
- },{
- accept: '*/*'
- provided: ['text/html']
- selected: ['text/html']
- },{
- accept: 'text/*'
- provided: ['text/html']
- selected: ['text/html']
- },{
- accept: 'application/json, text/html'
- provided: ['text/html']
- selected: ['text/html']
- },{
- accept: 'text/html;q=0.1'
- provided: ['text/html']
- selected: ['text/html']
- },{
- accept: 'application/json, text/html'
- provided: ['application/json', 'text/html']
- selected: ['application/json', 'text/html']
- },{
- accept: 'application/json;q=0.2, text/html'
- provided: ['application/json', 'text/html']
- selected: ['text/html', 'application/json']
- }]
-
-
-for configuration in testConfigurations
- testCorrectType(configuration)
-
View
66 test/mediaType.js
@@ -0,0 +1,66 @@
+(function() {
+ var configuration, preferredMediaTypes, testConfigurations, testCorrectType, _i, _len,
+ _this = this;
+
+ preferredMediaTypes = require('../lib/mediaType').preferredMediaTypes;
+
+ this["Should not return a media type when no media type provided"] = function(test) {
+ test.deepEqual(preferredMediaTypes('*/*', []), []);
+ return test.done();
+ };
+
+ this["Should not return a media type when no media type is acceptable"] = function(test) {
+ test.deepEqual(preferredMediaTypes('application/json', ['text/html']), []);
+ return test.done();
+ };
+
+ this["Should not return a media type with q = 0"] = function(test) {
+ test.deepEqual(preferredMediaTypes('text/html;q=0', ['text/html']), []);
+ return test.done();
+ };
+
+ testCorrectType = function(c) {
+ return _this["Should return " + c.selected + " for access header " + c.accept + " with provided types " + c.provided] = function(test) {
+ test.deepEqual(preferredMediaTypes(c.accept, c.provided), c.selected);
+ return test.done();
+ };
+ };
+
+ testConfigurations = [
+ {
+ accept: 'text/html',
+ provided: ['text/html'],
+ selected: ['text/html']
+ }, {
+ accept: '*/*',
+ provided: ['text/html'],
+ selected: ['text/html']
+ }, {
+ accept: 'text/*',
+ provided: ['text/html'],
+ selected: ['text/html']
+ }, {
+ accept: 'application/json, text/html',
+ provided: ['text/html'],
+ selected: ['text/html']
+ }, {
+ accept: 'text/html;q=0.1',
+ provided: ['text/html'],
+ selected: ['text/html']
+ }, {
+ accept: 'application/json, text/html',
+ provided: ['application/json', 'text/html'],
+ selected: ['application/json', 'text/html']
+ }, {
+ accept: 'application/json;q=0.2, text/html',
+ provided: ['application/json', 'text/html'],
+ selected: ['text/html', 'application/json']
+ }
+ ];
+
+ for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) {
+ configuration = testConfigurations[_i];
+ testCorrectType(configuration);
+ }
+
+}).call(this);
Please sign in to comment.
Something went wrong with that request. Please try again.