Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. 'use strict'
  2. var legacy = require('character-entities-legacy')
  3. var invalid = require('character-reference-invalid')
  4. var decimal = require('is-decimal')
  5. var hexadecimal = require('is-hexadecimal')
  6. var alphanumerical = require('is-alphanumerical')
  7. var decodeEntity = require('./decode-entity')
  8. module.exports = parseEntities
  9. var own = {}.hasOwnProperty
  10. var fromCharCode = String.fromCharCode
  11. var noop = Function.prototype
  12. // Default settings.
  13. var defaults = {
  14. warning: null,
  15. reference: null,
  16. text: null,
  17. warningContext: null,
  18. referenceContext: null,
  19. textContext: null,
  20. position: {},
  21. additional: null,
  22. attribute: false,
  23. nonTerminated: true
  24. }
  25. // Characters.
  26. var tab = 9 // '\t'
  27. var lineFeed = 10 // '\n'
  28. var formFeed = 12 // '\f'
  29. var space = 32 // ' '
  30. var ampersand = 38 // '&'
  31. var semicolon = 59 // ';'
  32. var lessThan = 60 // '<'
  33. var equalsTo = 61 // '='
  34. var numberSign = 35 // '#'
  35. var uppercaseX = 88 // 'X'
  36. var lowercaseX = 120 // 'x'
  37. var replacementCharacter = 65533 // '�'
  38. // Reference types.
  39. var name = 'named'
  40. var hexa = 'hexadecimal'
  41. var deci = 'decimal'
  42. // Map of bases.
  43. var bases = {}
  44. bases[hexa] = 16
  45. bases[deci] = 10
  46. // Map of types to tests.
  47. // Each type of character reference accepts different characters.
  48. // This test is used to detect whether a reference has ended (as the semicolon
  49. // is not strictly needed).
  50. var tests = {}
  51. tests[name] = alphanumerical
  52. tests[deci] = decimal
  53. tests[hexa] = hexadecimal
  54. // Warning types.
  55. var namedNotTerminated = 1
  56. var numericNotTerminated = 2
  57. var namedEmpty = 3
  58. var numericEmpty = 4
  59. var namedUnknown = 5
  60. var numericDisallowed = 6
  61. var numericProhibited = 7
  62. // Warning messages.
  63. var messages = {}
  64. messages[namedNotTerminated] =
  65. 'Named character references must be terminated by a semicolon'
  66. messages[numericNotTerminated] =
  67. 'Numeric character references must be terminated by a semicolon'
  68. messages[namedEmpty] = 'Named character references cannot be empty'
  69. messages[numericEmpty] = 'Numeric character references cannot be empty'
  70. messages[namedUnknown] = 'Named character references must be known'
  71. messages[numericDisallowed] =
  72. 'Numeric character references cannot be disallowed'
  73. messages[numericProhibited] =
  74. 'Numeric character references cannot be outside the permissible Unicode range'
  75. // Wrap to ensure clean parameters are given to `parse`.
  76. function parseEntities(value, options) {
  77. var settings = {}
  78. var option
  79. var key
  80. if (!options) {
  81. options = {}
  82. }
  83. for (key in defaults) {
  84. option = options[key]
  85. settings[key] =
  86. option === null || option === undefined ? defaults[key] : option
  87. }
  88. if (settings.position.indent || settings.position.start) {
  89. settings.indent = settings.position.indent || []
  90. settings.position = settings.position.start
  91. }
  92. return parse(value, settings)
  93. }
  94. // Parse entities.
  95. // eslint-disable-next-line complexity
  96. function parse(value, settings) {
  97. var additional = settings.additional
  98. var nonTerminated = settings.nonTerminated
  99. var handleText = settings.text
  100. var handleReference = settings.reference
  101. var handleWarning = settings.warning
  102. var textContext = settings.textContext
  103. var referenceContext = settings.referenceContext
  104. var warningContext = settings.warningContext
  105. var pos = settings.position
  106. var indent = settings.indent || []
  107. var length = value.length
  108. var index = 0
  109. var lines = -1
  110. var column = pos.column || 1
  111. var line = pos.line || 1
  112. var queue = ''
  113. var result = []
  114. var entityCharacters
  115. var namedEntity
  116. var terminated
  117. var characters
  118. var character
  119. var reference
  120. var following
  121. var warning
  122. var reason
  123. var output
  124. var entity
  125. var begin
  126. var start
  127. var type
  128. var test
  129. var prev
  130. var next
  131. var diff
  132. var end
  133. if (typeof additional === 'string') {
  134. additional = additional.charCodeAt(0)
  135. }
  136. // Cache the current point.
  137. prev = now()
  138. // Wrap `handleWarning`.
  139. warning = handleWarning ? parseError : noop
  140. // Ensure the algorithm walks over the first character and the end
  141. // (inclusive).
  142. index--
  143. length++
  144. while (++index < length) {
  145. // If the previous character was a newline.
  146. if (character === lineFeed) {
  147. column = indent[lines] || 1
  148. }
  149. character = value.charCodeAt(index)
  150. if (character === ampersand) {
  151. following = value.charCodeAt(index + 1)
  152. // The behaviour depends on the identity of the next character.
  153. if (
  154. following === tab ||
  155. following === lineFeed ||
  156. following === formFeed ||
  157. following === space ||
  158. following === ampersand ||
  159. following === lessThan ||
  160. following !== following ||
  161. (additional && following === additional)
  162. ) {
  163. // Not a character reference.
  164. // No characters are consumed, and nothing is returned.
  165. // This is not an error, either.
  166. queue += fromCharCode(character)
  167. column++
  168. continue
  169. }
  170. start = index + 1
  171. begin = start
  172. end = start
  173. if (following === numberSign) {
  174. // Numerical entity.
  175. end = ++begin
  176. // The behaviour further depends on the next character.
  177. following = value.charCodeAt(end)
  178. if (following === uppercaseX || following === lowercaseX) {
  179. // ASCII hex digits.
  180. type = hexa
  181. end = ++begin
  182. } else {
  183. // ASCII digits.
  184. type = deci
  185. }
  186. } else {
  187. // Named entity.
  188. type = name
  189. }
  190. entityCharacters = ''
  191. entity = ''
  192. characters = ''
  193. test = tests[type]
  194. end--
  195. while (++end < length) {
  196. following = value.charCodeAt(end)
  197. if (!test(following)) {
  198. break
  199. }
  200. characters += fromCharCode(following)
  201. // Check if we can match a legacy named reference.
  202. // If so, we cache that as the last viable named reference.
  203. // This ensures we do not need to walk backwards later.
  204. if (type === name && own.call(legacy, characters)) {
  205. entityCharacters = characters
  206. entity = legacy[characters]
  207. }
  208. }
  209. terminated = value.charCodeAt(end) === semicolon
  210. if (terminated) {
  211. end++
  212. namedEntity = type === name ? decodeEntity(characters) : false
  213. if (namedEntity) {
  214. entityCharacters = characters
  215. entity = namedEntity
  216. }
  217. }
  218. diff = 1 + end - start
  219. if (!terminated && !nonTerminated) {
  220. // Empty.
  221. } else if (!characters) {
  222. // An empty (possible) entity is valid, unless it’s numeric (thus an
  223. // ampersand followed by an octothorp).
  224. if (type !== name) {
  225. warning(numericEmpty, diff)
  226. }
  227. } else if (type === name) {
  228. // An ampersand followed by anything unknown, and not terminated, is
  229. // invalid.
  230. if (terminated && !entity) {
  231. warning(namedUnknown, 1)
  232. } else {
  233. // If theres something after an entity name which is not known, cap
  234. // the reference.
  235. if (entityCharacters !== characters) {
  236. end = begin + entityCharacters.length
  237. diff = 1 + end - begin
  238. terminated = false
  239. }
  240. // If the reference is not terminated, warn.
  241. if (!terminated) {
  242. reason = entityCharacters ? namedNotTerminated : namedEmpty
  243. if (settings.attribute) {
  244. following = value.charCodeAt(end)
  245. if (following === equalsTo) {
  246. warning(reason, diff)
  247. entity = null
  248. } else if (alphanumerical(following)) {
  249. entity = null
  250. } else {
  251. warning(reason, diff)
  252. }
  253. } else {
  254. warning(reason, diff)
  255. }
  256. }
  257. }
  258. reference = entity
  259. } else {
  260. if (!terminated) {
  261. // All non-terminated numeric entities are not rendered, and trigger a
  262. // warning.
  263. warning(numericNotTerminated, diff)
  264. }
  265. // When terminated and number, parse as either hexadecimal or decimal.
  266. reference = parseInt(characters, bases[type])
  267. // Trigger a warning when the parsed number is prohibited, and replace
  268. // with replacement character.
  269. if (prohibited(reference)) {
  270. warning(numericProhibited, diff)
  271. reference = fromCharCode(replacementCharacter)
  272. } else if (reference in invalid) {
  273. // Trigger a warning when the parsed number is disallowed, and replace
  274. // by an alternative.
  275. warning(numericDisallowed, diff)
  276. reference = invalid[reference]
  277. } else {
  278. // Parse the number.
  279. output = ''
  280. // Trigger a warning when the parsed number should not be used.
  281. if (disallowed(reference)) {
  282. warning(numericDisallowed, diff)
  283. }
  284. // Stringify the number.
  285. if (reference > 0xffff) {
  286. reference -= 0x10000
  287. output += fromCharCode((reference >>> (10 & 0x3ff)) | 0xd800)
  288. reference = 0xdc00 | (reference & 0x3ff)
  289. }
  290. reference = output + fromCharCode(reference)
  291. }
  292. }
  293. // Found it!
  294. // First eat the queued characters as normal text, then eat an entity.
  295. if (reference) {
  296. flush()
  297. prev = now()
  298. index = end - 1
  299. column += end - start + 1
  300. result.push(reference)
  301. next = now()
  302. next.offset++
  303. if (handleReference) {
  304. handleReference.call(
  305. referenceContext,
  306. reference,
  307. {start: prev, end: next},
  308. value.slice(start - 1, end)
  309. )
  310. }
  311. prev = next
  312. } else {
  313. // If we could not find a reference, queue the checked characters (as
  314. // normal characters), and move the pointer to their end.
  315. // This is possible because we can be certain neither newlines nor
  316. // ampersands are included.
  317. characters = value.slice(start - 1, end)
  318. queue += characters
  319. column += characters.length
  320. index = end - 1
  321. }
  322. } else {
  323. // Handle anything other than an ampersand, including newlines and EOF.
  324. if (
  325. character === 10 // Line feed
  326. ) {
  327. line++
  328. lines++
  329. column = 0
  330. }
  331. if (character === character) {
  332. queue += fromCharCode(character)
  333. column++
  334. } else {
  335. flush()
  336. }
  337. }
  338. }
  339. // Return the reduced nodes.
  340. return result.join('')
  341. // Get current position.
  342. function now() {
  343. return {
  344. line: line,
  345. column: column,
  346. offset: index + (pos.offset || 0)
  347. }
  348. }
  349. // “Throw” a parse-error: a warning.
  350. function parseError(code, offset) {
  351. var position = now()
  352. position.column += offset
  353. position.offset += offset
  354. handleWarning.call(warningContext, messages[code], position, code)
  355. }
  356. // Flush `queue` (normal text).
  357. // Macro invoked before each entity and at the end of `value`.
  358. // Does nothing when `queue` is empty.
  359. function flush() {
  360. if (queue) {
  361. result.push(queue)
  362. if (handleText) {
  363. handleText.call(textContext, queue, {start: prev, end: now()})
  364. }
  365. queue = ''
  366. }
  367. }
  368. }
  369. // Check if `character` is outside the permissible unicode range.
  370. function prohibited(code) {
  371. return (code >= 0xd800 && code <= 0xdfff) || code > 0x10ffff
  372. }
  373. // Check if `character` is disallowed.
  374. function disallowed(code) {
  375. return (
  376. (code >= 0x0001 && code <= 0x0008) ||
  377. code === 0x000b ||
  378. (code >= 0x000d && code <= 0x001f) ||
  379. (code >= 0x007f && code <= 0x009f) ||
  380. (code >= 0xfdd0 && code <= 0xfdef) ||
  381. (code & 0xffff) === 0xffff ||
  382. (code & 0xffff) === 0xfffe
  383. )
  384. }