仲裁视频会议H5

index.js 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. 'use strict';
  2. const valueParser = require('postcss-value-parser');
  3. /*
  4. * Constants (parser usage)
  5. */
  6. const SINGLE_QUOTE = "'".charCodeAt(0);
  7. const DOUBLE_QUOTE = '"'.charCodeAt(0);
  8. const BACKSLASH = '\\'.charCodeAt(0);
  9. const NEWLINE = '\n'.charCodeAt(0);
  10. const SPACE = ' '.charCodeAt(0);
  11. const FEED = '\f'.charCodeAt(0);
  12. const TAB = '\t'.charCodeAt(0);
  13. const CR = '\r'.charCodeAt(0);
  14. const WORD_END = /[ \n\t\r\f'"\\]/g;
  15. /*
  16. * Constants (node type strings)
  17. */
  18. const C_STRING = 'string';
  19. const C_ESCAPED_SINGLE_QUOTE = 'escapedSingleQuote';
  20. const C_ESCAPED_DOUBLE_QUOTE = 'escapedDoubleQuote';
  21. const C_SINGLE_QUOTE = 'singleQuote';
  22. const C_DOUBLE_QUOTE = 'doubleQuote';
  23. const C_NEWLINE = 'newline';
  24. const C_SINGLE = 'single';
  25. /*
  26. * Literals
  27. */
  28. const L_SINGLE_QUOTE = `'`;
  29. const L_DOUBLE_QUOTE = `"`;
  30. const L_NEWLINE = `\\\n`;
  31. /*
  32. * Parser nodes
  33. */
  34. const T_ESCAPED_SINGLE_QUOTE = { type: C_ESCAPED_SINGLE_QUOTE, value: `\\'` };
  35. const T_ESCAPED_DOUBLE_QUOTE = { type: C_ESCAPED_DOUBLE_QUOTE, value: `\\"` };
  36. const T_SINGLE_QUOTE = { type: C_SINGLE_QUOTE, value: L_SINGLE_QUOTE };
  37. const T_DOUBLE_QUOTE = { type: C_DOUBLE_QUOTE, value: L_DOUBLE_QUOTE };
  38. const T_NEWLINE = { type: C_NEWLINE, value: L_NEWLINE };
  39. /** @typedef {T_ESCAPED_SINGLE_QUOTE | T_ESCAPED_DOUBLE_QUOTE | T_SINGLE_QUOTE | T_NEWLINE} StringAstNode */
  40. /**
  41. * @typedef {{nodes: StringAstNode[],
  42. * types: {escapedSingleQuote: number, escapedDoubleQuote: number, singleQuote: number, doubleQuote: number},
  43. * quotes: boolean}} StringAst
  44. */
  45. /**
  46. * @param {StringAst} ast
  47. * @return {string}
  48. */
  49. function stringify(ast) {
  50. return ast.nodes.reduce((str, { value }) => {
  51. // Collapse multiple line strings automatically
  52. if (value === L_NEWLINE) {
  53. return str;
  54. }
  55. return str + value;
  56. }, '');
  57. }
  58. /**
  59. * @param {string} str
  60. * @return {StringAst}
  61. */
  62. function parse(str) {
  63. let code, next, value;
  64. let pos = 0;
  65. let len = str.length;
  66. /** @type StringAst */
  67. const ast = {
  68. nodes: [],
  69. types: {
  70. escapedSingleQuote: 0,
  71. escapedDoubleQuote: 0,
  72. singleQuote: 0,
  73. doubleQuote: 0,
  74. },
  75. quotes: false,
  76. };
  77. while (pos < len) {
  78. code = str.charCodeAt(pos);
  79. switch (code) {
  80. case SPACE:
  81. case TAB:
  82. case CR:
  83. case FEED:
  84. next = pos;
  85. do {
  86. next += 1;
  87. code = str.charCodeAt(next);
  88. } while (
  89. code === SPACE ||
  90. code === NEWLINE ||
  91. code === TAB ||
  92. code === CR ||
  93. code === FEED
  94. );
  95. ast.nodes.push({
  96. type: 'space',
  97. value: str.slice(pos, next),
  98. });
  99. pos = next - 1;
  100. break;
  101. case SINGLE_QUOTE:
  102. ast.nodes.push(T_SINGLE_QUOTE);
  103. ast.types[C_SINGLE_QUOTE]++;
  104. ast.quotes = true;
  105. break;
  106. case DOUBLE_QUOTE:
  107. ast.nodes.push(T_DOUBLE_QUOTE);
  108. ast.types[C_DOUBLE_QUOTE]++;
  109. ast.quotes = true;
  110. break;
  111. case BACKSLASH:
  112. next = pos + 1;
  113. if (str.charCodeAt(next) === SINGLE_QUOTE) {
  114. ast.nodes.push(T_ESCAPED_SINGLE_QUOTE);
  115. ast.types[C_ESCAPED_SINGLE_QUOTE]++;
  116. ast.quotes = true;
  117. pos = next;
  118. break;
  119. } else if (str.charCodeAt(next) === DOUBLE_QUOTE) {
  120. ast.nodes.push(T_ESCAPED_DOUBLE_QUOTE);
  121. ast.types[C_ESCAPED_DOUBLE_QUOTE]++;
  122. ast.quotes = true;
  123. pos = next;
  124. break;
  125. } else if (str.charCodeAt(next) === NEWLINE) {
  126. ast.nodes.push(T_NEWLINE);
  127. pos = next;
  128. break;
  129. }
  130. /*
  131. * We need to fall through here to handle the token as
  132. * a whole word. The missing 'break' is intentional.
  133. */
  134. default:
  135. WORD_END.lastIndex = pos + 1;
  136. WORD_END.test(str);
  137. if (WORD_END.lastIndex === 0) {
  138. next = len - 1;
  139. } else {
  140. next = WORD_END.lastIndex - 2;
  141. }
  142. value = str.slice(pos, next + 1);
  143. ast.nodes.push({
  144. type: C_STRING,
  145. value,
  146. });
  147. pos = next;
  148. }
  149. pos++;
  150. }
  151. return ast;
  152. }
  153. /**
  154. * @param {valueParser.StringNode} node
  155. * @param {StringAst} ast
  156. * @return {void}
  157. */
  158. function changeWrappingQuotes(node, ast) {
  159. const { types } = ast;
  160. if (types[C_SINGLE_QUOTE] || types[C_DOUBLE_QUOTE]) {
  161. return;
  162. }
  163. if (
  164. node.quote === L_SINGLE_QUOTE &&
  165. types[C_ESCAPED_SINGLE_QUOTE] > 0 &&
  166. !types[C_ESCAPED_DOUBLE_QUOTE]
  167. ) {
  168. node.quote = L_DOUBLE_QUOTE;
  169. }
  170. if (
  171. node.quote === L_DOUBLE_QUOTE &&
  172. types[C_ESCAPED_DOUBLE_QUOTE] > 0 &&
  173. !types[C_ESCAPED_SINGLE_QUOTE]
  174. ) {
  175. node.quote = L_SINGLE_QUOTE;
  176. }
  177. ast.nodes = changeChildQuotes(ast.nodes, node.quote);
  178. }
  179. /**
  180. * @param {StringAstNode[]} childNodes
  181. * @param {string} parentQuote
  182. * @return {StringAstNode[]}
  183. */
  184. function changeChildQuotes(childNodes, parentQuote) {
  185. const updatedChildren = [];
  186. for (const child of childNodes) {
  187. if (
  188. child.type === C_ESCAPED_DOUBLE_QUOTE &&
  189. parentQuote === L_SINGLE_QUOTE
  190. ) {
  191. updatedChildren.push(T_DOUBLE_QUOTE);
  192. } else if (
  193. child.type === C_ESCAPED_SINGLE_QUOTE &&
  194. parentQuote === L_DOUBLE_QUOTE
  195. ) {
  196. updatedChildren.push(T_SINGLE_QUOTE);
  197. } else {
  198. updatedChildren.push(child);
  199. }
  200. }
  201. return updatedChildren;
  202. }
  203. /**
  204. * @param {string} value
  205. * @param {'single' | 'double'} preferredQuote
  206. * @return {string}
  207. */
  208. function normalize(value, preferredQuote) {
  209. if (!value || !value.length) {
  210. return value;
  211. }
  212. return valueParser(value)
  213. .walk((child) => {
  214. if (child.type !== C_STRING) {
  215. return;
  216. }
  217. const ast = parse(child.value);
  218. if (ast.quotes) {
  219. changeWrappingQuotes(child, ast);
  220. } else if (preferredQuote === C_SINGLE) {
  221. child.quote = L_SINGLE_QUOTE;
  222. } else {
  223. child.quote = L_DOUBLE_QUOTE;
  224. }
  225. child.value = stringify(ast);
  226. })
  227. .toString();
  228. }
  229. /**
  230. * @param {string} original
  231. * @param {Map<string, string>} cache
  232. * @param {'single' | 'double'} preferredQuote
  233. * @return {string}
  234. */
  235. function minify(original, cache, preferredQuote) {
  236. const key = original + '|' + preferredQuote;
  237. if (cache.has(key)) {
  238. return /** @type {string} */ (cache.get(key));
  239. }
  240. const newValue = normalize(original, preferredQuote);
  241. cache.set(key, newValue);
  242. return newValue;
  243. }
  244. /** @typedef {{preferredQuote?: 'double' | 'single'}} Options */
  245. /**
  246. * @type {import('postcss').PluginCreator<Options>}
  247. * @param {Options} opts
  248. * @return {import('postcss').Plugin}
  249. */
  250. function pluginCreator(opts) {
  251. const { preferredQuote } = Object.assign(
  252. {},
  253. {
  254. preferredQuote: 'double',
  255. },
  256. opts
  257. );
  258. return {
  259. postcssPlugin: 'postcss-normalize-string',
  260. OnceExit(css) {
  261. const cache = new Map();
  262. css.walk((node) => {
  263. switch (node.type) {
  264. case 'rule':
  265. node.selector = minify(node.selector, cache, preferredQuote);
  266. break;
  267. case 'decl':
  268. node.value = minify(node.value, cache, preferredQuote);
  269. break;
  270. case 'atrule':
  271. node.params = minify(node.params, cache, preferredQuote);
  272. break;
  273. }
  274. });
  275. },
  276. };
  277. }
  278. pluginCreator.postcss = true;
  279. module.exports = pluginCreator;