mediaType.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. module.exports = preferredMediaTypes;
  2. preferredMediaTypes.preferredMediaTypes = preferredMediaTypes;
  3. function parseAccept(accept) {
  4. return accept.split(',').map(function(e) {
  5. return parseMediaType(e.trim());
  6. }).filter(function(e) {
  7. return e;
  8. });
  9. };
  10. function parseMediaType(s) {
  11. var match = s.match(/\s*(\S+?)\/([^;\s]+)\s*(?:;(.*))?/);
  12. if (!match) return null;
  13. var type = match[1],
  14. subtype = match[2],
  15. full = "" + type + "/" + subtype,
  16. params = {},
  17. q = 1;
  18. if (match[3]) {
  19. params = match[3].split(';').map(function(s) {
  20. return s.trim().split('=');
  21. }).reduce(function (set, p) {
  22. set[p[0]] = p[1];
  23. return set
  24. }, params);
  25. if (params.q != null) {
  26. q = parseFloat(params.q);
  27. delete params.q;
  28. }
  29. }
  30. return {
  31. type: type,
  32. subtype: subtype,
  33. params: params,
  34. q: q,
  35. full: full
  36. };
  37. }
  38. function getMediaTypePriority(type, accepted) {
  39. return (accepted.map(function(a) {
  40. return specify(type, a);
  41. }).filter(Boolean).sort(function (a, b) {
  42. if(a.s == b.s) {
  43. return a.q > b.q ? -1 : 1;
  44. } else {
  45. return a.s > b.s ? -1 : 1;
  46. }
  47. })[0] || {s: 0, q: 0});
  48. }
  49. function specify(type, spec) {
  50. var p = parseMediaType(type);
  51. var s = 0;
  52. if (!p) {
  53. return null;
  54. }
  55. if(spec.type == p.type) {
  56. s |= 4
  57. } else if(spec.type != '*') {
  58. return null;
  59. }
  60. if(spec.subtype == p.subtype) {
  61. s |= 2
  62. } else if(spec.subtype != '*') {
  63. return null;
  64. }
  65. var keys = Object.keys(spec.params);
  66. if (keys.length > 0) {
  67. if (keys.every(function (k) {
  68. return spec.params[k] == '*' || spec.params[k] == p.params[k];
  69. })) {
  70. s |= 1
  71. } else {
  72. return null
  73. }
  74. }
  75. return {
  76. q: spec.q,
  77. s: s,
  78. }
  79. }
  80. function preferredMediaTypes(accept, provided) {
  81. // RFC 2616 sec 14.2: no header = */*
  82. accept = parseAccept(accept === undefined ? '*/*' : accept || '');
  83. if (provided) {
  84. return provided.map(function(type) {
  85. return [type, getMediaTypePriority(type, accept)];
  86. }).filter(function(pair) {
  87. return pair[1].q > 0;
  88. }).sort(function(a, b) {
  89. var pa = a[1];
  90. var pb = b[1];
  91. if(pa.q == pb.q) {
  92. return pa.s < pb.s ? 1 : -1;
  93. } else {
  94. return pa.q < pb.q ? 1 : -1;
  95. }
  96. }).map(function(pair) {
  97. return pair[0];
  98. });
  99. } else {
  100. return accept.sort(function (a, b) {
  101. // revsort
  102. return a.q < b.q ? 1 : -1;
  103. }).filter(function(type) {
  104. return type.q > 0;
  105. }).map(function(type) {
  106. return type.full;
  107. });
  108. }
  109. }