index.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*!
  2. * media-typer
  3. * Copyright(c) 2014-2017 Douglas Christopher Wilson
  4. * MIT Licensed
  5. */
  6. 'use strict'
  7. /**
  8. * RegExp to match type in RFC 6838
  9. *
  10. * type-name = restricted-name
  11. * subtype-name = restricted-name
  12. * restricted-name = restricted-name-first *126restricted-name-chars
  13. * restricted-name-first = ALPHA / DIGIT
  14. * restricted-name-chars = ALPHA / DIGIT / "!" / "#" /
  15. * "$" / "&" / "-" / "^" / "_"
  16. * restricted-name-chars =/ "." ; Characters before first dot always
  17. * ; specify a facet name
  18. * restricted-name-chars =/ "+" ; Characters after last plus always
  19. * ; specify a structured syntax suffix
  20. * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
  21. * DIGIT = %x30-39 ; 0-9
  22. */
  23. var SUBTYPE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$/
  24. var TYPE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/
  25. var TYPE_REGEXP = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/
  26. /**
  27. * Module exports.
  28. */
  29. exports.format = format
  30. exports.parse = parse
  31. exports.test = test
  32. /**
  33. * Format object to media type.
  34. *
  35. * @param {object} obj
  36. * @return {string}
  37. * @public
  38. */
  39. function format (obj) {
  40. if (!obj || typeof obj !== 'object') {
  41. throw new TypeError('argument obj is required')
  42. }
  43. var subtype = obj.subtype
  44. var suffix = obj.suffix
  45. var type = obj.type
  46. if (!type || !TYPE_NAME_REGEXP.test(type)) {
  47. throw new TypeError('invalid type')
  48. }
  49. if (!subtype || !SUBTYPE_NAME_REGEXP.test(subtype)) {
  50. throw new TypeError('invalid subtype')
  51. }
  52. // format as type/subtype
  53. var string = type + '/' + subtype
  54. // append +suffix
  55. if (suffix) {
  56. if (!TYPE_NAME_REGEXP.test(suffix)) {
  57. throw new TypeError('invalid suffix')
  58. }
  59. string += '+' + suffix
  60. }
  61. return string
  62. }
  63. /**
  64. * Test media type.
  65. *
  66. * @param {string} string
  67. * @return {object}
  68. * @public
  69. */
  70. function test (string) {
  71. if (!string) {
  72. throw new TypeError('argument string is required')
  73. }
  74. if (typeof string !== 'string') {
  75. throw new TypeError('argument string is required to be a string')
  76. }
  77. return TYPE_REGEXP.test(string.toLowerCase())
  78. }
  79. /**
  80. * Parse media type to object.
  81. *
  82. * @param {string} string
  83. * @return {object}
  84. * @public
  85. */
  86. function parse (string) {
  87. if (!string) {
  88. throw new TypeError('argument string is required')
  89. }
  90. if (typeof string !== 'string') {
  91. throw new TypeError('argument string is required to be a string')
  92. }
  93. var match = TYPE_REGEXP.exec(string.toLowerCase())
  94. if (!match) {
  95. throw new TypeError('invalid media type')
  96. }
  97. var type = match[1]
  98. var subtype = match[2]
  99. var suffix
  100. // suffix after last +
  101. var index = subtype.lastIndexOf('+')
  102. if (index !== -1) {
  103. suffix = subtype.substr(index + 1)
  104. subtype = subtype.substr(0, index)
  105. }
  106. return new MediaType(type, subtype, suffix)
  107. }
  108. /**
  109. * Class for MediaType object.
  110. * @public
  111. */
  112. function MediaType (type, subtype, suffix) {
  113. this.type = type
  114. this.subtype = subtype
  115. this.suffix = suffix
  116. }