json-worker.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. // 创建一个处理BigInt的JSON解析器
  2. const JSONBigInt = {
  3. // 自定义的parse方法,处理大数字
  4. parse: function(text) {
  5. // 先尝试预处理字符串,将可能的大整数标记出来
  6. // 以更精确的方式匹配JSON中的大整数
  7. const preparedText = this._markBigInts(text);
  8. try {
  9. // 使用标准JSON解析,同时使用reviver函数还原BigInt
  10. return JSON.parse(preparedText, this._reviver);
  11. } catch (e) {
  12. // 如果处理失败,尝试原始解析方式
  13. console.error('BigInt处理失败,回退到标准解析', e);
  14. return JSON.parse(text);
  15. }
  16. },
  17. // 将JSON字符串中的大整数标记为特殊格式
  18. _markBigInts: function(text) {
  19. // 这个正则匹配JSON中的数字,但需要避免匹配到引号内的字符串
  20. // 匹配模式: 找到数字前面是冒号或左方括号的情况(表示这是个值而不是键名)
  21. // 允许数字后面有可选的空白字符
  22. return text.replace(
  23. /([:,\[]\s*)(-?\d{16,})(\s*)(?=[,\]\}])/g,
  24. function(match, prefix, number, spaces, offset) {
  25. // 检查这个位置是否在字符串内
  26. let inStr = false;
  27. let esc = false;
  28. for (let i = 0; i < offset; i++) {
  29. if (esc) {
  30. esc = false;
  31. continue;
  32. }
  33. if (text[i] === '\\') {
  34. esc = true;
  35. continue;
  36. }
  37. if (text[i] === '"') {
  38. inStr = !inStr;
  39. }
  40. }
  41. // 如果在字符串内,不替换
  42. if (inStr) {
  43. return match;
  44. }
  45. // 将大数字转换为特殊格式的字符串
  46. return prefix + '"__BigInt__' + number + '"' + spaces;
  47. }
  48. );
  49. },
  50. // 恢复函数,将标记的BigInt字符串转回BigInt类型
  51. _reviver: function(key, value) {
  52. // 检查是否是我们标记的BigInt字符串
  53. if (typeof value === 'string' && value.startsWith('__BigInt__')) {
  54. // 提取数字部分
  55. const numStr = value.substring(10);
  56. try {
  57. // 使用全局的 BigNumber 构造函数(来自 json-bigint.js)
  58. // 如果可用,优先使用 BigNumber,否则回退到原生 BigInt
  59. if (typeof BigNumber !== 'undefined') {
  60. return new BigNumber(numStr);
  61. } else {
  62. return BigInt(numStr);
  63. }
  64. } catch (e) {
  65. // 如果转换失败,保留原始字符串
  66. console.warn('无法转换为BigInt:', numStr);
  67. return numStr;
  68. }
  69. }
  70. return value;
  71. }
  72. };
  73. // 转义功能开启标记
  74. let escapeJsonStringEnabled = false;
  75. // 处理主线程消息
  76. self.onmessage = function(event) {
  77. // 格式化JSON
  78. if (event.data.jsonString) {
  79. // 接收转义功能标志
  80. if (event.data.escapeJsonString !== undefined) {
  81. escapeJsonStringEnabled = event.data.escapeJsonString;
  82. }
  83. // 发送格式化中的消息
  84. self.postMessage(['FORMATTING']);
  85. try {
  86. // 先预处理JSON字符串,防止大整数丢失精度
  87. let jsonObj;
  88. try {
  89. // 尝试使用自定义的BigInt解析器
  90. jsonObj = JSONBigInt.parse(event.data.jsonString);
  91. } catch (e) {
  92. // 如果解析失败,回退到标准解析
  93. console.error('BigInt解析失败,回退到标准解析', e);
  94. jsonObj = JSON.parse(event.data.jsonString);
  95. }
  96. // 如果是简单主题,直接返回格式化的JSON
  97. if (event.data.skin && event.data.skin === 'theme-simple') {
  98. // 处理BigInt特殊情况
  99. let formatted = JSON.stringify(jsonObj, function(key, value) {
  100. if (typeof value === 'bigint') {
  101. // 移除n后缀,只显示数字本身
  102. return value.toString();
  103. }
  104. if (isBigNumberLike(value)) {
  105. return getBigNumberDisplayString(value);
  106. }
  107. // 处理普通数字,避免科学计数法
  108. if (typeof value === 'number' && value.toString().includes('e')) {
  109. // 大数字转为字符串以避免科学计数法
  110. return value.toLocaleString('fullwide', {useGrouping: false});
  111. }
  112. return value;
  113. }, 4);
  114. let html = '<div id="formattedJson"><pre class="rootItem">' +
  115. formatted.replace(/&/g, '&amp;')
  116. .replace(/</g, '&lt;')
  117. .replace(/>/g, '&gt;')
  118. .replace(/"/g, '&quot;')
  119. .replace(/'/g, '&#039;') +
  120. '</pre></div>';
  121. self.postMessage(['FORMATTED', html]);
  122. return;
  123. }
  124. // 默认主题 - 创建更丰富的HTML结构
  125. let html = '<div id="formattedJson">' +
  126. formatJsonToHtml(jsonObj) +
  127. '</div>';
  128. self.postMessage(['FORMATTED', html]);
  129. } catch (e) {
  130. // 处理错误情况
  131. self.postMessage(['FORMATTED', '<div id="formattedJson"><div class="error">格式化失败: ' + e.message + '</div></div>']);
  132. }
  133. }
  134. };
  135. // HTML特殊字符格式化
  136. function htmlspecialchars(str) {
  137. str = str.replace(/&/g, '&amp;');
  138. str = str.replace(/</g, '&lt;');
  139. str = str.replace(/>/g, '&gt;');
  140. str = str.replace(/"/g, '&quot;');
  141. str = str.replace(/'/g, '&#039;');
  142. return str;
  143. }
  144. // 格式化字符串值,如果是URL则转换为链接
  145. function formatStringValue(str) {
  146. // URL正则表达式,匹配 http/https/ftp 协议的URL
  147. const urlRegex = /^(https?:\/\/|ftp:\/\/)[^\s<>"'\\]+$/i;
  148. if (urlRegex.test(str)) {
  149. // 如果是URL,转换为链接
  150. const escapedUrl = htmlspecialchars(str);
  151. return '<a href="' + escapedUrl + '" target="_blank" rel="noopener noreferrer" data-is-link="1" data-link-url="' + escapedUrl + '">' + htmlspecialchars(str) + '</a>';
  152. } else {
  153. // 直接显示解析后的字符串内容,不需要重新转义
  154. // 这样可以保持用户原始输入的意图
  155. return htmlspecialchars(str);
  156. }
  157. }
  158. // 格式化JSON为HTML
  159. function formatJsonToHtml(json) {
  160. return createNode(json).getHTML();
  161. }
  162. // 创建节点
  163. function createNode(value) {
  164. let node = {
  165. type: getType(value),
  166. value: value,
  167. children: [],
  168. getHTML: function() {
  169. switch(this.type) {
  170. case 'string':
  171. // 判断原始字符串是否为URL
  172. if (isUrl(this.value)) {
  173. // 用JSON.stringify保证转义符显示,内容包裹在<a>里
  174. return '<div class="item item-line"><span class="string"><a href="'
  175. + htmlspecialchars(this.value) + '" target="_blank" rel="noopener noreferrer" data-is-link="1" data-link-url="' + htmlspecialchars(this.value) + '">'
  176. + htmlspecialchars(JSON.stringify(this.value)) + '</a></span></div>';
  177. } else {
  178. // 检测字符串是否是有效的JSON(用于转义功能)
  179. // 当转义功能开启时,如果字符串是有效的JSON,就格式化显示
  180. if (escapeJsonStringEnabled) {
  181. const strValue = String(this.value);
  182. // 检查字符串是否看起来像JSON(以[或{开头,以]或}结尾)
  183. const trimmed = strValue.trim();
  184. if ((trimmed.startsWith('[') && trimmed.endsWith(']')) ||
  185. (trimmed.startsWith('{') && trimmed.endsWith('}'))) {
  186. try {
  187. // 尝试解析为JSON,使用全局的 JSON.parse(已被 json-bigint.js 覆盖)
  188. const parsed = JSON.parse(strValue);
  189. // 如果解析成功且是对象或数组,格式化显示
  190. if (typeof parsed === 'object' && parsed !== null) {
  191. const nestedNode = createNode(parsed);
  192. // 获取嵌套JSON的完整HTML(完全展开)
  193. let nestedHTML = nestedNode.getHTML();
  194. // 移除外层的item容器div,只保留内部内容
  195. nestedHTML = nestedHTML.replace(/^<div class="item[^"]*">/, '').replace(/<\/div>$/, '');
  196. // 返回格式化的JSON结构,但保持在外层的字符串容器中
  197. // 使用block显示,确保完全展开
  198. return '<div class="item item-line"><span class="string">' +
  199. '<span class="quote">"</span>' +
  200. '<div class="string-json-nested" style="display:block;margin-left:0;padding-left:0;">' +
  201. nestedHTML +
  202. '</div>' +
  203. '<span class="quote">"</span>' +
  204. '</span></div>';
  205. }
  206. } catch (e) {
  207. // 解析失败,按普通字符串处理
  208. }
  209. }
  210. }
  211. return '<div class="item item-line"><span class="string">' + formatStringValue(JSON.stringify(this.value)) + '</span></div>';
  212. }
  213. case 'number':
  214. // 确保大数字不使用科学计数法
  215. let numStr = typeof this.value === 'number' && this.value.toString().includes('e')
  216. ? this.value.toLocaleString('fullwide', {useGrouping: false})
  217. : this.value;
  218. return '<div class="item item-line"><span class="number">' +
  219. numStr +
  220. '</span></div>';
  221. case 'bigint':
  222. // 对BigInt类型特殊处理,只显示数字,不添加n后缀
  223. return '<div class="item item-line"><span class="number">' +
  224. getBigNumberDisplayString(this.value) +
  225. '</span></div>';
  226. case 'boolean':
  227. return '<div class="item item-line"><span class="bool">' +
  228. this.value +
  229. '</span></div>';
  230. case 'null':
  231. return '<div class="item item-line"><span class="null">null</span></div>';
  232. case 'object':
  233. return this.getObjectHTML();
  234. case 'array':
  235. return this.getArrayHTML();
  236. default:
  237. return '';
  238. }
  239. },
  240. getObjectHTML: function() {
  241. if (!this.value || Object.keys(this.value).length === 0) {
  242. return '<div class="item item-object"><span class="brace">{</span><span class="brace">}</span></div>';
  243. }
  244. let html = '<div class="item item-object">' +
  245. '<span class="expand"></span>' +
  246. '<span class="brace">{</span>' +
  247. '<span class="ellipsis"></span>' +
  248. '<div class="kv-list">';
  249. let keys = Object.keys(this.value);
  250. keys.forEach((key, index) => {
  251. let prop = this.value[key];
  252. let childNode = createNode(prop);
  253. // 判断子节点是否为对象或数组,决定是否加item-block
  254. let itemClass = (childNode.type === 'object' || childNode.type === 'array') ? 'item item-block' : 'item';
  255. html += '<div class="' + itemClass + '">';
  256. // 如果值是对象或数组,在key前面添加展开按钮
  257. if (childNode.type === 'object' || childNode.type === 'array') {
  258. html += '<span class="expand"></span>';
  259. }
  260. html += '<span class="quote">"</span>' +
  261. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  262. '<span class="quote">"</span>' +
  263. '<span class="colon">: </span>';
  264. // 添加值
  265. if (childNode.type === 'object' || childNode.type === 'array') {
  266. html += childNode.getInlineHTMLWithoutExpand();
  267. } else {
  268. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  269. }
  270. // 如果不是最后一个属性,添加逗号
  271. if (index < keys.length - 1) {
  272. html += '<span class="comma">,</span>';
  273. }
  274. html += '</div>';
  275. });
  276. html += '</div><span class="brace">}</span></div>';
  277. return html;
  278. },
  279. getArrayHTML: function() {
  280. if (!this.value || this.value.length === 0) {
  281. return '<div class="item item-array"><span class="brace">[</span><span class="brace">]</span></div>';
  282. }
  283. let html = '<div class="item item-array">' +
  284. '<span class="expand"></span>' +
  285. '<span class="brace">[</span>' +
  286. '<span class="ellipsis"></span>' +
  287. '<div class="kv-list item-array-container">';
  288. this.value.forEach((item, index) => {
  289. let childNode = createNode(item);
  290. html += '<div class="item item-block item-array-element" data-array-index="' + index + '">';
  291. // 如果数组元素是对象或数组,在前面添加展开按钮
  292. if (childNode.type === 'object' || childNode.type === 'array') {
  293. html += '<span class="expand"></span>';
  294. html += childNode.getInlineHTMLWithoutExpand();
  295. } else {
  296. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  297. }
  298. // 如果不是最后一个元素,添加逗号
  299. if (index < this.value.length - 1) {
  300. html += '<span class="comma">,</span>';
  301. }
  302. html += '</div>';
  303. });
  304. html += '</div><span class="brace">]</span></div>';
  305. return html;
  306. },
  307. // 新增内联HTML方法,用于在同一行显示开始大括号/方括号
  308. getInlineHTML: function() {
  309. switch(this.type) {
  310. case 'object':
  311. return this.getInlineObjectHTML();
  312. case 'array':
  313. return this.getInlineArrayHTML();
  314. default:
  315. return this.getHTML();
  316. }
  317. },
  318. // 新增不包含展开按钮的内联HTML方法
  319. getInlineHTMLWithoutExpand: function() {
  320. switch(this.type) {
  321. case 'object':
  322. return this.getInlineObjectHTMLWithoutExpand();
  323. case 'array':
  324. return this.getInlineArrayHTMLWithoutExpand();
  325. default:
  326. return this.getHTML();
  327. }
  328. },
  329. getInlineObjectHTML: function() {
  330. if (!this.value || Object.keys(this.value).length === 0) {
  331. return '<span class="brace">{</span><span class="brace">}</span>';
  332. }
  333. let html = '<span class="brace">{</span>' +
  334. '<span class="expand"></span>' +
  335. '<span class="ellipsis"></span>' +
  336. '<div class="kv-list">';
  337. let keys = Object.keys(this.value);
  338. keys.forEach((key, index) => {
  339. let prop = this.value[key];
  340. let childNode = createNode(prop);
  341. // 判断子节点是否为对象或数组,决定是否加item-block
  342. let itemClass = (childNode.type === 'object' || childNode.type === 'array') ? 'item item-block' : 'item';
  343. html += '<div class="' + itemClass + '">';
  344. if (childNode.type === 'object' || childNode.type === 'array') {
  345. html += '<span class="expand"></span>';
  346. }
  347. html += '<span class="quote">"</span>' +
  348. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  349. '<span class="quote">"</span>' +
  350. '<span class="colon">: </span>';
  351. if (childNode.type === 'object' || childNode.type === 'array') {
  352. html += childNode.getInlineHTMLWithoutExpand();
  353. } else {
  354. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  355. }
  356. if (index < keys.length - 1) {
  357. html += '<span class="comma">,</span>';
  358. }
  359. html += '</div>';
  360. });
  361. html += '</div><span class="brace">}</span>';
  362. return html;
  363. },
  364. getInlineArrayHTML: function() {
  365. if (!this.value || this.value.length === 0) {
  366. return '<span class="brace">[</span><span class="brace">]</span>';
  367. }
  368. let html = '<span class="brace">[</span>' +
  369. '<span class="expand"></span>' +
  370. '<span class="ellipsis"></span>' +
  371. '<div class="kv-list item-array-container">';
  372. this.value.forEach((item, index) => {
  373. let childNode = createNode(item);
  374. html += '<div class="item item-block item-array-element" data-array-index="' + index + '">';
  375. // 如果数组元素是对象或数组,在前面添加展开按钮
  376. if (childNode.type === 'object' || childNode.type === 'array') {
  377. html += '<span class="expand"></span>';
  378. html += childNode.getInlineHTMLWithoutExpand();
  379. } else {
  380. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  381. }
  382. // 如果不是最后一个元素,添加逗号
  383. if (index < this.value.length - 1) {
  384. html += '<span class="comma">,</span>';
  385. }
  386. html += '</div>';
  387. });
  388. html += '</div><span class="brace">]</span>';
  389. return html;
  390. },
  391. getInlineObjectHTMLWithoutExpand: function() {
  392. if (!this.value || Object.keys(this.value).length === 0) {
  393. return '<span class="brace">{</span><span class="brace">}</span>';
  394. }
  395. let html = '<span class="brace">{</span>' +
  396. '<span class="ellipsis"></span>' +
  397. '<div class="kv-list">';
  398. let keys = Object.keys(this.value);
  399. keys.forEach((key, index) => {
  400. let prop = this.value[key];
  401. let childNode = createNode(prop);
  402. // 判断子节点是否为对象或数组,决定是否加item-block
  403. let itemClass = (childNode.type === 'object' || childNode.type === 'array') ? 'item item-block' : 'item';
  404. html += '<div class="' + itemClass + '">';
  405. if (childNode.type === 'object' || childNode.type === 'array') {
  406. html += '<span class="expand"></span>';
  407. }
  408. html += '<span class="quote">"</span>' +
  409. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  410. '<span class="quote">"</span>' +
  411. '<span class="colon">: </span>';
  412. if (childNode.type === 'object' || childNode.type === 'array') {
  413. html += childNode.getInlineHTMLWithoutExpand();
  414. } else {
  415. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  416. }
  417. if (index < keys.length - 1) {
  418. html += '<span class="comma">,</span>';
  419. }
  420. html += '</div>';
  421. });
  422. html += '</div><span class="brace">}</span>';
  423. return html;
  424. },
  425. getInlineArrayHTMLWithoutExpand: function() {
  426. if (!this.value || this.value.length === 0) {
  427. return '<span class="brace">[</span><span class="brace">]</span>';
  428. }
  429. let html = '<span class="brace">[</span>' +
  430. '<span class="ellipsis"></span>' +
  431. '<div class="kv-list item-array-container">';
  432. this.value.forEach((item, index) => {
  433. let childNode = createNode(item);
  434. html += '<div class="item item-block item-array-element" data-array-index="' + index + '">';
  435. // 确保所有类型的数组元素都能正确处理
  436. if (childNode.type === 'object' || childNode.type === 'array') {
  437. html += '<span class="expand"></span>';
  438. html += childNode.getInlineHTMLWithoutExpand();
  439. } else {
  440. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  441. }
  442. // 如果不是最后一个元素,添加逗号
  443. if (index < this.value.length - 1) {
  444. html += '<span class="comma">,</span>';
  445. }
  446. html += '</div>';
  447. });
  448. html += '</div><span class="brace">]</span>';
  449. return html;
  450. }
  451. };
  452. return node;
  453. }
  454. // 获取值类型
  455. function getType(value) {
  456. if (value === null) return 'null';
  457. if (value === undefined) return 'undefined';
  458. let type = typeof value;
  459. // 特别处理BigInt类型
  460. if (type === 'bigint') return 'bigint';
  461. if (type === 'object') {
  462. if (isBigNumberLike(value)) {
  463. return 'bigint'; // 将 BigNumber 对象也当作 bigint 处理
  464. }
  465. if (Array.isArray(value)) return 'array';
  466. }
  467. return type;
  468. }
  469. function isUrl(str) {
  470. const urlRegex = /^(https?:\/\/|ftp:\/\/)[^\s<>"'\\]+$/i;
  471. return urlRegex.test(str);
  472. }
  473. function isBigNumberLike(value) {
  474. return value && typeof value === 'object' &&
  475. typeof value.s === 'number' &&
  476. typeof value.e === 'number' &&
  477. Array.isArray(value.c);
  478. }
  479. function getBigNumberDisplayString(value) {
  480. if (typeof value === 'bigint') {
  481. return value.toString();
  482. }
  483. if (!isBigNumberLike(value)) {
  484. return String(value);
  485. }
  486. const direct = tryConvertBigNumberToString(value);
  487. if (direct) {
  488. return direct;
  489. }
  490. return rebuildBigNumberFromParts(value);
  491. }
  492. function tryConvertBigNumberToString(value) {
  493. const nativeToString = value && value.toString;
  494. if (typeof nativeToString === 'function' && nativeToString !== Object.prototype.toString) {
  495. try {
  496. const result = nativeToString.call(value);
  497. if (typeof result === 'string' && result !== '[object Object]') {
  498. return result;
  499. }
  500. } catch (e) {}
  501. }
  502. const ctor = getAvailableBigNumberCtor();
  503. if (ctor && typeof Object.setPrototypeOf === 'function') {
  504. try {
  505. if (!(value instanceof ctor)) {
  506. Object.setPrototypeOf(value, ctor.prototype);
  507. }
  508. if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
  509. const result = value.toString();
  510. if (typeof result === 'string' && result !== '[object Object]') {
  511. return result;
  512. }
  513. }
  514. } catch (e) {}
  515. }
  516. return null;
  517. }
  518. function rebuildBigNumberFromParts(value) {
  519. const sign = value.s < 0 ? '-' : '';
  520. const CHUNK_SIZE = 14;
  521. let digits = '';
  522. for (let i = 0; i < value.c.length; i++) {
  523. let chunkStr = Math.abs(value.c[i]).toString();
  524. if (i > 0) {
  525. chunkStr = chunkStr.padStart(CHUNK_SIZE, '0');
  526. }
  527. digits += chunkStr;
  528. }
  529. digits = digits.replace(/^0+/, '') || '0';
  530. const decimalIndex = value.e + 1;
  531. if (decimalIndex <= 0) {
  532. const zeros = '0'.repeat(Math.abs(decimalIndex));
  533. let fraction = zeros + digits;
  534. fraction = fraction.replace(/0+$/, '');
  535. if (!fraction) {
  536. return sign + '0';
  537. }
  538. return sign + '0.' + fraction;
  539. }
  540. if (decimalIndex >= digits.length) {
  541. return sign + digits + '0'.repeat(decimalIndex - digits.length);
  542. }
  543. const intPart = digits.slice(0, decimalIndex);
  544. let fracPart = digits.slice(decimalIndex).replace(/0+$/, '');
  545. if (!fracPart) {
  546. return sign + intPart;
  547. }
  548. return sign + intPart + '.' + fracPart;
  549. }
  550. function getAvailableBigNumberCtor() {
  551. if (typeof JSON !== 'undefined' && typeof JSON.BigNumber === 'function') {
  552. return JSON.BigNumber;
  553. }
  554. if (typeof BigNumber === 'function') {
  555. return BigNumber;
  556. }
  557. return null;
  558. }