logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/

iss_deserializer.js (5120B)


  1. import { flattenDeep } from 'lodash'
  2. export const deserializeShadow = string => {
  3. const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha', 'name']
  4. const regexPrep = [
  5. // inset keyword (optional)
  6. '^',
  7. '(?:(inset)\\s+)?',
  8. // x
  9. '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)',
  10. // y
  11. '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)',
  12. // blur (optional)
  13. '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
  14. // spread (optional)
  15. '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
  16. // either hex, variable or function
  17. '(#[0-9a-f]{6}|--[a-z0-9\\-_]+|\\$[a-z0-9\\-()_ ]+)',
  18. // opacity (optional)
  19. '(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
  20. // name
  21. '(?:\\s+#(\\w+)\\s*)?',
  22. '$'
  23. ].join('')
  24. const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
  25. const result = regex.exec(string)
  26. if (result == null) {
  27. if (string.startsWith('$') || string.startsWith('--')) {
  28. return string
  29. } else {
  30. throw new Error(`Invalid shadow definition: '${string}'`)
  31. }
  32. } else {
  33. const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
  34. const { x, y, blur, spread, alpha, inset, color, name } = Object.fromEntries(modes.map((mode, i) => {
  35. if (numeric.has(mode)) {
  36. const number = Number(result[i])
  37. if (Number.isNaN(number)) {
  38. if (mode === 'alpha') return [mode, 1]
  39. return [mode, 0]
  40. }
  41. return [mode, number]
  42. } else if (mode === 'inset') {
  43. return [mode, !!result[i]]
  44. } else {
  45. return [mode, result[i]]
  46. }
  47. }).filter(([k, v]) => v !== false).slice(1))
  48. return { x, y, blur, spread, color, alpha, inset, name }
  49. }
  50. }
  51. // this works nearly the same as HTML tree converter
  52. const parseIss = (input) => {
  53. const buffer = [{ selector: null, content: [] }]
  54. let textBuffer = ''
  55. const getCurrentBuffer = () => {
  56. let current = buffer[buffer.length - 1]
  57. if (current == null) {
  58. current = { selector: null, content: [] }
  59. }
  60. return current
  61. }
  62. // Processes current line buffer, adds it to output buffer and clears line buffer
  63. const flushText = (kind) => {
  64. if (textBuffer === '') return
  65. if (kind === 'content') {
  66. getCurrentBuffer().content.push(textBuffer.trim())
  67. } else {
  68. getCurrentBuffer().selector = textBuffer.trim()
  69. }
  70. textBuffer = ''
  71. }
  72. for (let i = 0; i < input.length; i++) {
  73. const char = input[i]
  74. if (char === ';') {
  75. flushText('content')
  76. } else if (char === '{') {
  77. flushText('header')
  78. } else if (char === '}') {
  79. flushText('content')
  80. buffer.push({ selector: null, content: [] })
  81. textBuffer = ''
  82. } else {
  83. textBuffer += char
  84. }
  85. }
  86. return buffer
  87. }
  88. export const deserialize = (input) => {
  89. const ast = parseIss(input)
  90. const finalResult = ast.filter(i => i.selector != null).map(item => {
  91. const { selector, content } = item
  92. let stateCount = 0
  93. const selectors = selector.split(/,/g)
  94. const result = selectors.map(selector => {
  95. const output = { component: '' }
  96. let currentDepth = null
  97. selector.split(/ /g).reverse().forEach((fragment, index, arr) => {
  98. const fragmentObject = { component: '' }
  99. let mode = 'component'
  100. for (let i = 0; i < fragment.length; i++) {
  101. const char = fragment[i]
  102. switch (char) {
  103. case '.': {
  104. mode = 'variant'
  105. fragmentObject.variant = ''
  106. break
  107. }
  108. case ':': {
  109. mode = 'state'
  110. fragmentObject.state = fragmentObject.state || []
  111. stateCount++
  112. break
  113. }
  114. default: {
  115. if (mode === 'state') {
  116. const currentState = fragmentObject.state[stateCount - 1]
  117. if (currentState == null) {
  118. fragmentObject.state.push('')
  119. }
  120. fragmentObject.state[stateCount - 1] += char
  121. } else {
  122. fragmentObject[mode] += char
  123. }
  124. }
  125. }
  126. }
  127. if (currentDepth !== null) {
  128. currentDepth.parent = { ...fragmentObject }
  129. currentDepth = currentDepth.parent
  130. } else {
  131. Object.keys(fragmentObject).forEach(key => {
  132. output[key] = fragmentObject[key]
  133. })
  134. if (index !== (arr.length - 1)) {
  135. output.parent = { component: '' }
  136. }
  137. currentDepth = output
  138. }
  139. })
  140. output.directives = Object.fromEntries(content.map(d => {
  141. const [property, value] = d.split(':')
  142. let realValue = (value || '').trim()
  143. if (property === 'shadow') {
  144. if (realValue === 'none') {
  145. realValue = []
  146. } else {
  147. realValue = value.split(',').map(v => deserializeShadow(v.trim()))
  148. }
  149. } if (!Number.isNaN(Number(value))) {
  150. realValue = Number(value)
  151. }
  152. return [property, realValue]
  153. }))
  154. return output
  155. })
  156. return result
  157. })
  158. return flattenDeep(finalResult)
  159. }