marked/test/unit/Lexer-spec.js

1049 lines
26 KiB
JavaScript
Raw Normal View History

2020-04-02 00:22:40 -05:00
const Lexer = require('../../src/Lexer.js');
function expectTokens({ md, options, tokens = [], links = {} }) {
const lexer = new Lexer(options);
const actual = lexer.lex(md);
const expected = tokens;
expected.links = links;
// console.log(JSON.stringify(actual, null, 2));
expect(actual).toEqual(expected);
}
2020-04-06 23:25:33 -05:00
function expectInlineTokens({ md, options, tokens = jasmine.any(Array), links = {} }) {
2020-04-02 00:22:40 -05:00
const lexer = new Lexer(options);
lexer.tokens.links = links;
const outTokens = [];
2020-04-06 23:25:33 -05:00
lexer.inlineTokens(md, outTokens);
expect(outTokens).toEqual(tokens);
2020-04-02 00:22:40 -05:00
}
2020-04-06 22:28:47 -05:00
function expectInline({ token, options, tokens }) {
2020-04-02 00:22:40 -05:00
const lexer = new Lexer(options);
2020-04-06 22:28:47 -05:00
lexer.inline([token]);
2020-04-02 00:22:40 -05:00
expect(token.tokens).toEqual(tokens);
}
describe('Lexer', () => {
describe('paragraph', () => {
it('space between paragraphs', () => {
expectTokens({
md: 'paragraph 1\n\nparagraph 2',
tokens: [
{
type: 'paragraph',
raw: 'paragraph 1',
text: 'paragraph 1',
tokens: [{ type: 'text', raw: 'paragraph 1', text: 'paragraph 1' }]
},
{ type: 'space', raw: '\n\n' },
{
type: 'paragraph',
raw: 'paragraph 2',
text: 'paragraph 2',
tokens: [{ type: 'text', raw: 'paragraph 2', text: 'paragraph 2' }]
}
]
});
});
});
describe('code', () => {
it('indented code', () => {
expectTokens({
md: ' code',
tokens: [
{ type: 'code', raw: ' code', text: 'code', codeBlockStyle: 'indented' }
]
});
});
it('fenced code', () => {
expectTokens({
md: '```\ncode\n```',
tokens: [
{ type: 'code', raw: '```\ncode\n```', text: 'code', lang: '' }
]
});
});
it('fenced code lang', () => {
expectTokens({
md: '```text\ncode\n```',
tokens: [
{ type: 'code', raw: '```text\ncode\n```', text: 'code', lang: 'text' }
]
});
});
});
describe('headings', () => {
it('depth', () => {
expectTokens({
md: `
# heading 1
## heading 2
### heading 3
#### heading 4
##### heading 5
###### heading 6
lheading 1
==========
lheading 2
----------
`,
tokens: [
{
type: 'heading',
raw: '# heading 1\n\n',
depth: 1,
text: 'heading 1',
tokens: [{ type: 'text', raw: 'heading 1', text: 'heading 1' }]
},
{
type: 'heading',
raw: '## heading 2\n\n',
depth: 2,
text: 'heading 2',
tokens: [{ type: 'text', raw: 'heading 2', text: 'heading 2' }]
},
{
type: 'heading',
raw: '### heading 3\n\n',
depth: 3,
text: 'heading 3',
tokens: [{ type: 'text', raw: 'heading 3', text: 'heading 3' }]
},
{
type: 'heading',
raw: '#### heading 4\n\n',
depth: 4,
text: 'heading 4',
tokens: [{ type: 'text', raw: 'heading 4', text: 'heading 4' }]
},
{
type: 'heading',
raw: '##### heading 5\n\n',
depth: 5,
text: 'heading 5',
tokens: [{ type: 'text', raw: 'heading 5', text: 'heading 5' }]
},
{
type: 'heading',
raw: '###### heading 6\n\n',
depth: 6,
text: 'heading 6',
tokens: [{ type: 'text', raw: 'heading 6', text: 'heading 6' }]
},
{
type: 'heading',
raw: 'lheading 1\n==========\n\n',
depth: 1,
text: 'lheading 1',
tokens: [{ type: 'text', raw: 'lheading 1', text: 'lheading 1' }]
},
{
type: 'heading',
raw: 'lheading 2\n----------\n',
depth: 2,
text: 'lheading 2',
tokens: [{ type: 'text', raw: 'lheading 2', text: 'lheading 2' }]
}
]
});
});
it('should not be heading if depth > 6', () => {
expectTokens({
md: '####### heading 7',
tokens: [{
type: 'paragraph',
raw: '####### heading 7',
text: '####### heading 7',
tokens: [{ type: 'text', raw: '####### heading 7', text: '####### heading 7' }]
}]
});
});
});
describe('table', () => {
it('pipe table', () => {
expectTokens({
md: `
| a | b |
|---|---|
| 1 | 2 |
`,
tokens: [{
type: 'table',
header: ['a', 'b'],
align: [null, null],
cells: [['1', '2']],
raw: '| a | b |\n|---|---|\n| 1 | 2 |\n',
tokens: {
header: [
[{ type: 'text', raw: 'a', text: 'a' }],
[{ type: 'text', raw: 'b', text: 'b' }]
],
cells: [[
[{ type: 'text', raw: '1', text: '1' }],
[{ type: 'text', raw: '2', text: '2' }]
]]
}
}]
});
});
it('align table', () => {
expectTokens({
md: `
| a | b | c |
|:--|:-:|--:|
| 1 | 2 | 3 |
`,
tokens: [{
type: 'table',
header: ['a', 'b', 'c'],
align: ['left', 'center', 'right'],
cells: [['1', '2', '3']],
raw: '| a | b | c |\n|:--|:-:|--:|\n| 1 | 2 | 3 |\n',
tokens: {
header: [
[{ type: 'text', raw: 'a', text: 'a' }],
[{ type: 'text', raw: 'b', text: 'b' }],
[{ type: 'text', raw: 'c', text: 'c' }]
],
cells: [[
[{ type: 'text', raw: '1', text: '1' }],
[{ type: 'text', raw: '2', text: '2' }],
[{ type: 'text', raw: '3', text: '3' }]
]]
}
}]
});
});
it('no pipe table', () => {
expectTokens({
md: `
a | b
--|--
1 | 2
`,
tokens: [{
type: 'table',
header: ['a', 'b'],
align: [null, null],
cells: [['1', '2']],
raw: 'a | b\n--|--\n1 | 2\n',
tokens: {
header: [
[{ type: 'text', raw: 'a', text: 'a' }],
[{ type: 'text', raw: 'b', text: 'b' }]
],
cells: [[
[{ type: 'text', raw: '1', text: '1' }],
[{ type: 'text', raw: '2', text: '2' }]
]]
}
}]
});
});
});
describe('hr', () => {
it('hr', () => {
expectTokens({
md: '---',
tokens: [
{ type: 'hr', raw: '---' }
]
});
});
});
describe('blockquote', () => {
it('start, inner-tokens, end', () => {
expectTokens({
md: '> blockquote',
tokens: [
{
type: 'blockquote',
raw: '> blockquote',
text: 'blockquote',
2020-04-02 00:22:40 -05:00
tokens: [{
type: 'paragraph',
raw: 'blockquote',
text: 'blockquote',
tokens: [
{ type: 'text', raw: 'blockquote', text: 'blockquote' }
]
}]
}
]
});
});
});
describe('list', () => {
it('unordered', () => {
expectTokens({
md: `
- item 1
- item 2
`,
tokens: [
{
type: 'list',
raw: '- item 1\n- item 2\n',
ordered: false,
start: '',
loose: false,
items: [
{
raw: '- item 1',
task: false,
checked: undefined,
loose: false,
text: 'item 1',
2020-04-02 00:22:40 -05:00
tokens: [{
type: 'text',
raw: 'item 1',
text: 'item 1',
tokens: [{ type: 'text', raw: 'item 1', text: 'item 1' }]
}]
},
{
2020-04-06 23:25:33 -05:00
raw: '- item 2\n',
2020-04-02 00:22:40 -05:00
task: false,
checked: undefined,
loose: false,
text: 'item 2\n',
2020-04-02 00:22:40 -05:00
tokens: [{
type: 'text',
raw: 'item 2',
text: 'item 2',
tokens: [{ type: 'text', raw: 'item 2', text: 'item 2' }]
}]
}
]
}
]
});
});
it('ordered', () => {
expectTokens({
md: `
1. item 1
2. item 2
`,
tokens: jasmine.arrayContaining([
jasmine.objectContaining({
type: 'list',
raw: '1. item 1\n2. item 2\n',
ordered: true,
start: 1,
items: [
jasmine.objectContaining({
raw: '1. item 1'
}),
jasmine.objectContaining({
2020-04-06 23:25:33 -05:00
raw: '2. item 2\n'
2020-04-02 00:22:40 -05:00
})
]
})
])
});
});
it('start', () => {
expectTokens({
md: `
2. item 1
3. item 2
`,
tokens: jasmine.arrayContaining([
jasmine.objectContaining({
type: 'list',
raw: '2. item 1\n3. item 2\n',
ordered: true,
start: 2,
items: [
jasmine.objectContaining({
raw: '2. item 1'
}),
jasmine.objectContaining({
2020-04-06 23:25:33 -05:00
raw: '3. item 2\n'
2020-04-02 00:22:40 -05:00
})
]
})
])
});
});
it('loose', () => {
expectTokens({
md: `
- item 1
- item 2
`,
tokens: jasmine.arrayContaining([
jasmine.objectContaining({
type: 'list',
raw: '- item 1\n\n- item 2\n',
loose: true
})
])
});
});
it('task', () => {
expectTokens({
md: `
- [ ] item 1
- [x] item 2
`,
tokens: jasmine.arrayContaining([
jasmine.objectContaining({
type: 'list',
raw: '- [ ] item 1\n- [x] item 2\n',
items: [
jasmine.objectContaining({
raw: '- [ ] item 1',
task: true,
checked: false
}),
jasmine.objectContaining({
2020-04-06 23:25:33 -05:00
raw: '- [x] item 2\n',
2020-04-02 00:22:40 -05:00
task: true,
checked: true
})
]
})
])
});
});
});
describe('html', () => {
it('div', () => {
expectTokens({
md: '<div>html</div>',
tokens: [
{
type: 'html',
raw: '<div>html</div>',
pre: false,
text: '<div>html</div>'
}
]
});
});
it('pre', () => {
expectTokens({
md: '<pre>html</pre>',
tokens: [
{
type: 'html',
raw: '<pre>html</pre>',
pre: true,
text: '<pre>html</pre>'
}
]
});
});
it('sanitize', () => {
expectTokens({
md: '<div>html</div>',
options: { sanitize: true },
tokens: [
{
type: 'paragraph',
raw: '<div>html</div>',
pre: false,
text: '&lt;div&gt;html&lt;/div&gt;',
tokens: [{
type: 'text',
raw: '&lt;div&gt;html&lt;/div&gt;',
text: '&lt;div&gt;html&lt;/div&gt;'
}]
}
]
});
});
});
describe('def', () => {
it('link', () => {
expectTokens({
md: '[link]: https://example.com',
links: {
link: { href: 'https://example.com', title: undefined }
}
});
});
it('title', () => {
expectTokens({
md: '[link]: https://example.com "title"',
links: {
link: { href: 'https://example.com', title: 'title' }
}
});
});
});
describe('inline', () => {
2020-04-06 23:25:33 -05:00
describe('inline', () => {
2020-04-02 00:22:40 -05:00
it('paragraph', () => {
2020-04-06 22:28:47 -05:00
expectInline({
2020-04-02 00:22:40 -05:00
token: { type: 'paragraph', text: 'text' },
tokens: [
{ type: 'text', raw: 'text', text: 'text' }
]
});
});
it('text', () => {
2020-04-06 22:28:47 -05:00
expectInline({
2020-04-02 00:22:40 -05:00
token: { type: 'text', text: 'text' },
tokens: [
{ type: 'text', raw: 'text', text: 'text' }
]
});
});
it('heading', () => {
2020-04-06 22:28:47 -05:00
expectInline({
2020-04-02 00:22:40 -05:00
token: { type: 'heading', text: 'text' },
tokens: [
{ type: 'text', raw: 'text', text: 'text' }
]
});
});
it('table', () => {
2020-04-06 22:28:47 -05:00
expectInline({
2020-04-02 00:22:40 -05:00
token: {
type: 'table',
header: ['a', 'b'],
align: [null, null],
cells: [['1', '2']]
},
tokens: {
header: [
[{ type: 'text', raw: 'a', text: 'a' }],
[{ type: 'text', raw: 'b', text: 'b' }]
],
cells: [
[
[{ type: 'text', raw: '1', text: '1' }],
[{ type: 'text', raw: '2', text: '2' }]
]
]
}
});
});
it('code no inline tokens', () => {
2020-04-06 22:28:47 -05:00
expectInline({
2020-04-02 00:22:40 -05:00
token: { type: 'code', text: 'code' },
tokens: undefined
});
});
});
2020-04-06 23:25:33 -05:00
describe('inlineTokens', () => {
2020-04-02 00:22:40 -05:00
it('escape', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '\\>',
tokens: [
{ type: 'escape', raw: '\\>', text: '&gt;' }
]
});
});
it('html', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '<div>html</div>',
tokens: [
{ type: 'html', raw: '<div>', inLink: false, inRawBlock: false, text: '<div>' },
2020-04-02 00:22:40 -05:00
{ type: 'text', raw: 'html', text: 'html' },
{ type: 'html', raw: '</div>', inLink: false, inRawBlock: false, text: '</div>' }
2020-04-02 00:22:40 -05:00
]
});
});
it('html sanitize', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '<div>html</div>',
options: { sanitize: true },
tokens: [
{ type: 'text', raw: '<div>', inLink: false, inRawBlock: false, text: '&lt;div&gt;' },
2020-04-02 00:22:40 -05:00
{ type: 'text', raw: 'html', text: 'html' },
{ type: 'text', raw: '</div>', inLink: false, inRawBlock: false, text: '&lt;/div&gt;' }
2020-04-02 00:22:40 -05:00
]
});
});
it('link', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '[link](https://example.com)',
tokens: [
{
type: 'link',
raw: '[link](https://example.com)',
href: 'https://example.com',
title: null,
text: 'link',
2020-04-02 00:22:40 -05:00
tokens: [
{ type: 'text', raw: 'link', text: 'link' }
]
}
]
});
});
it('link title', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '[link](https://example.com "title")',
tokens: [
{
type: 'link',
raw: '[link](https://example.com "title")',
href: 'https://example.com',
title: 'title',
text: 'link',
2020-04-02 00:22:40 -05:00
tokens: [
{ type: 'text', raw: 'link', text: 'link' }
]
}
]
});
});
it('image', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '![image](https://example.com/image.png)',
tokens: [
{
type: 'image',
raw: '![image](https://example.com/image.png)',
text: 'image',
href: 'https://example.com/image.png',
title: null
}
]
});
});
it('image title', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '![image](https://example.com/image.png "title")',
tokens: [
{
type: 'image',
raw: '![image](https://example.com/image.png "title")',
text: 'image',
href: 'https://example.com/image.png',
title: 'title'
}
]
});
});
describe('reflink', () => {
it('reflink', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '[link][]',
links: {
link: { href: 'https://example.com', title: 'title' }
},
tokens: [
{
type: 'link',
raw: '[link][]',
href: 'https://example.com',
title: 'title',
text: 'link',
2020-04-02 00:22:40 -05:00
tokens: [{
type: 'text',
raw: 'link',
text: 'link'
}]
}
]
});
});
it('nolink', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '[link]',
links: {
link: { href: 'https://example.com', title: 'title' }
},
tokens: [
{
type: 'link',
raw: '[link]',
href: 'https://example.com',
title: 'title',
text: 'link',
2020-04-02 00:22:40 -05:00
tokens: [{
type: 'text',
raw: 'link',
text: 'link'
}]
}
]
});
});
it('no def', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '[link]',
tokens: [
{ type: 'text', raw: '[', text: '[' },
{ type: 'text', raw: 'link]', text: 'link]' }
]
});
});
});
it('strong', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '**strong**',
tokens: [
{
type: 'strong',
raw: '**strong**',
text: 'strong',
2020-04-02 00:22:40 -05:00
tokens: [
{ type: 'text', raw: 'strong', text: 'strong' }
]
}
]
});
});
it('em', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '*em*',
tokens: [
{
type: 'em',
raw: '*em*',
text: 'em',
2020-04-02 00:22:40 -05:00
tokens: [
{ type: 'text', raw: 'em', text: 'em' }
]
}
]
});
});
2020-04-24 00:39:47 -05:00
describe('codespan', () => {
it('code', () => {
expectInlineTokens({
md: '`code`',
tokens: [
{ type: 'codespan', raw: '`code`', text: 'code' }
]
});
});
it('only spaces', () => {
expectInlineTokens({
md: '` `',
tokens: [
{ type: 'codespan', raw: '` `', text: ' ' }
]
});
});
it('beginning space', () => {
expectInlineTokens({
md: '` a`',
tokens: [
{ type: 'codespan', raw: '` a`', text: ' a' }
]
});
});
it('end space', () => {
expectInlineTokens({
md: '`a `',
tokens: [
{ type: 'codespan', raw: '`a `', text: 'a ' }
]
});
});
it('begin and end space', () => {
expectInlineTokens({
md: '` a `',
tokens: [
{ type: 'codespan', raw: '` a `', text: 'a' }
]
});
2020-04-02 00:22:40 -05:00
});
2020-04-24 00:44:02 -05:00
it('begin and end multiple space', () => {
expectInlineTokens({
md: '` a `',
tokens: [
{ type: 'codespan', raw: '` a `', text: ' a ' }
]
});
});
2020-04-24 00:51:16 -05:00
it('newline to space', () => {
expectInlineTokens({
md: '`a\nb`',
tokens: [
{ type: 'codespan', raw: '`a\nb`', text: 'a b' }
]
});
});
2020-04-02 00:22:40 -05:00
});
it('br', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'a\nb',
options: { gfm: true, breaks: true },
tokens: jasmine.arrayContaining([
{ type: 'br', raw: '\n' }
])
});
});
it('del', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '~~del~~',
tokens: [
{
type: 'del',
raw: '~~del~~',
text: 'del',
2020-04-02 00:22:40 -05:00
tokens: [
{ type: 'text', raw: 'del', text: 'del' }
]
}
]
});
});
describe('url', () => {
it('autolink', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '<https://example.com>',
tokens: [
{
type: 'link',
raw: '<https://example.com>',
text: 'https://example.com',
href: 'https://example.com',
tokens: [
{ type: 'text', raw: 'https://example.com', text: 'https://example.com' }
]
}
]
});
});
it('autolink email', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '<test@example.com>',
options: { mangle: false },
tokens: [
{
type: 'link',
raw: '<test@example.com>',
text: 'test@example.com',
href: 'mailto:test@example.com',
tokens: [
{ type: 'text', raw: 'test@example.com', text: 'test@example.com' }
]
}
]
});
});
it('autolink mangle email', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '<test@example.com>',
options: { mangle: true },
tokens: [
{
type: 'link',
raw: '<test@example.com>',
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
href: jasmine.stringMatching(/^mailto:(&#x?[0-9a-f]+;)+$/),
tokens: [
{
type: 'text',
raw: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/)
}
]
}
]
});
});
it('url', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'https://example.com',
tokens: [
{
type: 'link',
raw: 'https://example.com',
text: 'https://example.com',
href: 'https://example.com',
tokens: [
{ type: 'text', raw: 'https://example.com', text: 'https://example.com' }
]
}
]
});
});
it('url email', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'test@example.com',
options: { gfm: true, mangle: false },
tokens: [
{
type: 'link',
raw: 'test@example.com',
text: 'test@example.com',
href: 'mailto:test@example.com',
tokens: [
{ type: 'text', raw: 'test@example.com', text: 'test@example.com' }
]
}
]
});
});
it('url mangle email', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'test@example.com',
options: { gfm: true, mangle: true },
tokens: [
{
type: 'link',
raw: 'test@example.com',
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
href: jasmine.stringMatching(/^mailto:(&#x?[0-9a-f]+;)+$/),
tokens: [
{
type: 'text',
raw: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/)
}
]
}
]
});
});
});
it('text', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'text',
tokens: [
{
type: 'text',
raw: 'text',
text: 'text'
}
]
});
});
describe('smartypants', () => {
it('single quotes', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: "'single quotes'",
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: "'single quotes'",
text: 'single quotes'
}
]
});
});
it('double quotes', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: '"double quotes"',
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: '"double quotes"',
text: '“double quotes”'
}
]
});
});
it('ellipses', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'ellipses...',
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: 'ellipses...',
text: 'ellipses…'
}
]
});
});
it('en-dash', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'en--dash',
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: 'en--dash',
text: 'endash'
}
]
});
});
it('em-dash', () => {
2020-04-06 22:28:47 -05:00
expectInlineTokens({
2020-04-02 00:22:40 -05:00
md: 'em---dash',
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: 'em---dash',
text: 'em—dash'
}
]
});
});
});
});
});
});