@webex/helper-html 2.59.2 → 2.59.3-next.1

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/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- export {escape, escapeSync, filter, filterSync, filterEscape, filterEscapeSync} from './html';
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ export {escape, escapeSync, filter, filterSync, filterEscape, filterEscapeSync} from './html';
@@ -1,286 +1,286 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- import {assert} from '@webex/test-helper-chai';
6
- import {
7
- escape,
8
- escapeSync,
9
- filter,
10
- filterSync,
11
- filterEscape,
12
- filterEscapeSync,
13
- } from '@webex/helper-html';
14
- import {skipInNode} from '@webex/test-helper-mocha';
15
-
16
- skipInNode(describe)('html', () => {
17
- const allowedTags = {
18
- br: [],
19
- em: [],
20
- strong: [],
21
- a: ['style', 'href', 'type'],
22
- b: [],
23
- blockquote: ['style'],
24
- cite: ['style'],
25
- img: ['style', 'alt', 'height', 'src', 'width'],
26
- li: ['style'],
27
- ol: ['style'],
28
- p: ['style'],
29
- span: ['style'],
30
- ul: ['style'],
31
- body: ['style', 'xmlns', 'xml:lang'],
32
- 'webex-mention': ['data-object-id', 'data-object-type'],
33
- };
34
-
35
- const allowedStyles = [
36
- 'background-color',
37
- 'color',
38
- 'font-family',
39
- 'font-size',
40
- 'font-style',
41
- 'font-weight',
42
- 'margin-left',
43
- 'margin-right',
44
- 'text-align',
45
- 'text-decoration',
46
- ];
47
-
48
- /**
49
- * @private
50
- * @returns {undefined}
51
- */
52
- function noop() {
53
- /* ignore */
54
- }
55
- const cfilter = filter(noop, allowedTags, allowedStyles);
56
- const cfilterSync = filterSync(noop, allowedTags, allowedStyles);
57
- const cfilterEscape = filterEscape(noop, allowedTags, allowedStyles);
58
- const cfilterEscapeSync = filterEscapeSync(noop, allowedTags, allowedStyles);
59
-
60
- describe('#filter()', () => {
61
- it('sanitizes trivial html', () =>
62
- cfilter('<p data-test="5"><em>foo</em></p>').then((result) =>
63
- assert.deepEqual(result, '<p><em>foo</em></p>')
64
- ));
65
- });
66
-
67
- describe('#filterSync()', () => {
68
- it('sanitizes trivial html', () =>
69
- assert.deepEqual(cfilterSync('<p data-test="5"><em>foo</em></p>'), '<p><em>foo</em></p>'));
70
- });
71
-
72
- [
73
- {
74
- // IE behaves differently from other browsers when DOMParser receives an
75
- // emptry string
76
- it: 'accepts blank strings',
77
- input: '',
78
- output: '',
79
- },
80
- {
81
- it: 'allows custom tags',
82
- input:
83
- '<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
84
- output:
85
- '<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
86
- },
87
- {
88
- it: 'filters tags',
89
- input:
90
- '<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
91
- output: '<p>text1<em>text2</em>text3text4<strong>text5</strong>text6</p>',
92
- },
93
- {
94
- it: 'filters attributes',
95
- input: '<p remove="me" style="font-size:large"><em>foo</em></p>',
96
- output: /<p style="font-size:\s?large;?"><em>foo<\/em><\/p>/,
97
- },
98
- {
99
- it: 'filters styles',
100
- input: '<p style="color:red;remove:me;font-size:large"><em>foo</em></p>',
101
- output: /<p style="color:\s?red;\s?font-size:\s?large;?"><em>foo<\/em><\/p>/,
102
- },
103
- {
104
- it: 'filters child attributes',
105
- input: '<body><span bcd="abc" style="font-size:large"><p><em>foo</em></p></span></body>',
106
- output: /<body><span style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/span><\/body>/,
107
- },
108
- {
109
- it: 'filters disallowed attributes from allowed tags',
110
- input: '<strong style="font-size:large"><span>text</span></strong>',
111
- output: '<strong><span>text</span></strong>',
112
- },
113
- {
114
- it: 'filters javascript: from a href',
115
- input: '<p><a href="javascript:window.close(); return false">click here</a></p>',
116
- output: '<p>click here</p>',
117
- },
118
- {
119
- it: 'filters javascript: from a href (mixed case)',
120
- input: '<p><a href="JavaScript:window.close(); return false">click here</a></p>',
121
- output: '<p>click here</p>',
122
- },
123
- {
124
- it: 'filters vbscript: from a href',
125
- input: '<p><a href="vbscript:window.close(); return false">click here</a></p>',
126
- output: '<p>click here</p>',
127
- },
128
- {
129
- it: 'filters vbscript: from a href (mixed case)',
130
- input: '<p><a href="VBScript:window.close(); return false">click here</a></p>',
131
- output: '<p>click here</p>',
132
- },
133
- {
134
- it: 'does not filter arbitrary strings from a href',
135
- input: '<p><a href="window.close(); return false">click here</a></p>',
136
- output: '<p><a href="window.close(); return false">click here</a></p>',
137
- },
138
- {
139
- it: 'filters javascript: from img src',
140
- input: '<p><img src="javascript:window.close(); return false">foo</img>bar</p>',
141
- output: '<p>foobar</p>',
142
- },
143
- {
144
- it: 'filters javascript: from img src (mixed case)',
145
- input: '<p><img src="javaScript:window.close(); return false">foo</img>bar</p>',
146
- output: '<p>foobar</p>',
147
- },
148
- {
149
- it: 'filters vbscript: from img src',
150
- input: '<p><img src="vbscript:window.close(); return false">foo</img>bar</p>',
151
- output: '<p>foobar</p>',
152
- },
153
- {
154
- it: 'filters vbscript: from img src (mixed case)',
155
- input: '<p><img src="VBScript:window.close(); return false">foo</img>bar</p>',
156
- output: '<p>foobar</p>',
157
- },
158
- {
159
- it: 'does not filter arbitrary strings from img src',
160
- input: '<p><img src="window.close(); return false">foo</img></p>',
161
- output: '<p><img src="window.close(); return false">foo</p>',
162
- },
163
- {
164
- it: 'correctly cleans nested a/img tags with javscript: href/src',
165
- input:
166
- '<p><a href="javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src="javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
167
- output:
168
- '<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
169
- },
170
- {
171
- it: 'correctly cleans nested a/img tags with javscript: href/src, even cleverly obfuscated with whitespace',
172
- input:
173
- '<p><a href=" javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src=" javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
174
- output:
175
- '<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
176
- },
177
- {
178
- it: 'handles weirder nesting',
179
- input:
180
- '<p>text</p><div><p>text0</p><div style="font-size: large;"><span>text1</span><span>text2</span><script></script></div></div>',
181
- output: '<p>text</p><p>text0</p><span>text1</span><span>text2</span>',
182
- },
183
- {
184
- it: 'filters bad html from unwrapped strings',
185
- input: 'Hi <script></script><em style="font-size:large;">Steve</em>',
186
- output: 'Hi <em>Steve</em>',
187
- },
188
- {
189
- it: 'filters disallowed attributes from a href',
190
- input: '<a remove="me" href="http://www.jabber.org/"><p><em>foo</em></p></a>',
191
- output: '<a href="http://www.jabber.org/"><p><em>foo</em></p></a>',
192
- },
193
- {
194
- it: 'filters disallowed attributes from a style',
195
- input: '<a remove="me" style="font-size:large"><p><em>foo</em></p></a>',
196
- output: /<a style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/a>/,
197
- },
198
- {
199
- it: 'filters disallowed attributes from a type',
200
- input: '<a remove="me" type="stuff"><p><em>foo</em></p></a>',
201
- output: '<a type="stuff"><p><em>foo</em></p></a>',
202
- },
203
- {
204
- it: 'filters disallowed attributes from img src',
205
- input: '<img remove="me" src="http://www.xmpp.org/images/psa-license.jpg">bar</img>',
206
- output: '<img src="http://www.xmpp.org/images/psa-license.jpg">bar',
207
- },
208
- {
209
- it: 'filters disallowed attributes from img alt',
210
- input: '<img remove="me" alt="A License to Jabber">bar</img>',
211
- output: '<img alt="A License to Jabber">bar',
212
- },
213
- {
214
- it: 'filters disallowed attributes from img height',
215
- input: '<img remove="me" height="261">bar</img>',
216
- output: '<img height="261">bar',
217
- },
218
- {
219
- it: 'filters disallowed attributes from img width',
220
- input: '<img remove="me" width="537">bar</img>',
221
- output: '<img width="537">bar',
222
- },
223
- ].forEach((def) => {
224
- describe('#filter()', () => {
225
- it(def.it, () => cfilter(def.input).then((out) => assert.match(out, def.output)));
226
- });
227
-
228
- describe('#filterSync()', () => {
229
- it(def.it, () => {
230
- assert.match(cfilterSync(def.input), def.output);
231
- });
232
- });
233
- });
234
-
235
- [
236
- {
237
- it: 'escapes html',
238
- input: 'This is an <b>invalid</b> tag',
239
- output: 'This is an &lt;b&gt;invalid&lt;/b&gt; tag',
240
- },
241
- ].forEach((def) => {
242
- describe('#escape()', () => {
243
- it(def.it, () => escape(def.input).then((result) => assert.deepEqual(result, def.output)));
244
- });
245
-
246
- describe('#escapeSync()', () => {
247
- it(def.it, () => {
248
- assert.deepEqual(escapeSync(def.input), def.output);
249
- });
250
- });
251
- });
252
-
253
- [
254
- {
255
- it: 'escapes invalid tags',
256
- input: '<bar>text1<em>text2</em>text3</bar>',
257
- output: '&lt;bar&gt;text1<em>text2</em>text3&lt;/bar&gt;',
258
- },
259
- {
260
- it: 'escapes deeply nested invalid tags',
261
- input:
262
- '<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
263
- output:
264
- '<p>&lt;remove-me&gt;&lt;bar&gt;text1<em>text2</em>text3&lt;/bar&gt;text4&lt;/remove-me&gt;<strong>text5</strong>text6</p>',
265
- },
266
- {
267
- it: 'esapes special characters',
268
- input: '<b>&<</b>',
269
- output: '<b>&amp;&lt;</b>',
270
- },
271
- ].forEach((def) => {
272
- describe('#filterEscape()', () => {
273
- it(def.it, () =>
274
- cfilterEscape(def.input).then((out) => {
275
- assert.match(out, def.output);
276
- })
277
- );
278
- });
279
-
280
- describe('#filterEscapeSync()', () => {
281
- it(def.it, () => {
282
- assert.match(cfilterEscapeSync(def.input), def.output);
283
- });
284
- });
285
- });
286
- });
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {assert} from '@webex/test-helper-chai';
6
+ import {
7
+ escape,
8
+ escapeSync,
9
+ filter,
10
+ filterSync,
11
+ filterEscape,
12
+ filterEscapeSync,
13
+ } from '@webex/helper-html';
14
+ import {skipInNode} from '@webex/test-helper-mocha';
15
+
16
+ skipInNode(describe)('html', () => {
17
+ const allowedTags = {
18
+ br: [],
19
+ em: [],
20
+ strong: [],
21
+ a: ['style', 'href', 'type'],
22
+ b: [],
23
+ blockquote: ['style'],
24
+ cite: ['style'],
25
+ img: ['style', 'alt', 'height', 'src', 'width'],
26
+ li: ['style'],
27
+ ol: ['style'],
28
+ p: ['style'],
29
+ span: ['style'],
30
+ ul: ['style'],
31
+ body: ['style', 'xmlns', 'xml:lang'],
32
+ 'webex-mention': ['data-object-id', 'data-object-type'],
33
+ };
34
+
35
+ const allowedStyles = [
36
+ 'background-color',
37
+ 'color',
38
+ 'font-family',
39
+ 'font-size',
40
+ 'font-style',
41
+ 'font-weight',
42
+ 'margin-left',
43
+ 'margin-right',
44
+ 'text-align',
45
+ 'text-decoration',
46
+ ];
47
+
48
+ /**
49
+ * @private
50
+ * @returns {undefined}
51
+ */
52
+ function noop() {
53
+ /* ignore */
54
+ }
55
+ const cfilter = filter(noop, allowedTags, allowedStyles);
56
+ const cfilterSync = filterSync(noop, allowedTags, allowedStyles);
57
+ const cfilterEscape = filterEscape(noop, allowedTags, allowedStyles);
58
+ const cfilterEscapeSync = filterEscapeSync(noop, allowedTags, allowedStyles);
59
+
60
+ describe('#filter()', () => {
61
+ it('sanitizes trivial html', () =>
62
+ cfilter('<p data-test="5"><em>foo</em></p>').then((result) =>
63
+ assert.deepEqual(result, '<p><em>foo</em></p>')
64
+ ));
65
+ });
66
+
67
+ describe('#filterSync()', () => {
68
+ it('sanitizes trivial html', () =>
69
+ assert.deepEqual(cfilterSync('<p data-test="5"><em>foo</em></p>'), '<p><em>foo</em></p>'));
70
+ });
71
+
72
+ [
73
+ {
74
+ // IE behaves differently from other browsers when DOMParser receives an
75
+ // emptry string
76
+ it: 'accepts blank strings',
77
+ input: '',
78
+ output: '',
79
+ },
80
+ {
81
+ it: 'allows custom tags',
82
+ input:
83
+ '<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
84
+ output:
85
+ '<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
86
+ },
87
+ {
88
+ it: 'filters tags',
89
+ input:
90
+ '<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
91
+ output: '<p>text1<em>text2</em>text3text4<strong>text5</strong>text6</p>',
92
+ },
93
+ {
94
+ it: 'filters attributes',
95
+ input: '<p remove="me" style="font-size:large"><em>foo</em></p>',
96
+ output: /<p style="font-size:\s?large;?"><em>foo<\/em><\/p>/,
97
+ },
98
+ {
99
+ it: 'filters styles',
100
+ input: '<p style="color:red;remove:me;font-size:large"><em>foo</em></p>',
101
+ output: /<p style="color:\s?red;\s?font-size:\s?large;?"><em>foo<\/em><\/p>/,
102
+ },
103
+ {
104
+ it: 'filters child attributes',
105
+ input: '<body><span bcd="abc" style="font-size:large"><p><em>foo</em></p></span></body>',
106
+ output: /<body><span style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/span><\/body>/,
107
+ },
108
+ {
109
+ it: 'filters disallowed attributes from allowed tags',
110
+ input: '<strong style="font-size:large"><span>text</span></strong>',
111
+ output: '<strong><span>text</span></strong>',
112
+ },
113
+ {
114
+ it: 'filters javascript: from a href',
115
+ input: '<p><a href="javascript:window.close(); return false">click here</a></p>',
116
+ output: '<p>click here</p>',
117
+ },
118
+ {
119
+ it: 'filters javascript: from a href (mixed case)',
120
+ input: '<p><a href="JavaScript:window.close(); return false">click here</a></p>',
121
+ output: '<p>click here</p>',
122
+ },
123
+ {
124
+ it: 'filters vbscript: from a href',
125
+ input: '<p><a href="vbscript:window.close(); return false">click here</a></p>',
126
+ output: '<p>click here</p>',
127
+ },
128
+ {
129
+ it: 'filters vbscript: from a href (mixed case)',
130
+ input: '<p><a href="VBScript:window.close(); return false">click here</a></p>',
131
+ output: '<p>click here</p>',
132
+ },
133
+ {
134
+ it: 'does not filter arbitrary strings from a href',
135
+ input: '<p><a href="window.close(); return false">click here</a></p>',
136
+ output: '<p><a href="window.close(); return false">click here</a></p>',
137
+ },
138
+ {
139
+ it: 'filters javascript: from img src',
140
+ input: '<p><img src="javascript:window.close(); return false">foo</img>bar</p>',
141
+ output: '<p>foobar</p>',
142
+ },
143
+ {
144
+ it: 'filters javascript: from img src (mixed case)',
145
+ input: '<p><img src="javaScript:window.close(); return false">foo</img>bar</p>',
146
+ output: '<p>foobar</p>',
147
+ },
148
+ {
149
+ it: 'filters vbscript: from img src',
150
+ input: '<p><img src="vbscript:window.close(); return false">foo</img>bar</p>',
151
+ output: '<p>foobar</p>',
152
+ },
153
+ {
154
+ it: 'filters vbscript: from img src (mixed case)',
155
+ input: '<p><img src="VBScript:window.close(); return false">foo</img>bar</p>',
156
+ output: '<p>foobar</p>',
157
+ },
158
+ {
159
+ it: 'does not filter arbitrary strings from img src',
160
+ input: '<p><img src="window.close(); return false">foo</img></p>',
161
+ output: '<p><img src="window.close(); return false">foo</p>',
162
+ },
163
+ {
164
+ it: 'correctly cleans nested a/img tags with javscript: href/src',
165
+ input:
166
+ '<p><a href="javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src="javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
167
+ output:
168
+ '<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
169
+ },
170
+ {
171
+ it: 'correctly cleans nested a/img tags with javscript: href/src, even cleverly obfuscated with whitespace',
172
+ input:
173
+ '<p><a href=" javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src=" javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
174
+ output:
175
+ '<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
176
+ },
177
+ {
178
+ it: 'handles weirder nesting',
179
+ input:
180
+ '<p>text</p><div><p>text0</p><div style="font-size: large;"><span>text1</span><span>text2</span><script></script></div></div>',
181
+ output: '<p>text</p><p>text0</p><span>text1</span><span>text2</span>',
182
+ },
183
+ {
184
+ it: 'filters bad html from unwrapped strings',
185
+ input: 'Hi <script></script><em style="font-size:large;">Steve</em>',
186
+ output: 'Hi <em>Steve</em>',
187
+ },
188
+ {
189
+ it: 'filters disallowed attributes from a href',
190
+ input: '<a remove="me" href="http://www.jabber.org/"><p><em>foo</em></p></a>',
191
+ output: '<a href="http://www.jabber.org/"><p><em>foo</em></p></a>',
192
+ },
193
+ {
194
+ it: 'filters disallowed attributes from a style',
195
+ input: '<a remove="me" style="font-size:large"><p><em>foo</em></p></a>',
196
+ output: /<a style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/a>/,
197
+ },
198
+ {
199
+ it: 'filters disallowed attributes from a type',
200
+ input: '<a remove="me" type="stuff"><p><em>foo</em></p></a>',
201
+ output: '<a type="stuff"><p><em>foo</em></p></a>',
202
+ },
203
+ {
204
+ it: 'filters disallowed attributes from img src',
205
+ input: '<img remove="me" src="http://www.xmpp.org/images/psa-license.jpg">bar</img>',
206
+ output: '<img src="http://www.xmpp.org/images/psa-license.jpg">bar',
207
+ },
208
+ {
209
+ it: 'filters disallowed attributes from img alt',
210
+ input: '<img remove="me" alt="A License to Jabber">bar</img>',
211
+ output: '<img alt="A License to Jabber">bar',
212
+ },
213
+ {
214
+ it: 'filters disallowed attributes from img height',
215
+ input: '<img remove="me" height="261">bar</img>',
216
+ output: '<img height="261">bar',
217
+ },
218
+ {
219
+ it: 'filters disallowed attributes from img width',
220
+ input: '<img remove="me" width="537">bar</img>',
221
+ output: '<img width="537">bar',
222
+ },
223
+ ].forEach((def) => {
224
+ describe('#filter()', () => {
225
+ it(def.it, () => cfilter(def.input).then((out) => assert.match(out, def.output)));
226
+ });
227
+
228
+ describe('#filterSync()', () => {
229
+ it(def.it, () => {
230
+ assert.match(cfilterSync(def.input), def.output);
231
+ });
232
+ });
233
+ });
234
+
235
+ [
236
+ {
237
+ it: 'escapes html',
238
+ input: 'This is an <b>invalid</b> tag',
239
+ output: 'This is an &lt;b&gt;invalid&lt;/b&gt; tag',
240
+ },
241
+ ].forEach((def) => {
242
+ describe('#escape()', () => {
243
+ it(def.it, () => escape(def.input).then((result) => assert.deepEqual(result, def.output)));
244
+ });
245
+
246
+ describe('#escapeSync()', () => {
247
+ it(def.it, () => {
248
+ assert.deepEqual(escapeSync(def.input), def.output);
249
+ });
250
+ });
251
+ });
252
+
253
+ [
254
+ {
255
+ it: 'escapes invalid tags',
256
+ input: '<bar>text1<em>text2</em>text3</bar>',
257
+ output: '&lt;bar&gt;text1<em>text2</em>text3&lt;/bar&gt;',
258
+ },
259
+ {
260
+ it: 'escapes deeply nested invalid tags',
261
+ input:
262
+ '<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
263
+ output:
264
+ '<p>&lt;remove-me&gt;&lt;bar&gt;text1<em>text2</em>text3&lt;/bar&gt;text4&lt;/remove-me&gt;<strong>text5</strong>text6</p>',
265
+ },
266
+ {
267
+ it: 'esapes special characters',
268
+ input: '<b>&<</b>',
269
+ output: '<b>&amp;&lt;</b>',
270
+ },
271
+ ].forEach((def) => {
272
+ describe('#filterEscape()', () => {
273
+ it(def.it, () =>
274
+ cfilterEscape(def.input).then((out) => {
275
+ assert.match(out, def.output);
276
+ })
277
+ );
278
+ });
279
+
280
+ describe('#filterEscapeSync()', () => {
281
+ it(def.it, () => {
282
+ assert.match(cfilterEscapeSync(def.input), def.output);
283
+ });
284
+ });
285
+ });
286
+ });