eslint-plugin-smarthr 0.3.16 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.3.18](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.17...v0.3.18) (2023-12-30)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a11y-delegate-element-has-role-presentation でインタラクティブな要素として扱われるコンポーネントを追加する ([#95](https://github.com/kufu/eslint-plugin-smarthr/issues/95)) ([7d2b66d](https://github.com/kufu/eslint-plugin-smarthr/commit/7d2b66d7f998e78e767a8d1353a6beac5312027c))
|
|
11
|
+
|
|
12
|
+
### [0.3.17](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.16...v0.3.17) (2023-12-30)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* a11y-delegate-element-has-role-presentationのインタラクティブなコンポーネントであることの判定で、小文字始まりのコンポーネントの場合判定ミスされるバグを修正 ([#94](https://github.com/kufu/eslint-plugin-smarthr/issues/94)) ([50a8296](https://github.com/kufu/eslint-plugin-smarthr/commit/50a8296b6d22c5a6475469b1ed2ea1a46234f6fd))
|
|
18
|
+
|
|
5
19
|
### [0.3.16](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.15...v0.3.16) (2023-12-30)
|
|
6
20
|
|
|
7
21
|
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- [a11y-anchor-has-href-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-anchor-has-href-attribute)
|
|
4
4
|
- [a11y-clickable-element-has-text](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-clickable-element-has-text)
|
|
5
|
+
- [a11y-delegate-element-has-role-presentation](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-delegate-element-has-role-presentation)
|
|
5
6
|
- [a11y-heading-in-sectioning-content](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-heading-in-sectioning-content)
|
|
6
7
|
- [a11y-image-has-alt-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-image-has-alt-attribute)
|
|
7
8
|
- [a11y-input-has-name-attribute](https://github.com/kufu/eslint-plugin-smarthr/tree/main/rules/a11y-input-has-name-attribute)
|
package/package.json
CHANGED
|
@@ -10,6 +10,9 @@ const EXPECTED_NAMES = {
|
|
|
10
10
|
'Combo(b|B)ox$': 'ComboBox$',
|
|
11
11
|
'DatePicker$': 'DatePicker$',
|
|
12
12
|
'DropZone$': 'DropZone$',
|
|
13
|
+
'Switch$': 'Switch$',
|
|
14
|
+
'SegmentedControl$': 'SegmentedControl$',
|
|
15
|
+
'RightFixedNote$': 'RightFixedNote$',
|
|
13
16
|
'FieldSet$': 'FieldSet$',
|
|
14
17
|
'(b|B)utton$': 'Button$',
|
|
15
18
|
'Anchor$': 'Anchor$',
|
|
@@ -21,6 +24,7 @@ const EXPECTED_NAMES = {
|
|
|
21
24
|
'ActionDialogWithTrigger$': 'ActionDialogWithTrigger$',
|
|
22
25
|
'RemoteDialogTrigger$': 'RemoteDialogTrigger$',
|
|
23
26
|
'RemoteTrigger(.+)Dialog$': 'RemoteTrigger(.+)Dialog$',
|
|
27
|
+
'FormDialog$': 'FormDialog$',
|
|
24
28
|
'Pagination$': 'Pagination$',
|
|
25
29
|
'SideNav$': 'SideNav$',
|
|
26
30
|
'AccordionPanel$': 'AccordionPanel$',
|
|
@@ -32,7 +36,7 @@ const UNEXPECTED_NAMES = {
|
|
|
32
36
|
'(Link|^a)$': '(Link)$',
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
const INTERACTIVE_COMPONENT_NAMES = Object.
|
|
39
|
+
const INTERACTIVE_COMPONENT_NAMES = Object.keys(EXPECTED_NAMES)
|
|
36
40
|
const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
|
|
37
41
|
|
|
38
42
|
const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex, onAttrs) => {
|
|
@@ -40,14 +44,14 @@ const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex,
|
|
|
40
44
|
|
|
41
45
|
return `${nodeName} に${onAttrsText}を設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
42
46
|
- 方法1: ${nodeName}がinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
43
|
-
- "${interactiveComponentRegex}" の正規表現にmatch
|
|
47
|
+
- "${interactiveComponentRegex}" の正規表現にmatchするコンポーネントに差し替える、もしくは名称を変更してください
|
|
44
48
|
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
|
|
45
49
|
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
|
|
46
50
|
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください`
|
|
47
51
|
}
|
|
48
52
|
const messageRolePresentationNotHasInteractive = (nodeName, interactiveComponentRegex, onAttrs) => `${nodeName}に 'role="presentation"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
49
53
|
- 方法1: 子要素にインタラクティブな要素が存在するにも関わらずこのエラーが表示されている場合、子要素の名称を変更してください
|
|
50
|
-
- "${interactiveComponentRegex}" の正規表現にmatch
|
|
54
|
+
- "${interactiveComponentRegex}" の正規表現にmatchするよう、インタラクティブな子要素全てを差し替える、もしくは名称を変更してください
|
|
51
55
|
- 方法2: ${nodeName}自体がインタラクティブな要素の場合、'role="presentation"'を削除した上で名称を変更してください
|
|
52
56
|
- "${interactiveComponentRegex}" の正規表現にmatchするよう、${nodeName}の名称を変更してください
|
|
53
57
|
- 方法3: 子要素にインタラクティブな要素が存在し、${onAttrs.join(', ')}全属性をそれらの要素に移動させられる場合、'role="presentation"'を消した上で実施してください`
|
|
@@ -12,6 +12,27 @@ const ruleTester = new RuleTester({
|
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
+
const defaultInteractiveRegex = '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/'
|
|
16
|
+
const messageNonInteractiveEventHandler = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => {
|
|
17
|
+
const onAttrsText = onAttrs.join(', ')
|
|
18
|
+
|
|
19
|
+
return `${nodeName} に${onAttrsText}を設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
20
|
+
- 方法1: ${nodeName}がinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
21
|
+
- "${interactiveComponentRegex}" の正規表現にmatchするコンポーネントに差し替える、もしくは名称を変更してください
|
|
22
|
+
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
|
|
23
|
+
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
|
|
24
|
+
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください`
|
|
25
|
+
}
|
|
26
|
+
const messageRolePresentationNotHasInteractive = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => `${nodeName}に 'role="presentation"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
27
|
+
- 方法1: 子要素にインタラクティブな要素が存在するにも関わらずこのエラーが表示されている場合、子要素の名称を変更してください
|
|
28
|
+
- "${interactiveComponentRegex}" の正規表現にmatchするよう、インタラクティブな子要素全てを差し替える、もしくは名称を変更してください
|
|
29
|
+
- 方法2: ${nodeName}自体がインタラクティブな要素の場合、'role="presentation"'を削除した上で名称を変更してください
|
|
30
|
+
- "${interactiveComponentRegex}" の正規表現にmatchするよう、${nodeName}の名称を変更してください
|
|
31
|
+
- 方法3: 子要素にインタラクティブな要素が存在し、${onAttrs.join(', ')}全属性をそれらの要素に移動させられる場合、'role="presentation"'を消した上で実施してください`
|
|
32
|
+
const messageInteractiveHasRolePresentation = (nodeName, interactiveComponentRegex = defaultInteractiveRegex) => `${nodeName}はinput、buttonやaなどのインタラクティブな要素にもかかわらず 'role="presentation"' が設定されているため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
33
|
+
- 方法1: 'role="presentation"' を削除してください
|
|
34
|
+
- 方法2: ${nodeName}の名称を "${interactiveComponentRegex}" とマッチしない名称に変更してください`
|
|
35
|
+
|
|
15
36
|
ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
16
37
|
valid: [
|
|
17
38
|
{ code: '<Input />' },
|
|
@@ -19,52 +40,20 @@ ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
|
19
40
|
{ code: '<FugaButton>any</FugaButton>' },
|
|
20
41
|
{ code: '<Link />' },
|
|
21
42
|
{ code: '<div onClick={any} role="presentation"><Link /></div>' },
|
|
43
|
+
{ code: '<div onClick={any} role="presentation"><button /></div>' },
|
|
22
44
|
{ code: '<Wrapper onClick={any} role="presentation"><Link /></Wrapper>' },
|
|
23
45
|
{ code: '<Wrapper onClick={any} role="presentation"><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }] },
|
|
24
46
|
{ code: '<Wrapper onClick={any} role="presentation"><any><Link /></any></Wrapper>' },
|
|
25
47
|
],
|
|
26
48
|
invalid: [
|
|
27
|
-
{ code: '<Input role="presentation" />', errors: [ { message:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{ code: '<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{ code: '<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{ code: '<Link role="presentation" />', errors: [ { message: `Linkはinput、buttonやaなどのインタラクティブな要素にもかかわらず 'role="presentation"' が設定されているため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
37
|
-
- 方法1: 'role="presentation"' を削除してください
|
|
38
|
-
- 方法2: Linkの名称を "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" とマッチしない名称に変更してください` } ] },
|
|
39
|
-
{ code: '<div onClick={any} onSubmit={any2} role="presentation"><Hoge /></div>', errors: [ { message: `divに 'role="presentation"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
40
|
-
- 方法1: 子要素にインタラクティブな要素が存在するにも関わらずこのエラーが表示されている場合、子要素の名称を変更してください
|
|
41
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" の正規表現にmatchするよう、インタラクティブな子要素全ての名称を変更してください
|
|
42
|
-
- 方法2: div自体がインタラクティブな要素の場合、'role="presentation"'を削除した上で名称を変更してください
|
|
43
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" の正規表現にmatchするよう、divの名称を変更してください
|
|
44
|
-
- 方法3: 子要素にインタラクティブな要素が存在し、onClick, onSubmit全属性をそれらの要素に移動させられる場合、'role="presentation"'を消した上で実施してください` } ] },
|
|
45
|
-
{ code: '<div onClick={any}><Link /></div>', errors: [ { message: `div にonClickを設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
46
|
-
- 方法1: divがinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
47
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" の正規表現にmatchする名称に変更してください
|
|
48
|
-
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接onClickを設定することを検討してください
|
|
49
|
-
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、onClickの設定要素を検討してください
|
|
50
|
-
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的でonClickを設定している場合、'role="presentation"' を設定してください` } ] },
|
|
51
|
-
{ code: '<Wrapper onClick={any}><Link /></Wrapper>', errors: [ { message: `Wrapper にonClickを設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
52
|
-
- 方法1: Wrapperがinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
53
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" の正規表現にmatchする名称に変更してください
|
|
54
|
-
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接onClickを設定することを検討してください
|
|
55
|
-
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、onClickの設定要素を検討してください
|
|
56
|
-
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的でonClickを設定している場合、'role="presentation"' を設定してください` } ] },
|
|
57
|
-
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: `Wrapper にonSubmitを設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
58
|
-
- 方法1: Wrapperがinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
59
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/" の正規表現にmatchする名称に変更してください
|
|
60
|
-
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接onSubmitを設定することを検討してください
|
|
61
|
-
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、onSubmitの設定要素を検討してください
|
|
62
|
-
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的でonSubmitを設定している場合、'role="presentation"' を設定してください` } ] },
|
|
63
|
-
{ code: '<Wrapper onClick={any} onChange={anyany}><any><Link /></any></Wrapper>', errors: [ { message: `Wrapper にonClick, onChangeを設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
64
|
-
- 方法1: Wrapperがinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
|
|
65
|
-
- "/(Input$|Textarea$|Select$|InputFile$|RadioButtonPanel$|CheckBox$|ComboBox$|DatePicker$|DropZone$|FieldSet$|Button$|Anchor$|Link$|TabItem$|(Anchor|Link)$|Form$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$)/" の正規表現にmatchする名称に変更してください
|
|
66
|
-
- 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接onClick, onChangeを設定することを検討してください
|
|
67
|
-
- 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、onClick, onChangeの設定要素を検討してください
|
|
68
|
-
- 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的でonClick, onChangeを設定している場合、'role="presentation"' を設定してください` } ] },
|
|
49
|
+
{ code: '<Input role="presentation" />', errors: [ { message: messageInteractiveHasRolePresentation('Input') } ] },
|
|
50
|
+
{ code: '<HogeForm role="presentation">any</HogeForm>', errors: [ { message: messageInteractiveHasRolePresentation('HogeForm') } ] },
|
|
51
|
+
{ code: '<FugaButton role="presentation">any</FugaButton>', errors: [ { message: messageInteractiveHasRolePresentation('FugaButton') } ] },
|
|
52
|
+
{ code: '<Link role="presentation" />', errors: [ { message: messageInteractiveHasRolePresentation('Link') } ] },
|
|
53
|
+
{ code: '<div onClick={any} onSubmit={any2} role="presentation"><Hoge /></div>', errors: [ { message: messageRolePresentationNotHasInteractive('div', ['onClick', 'onSubmit']) } ] },
|
|
54
|
+
{ code: '<div onClick={any}><Link /></div>', errors: [ { message: messageNonInteractiveEventHandler('div', ['onClick']) } ] },
|
|
55
|
+
{ code: '<Wrapper onClick={any}><Link /></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick']) } ] },
|
|
56
|
+
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onSubmit'], '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/') } ] },
|
|
57
|
+
{ code: '<Wrapper onClick={any} onChange={anyany}><any><Link /></any></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick', 'onChange']) } ] },
|
|
69
58
|
],
|
|
70
59
|
});
|