--- /dev/null
+module.exports = preferredEncodings;
+preferredEncodings.preferredEncodings = preferredEncodings;
+
+function parseAcceptEncoding(accept) {
+ var accepts = accept.split(',');
+ var hasIdentity = false;
+ var minQuality = 1;
+
+ for (var i = 0, j = 0; i < accepts.length; i++) {
+ var encoding = parseEncoding(accepts[i].trim(), i);
+
+ if (encoding) {
+ accepts[j++] = encoding;
+ hasIdentity = hasIdentity || specify('identity', encoding);
+ minQuality = Math.min(minQuality, encoding.q || 1);
+ }
+ }
+
+ if (!hasIdentity) {
+ /*
+ * If identity doesn't explicitly appear in the accept-encoding header,
+ * it's added to the list of acceptable encoding with the lowest q
+ */
+ accepts[j++] = {
+ encoding: 'identity',
+ q: minQuality,
+ i: i
+ };
+ }
+
+ // trim accepts
+ accepts.length = j;
+
+ return accepts;
+}
+
+function parseEncoding(s, i) {
+ 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,
+ i: i
+ };
+}
+
+function getEncodingPriority(encoding, accepted, index) {
+ var priority = {o: -1, q: 0, s: 0};
+
+ for (var i = 0; i < accepted.length; i++) {
+ var spec = specify(encoding, accepted[i], index);
+
+ if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
+ priority = spec;
+ }
+ }
+
+ return priority;
+}
+
+function specify(encoding, spec, index) {
+ var s = 0;
+ if(spec.encoding.toLowerCase() === encoding.toLowerCase()){
+ s |= 1;
+ } else if (spec.encoding !== '*' ) {
+ return null
+ }
+
+ return {
+ i: index,
+ o: spec.i,
+ q: spec.q,
+ s: s
+ }
+};
+
+function preferredEncodings(accept, provided) {
+ var accepts = parseAcceptEncoding(accept || '');
+
+ if (!provided) {
+ // sorted list of all encodings
+ return accepts.filter(isQuality).sort(compareSpecs).map(function getEncoding(spec) {
+ return spec.encoding;
+ });
+ }
+
+ var priorities = provided.map(function getPriority(type, index) {
+ return getEncodingPriority(type, accepts, index);
+ });
+
+ // sorted list of accepted encodings
+ return priorities.filter(isQuality).sort(compareSpecs).map(function getEncoding(priority) {
+ return provided[priorities.indexOf(priority)];
+ });
+}
+
+function compareSpecs(a, b) {
+ return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;
+}
+
+function isQuality(spec) {
+ return spec.q > 0;
+}