@tarojs/plugin-platform-harmony-ets 4.0.0-beta.13 → 4.0.0-beta.15

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.
@@ -0,0 +1,214 @@
1
+ // 抽出来的嵌套查询
2
+ // const __nesting_style__ = [
3
+ // {
4
+ // selectors: ['container', '>', 'hello', ' ', 'txt'],
5
+ // declaration: {
6
+ // _color: "#00F00F",
7
+ // _fontSize: convertNumber2VP(40),
8
+ // }
9
+ // },
10
+ // ]
11
+ //
12
+ // function Index() {
13
+ // return __combine_nesting_style__(jsx(TaroViewTagName, {
14
+ // className: "container aaa",
15
+ // style: calcDynamicStyle(__inner_style__(), "container", null),
16
+ // children: [
17
+ // jsx(TaroTextTagName, {
18
+ // className: "txt cc",
19
+ // style: calcDynamicStyle(__inner_style__(), "txt", null),
20
+ // children: "Hello!"
21
+ // }),
22
+ // jsx(TaroViewTagName, {
23
+ // className: "hello",
24
+ // style: calcDynamicStyle(__inner_style__(), "hello", null),
25
+ // children: [
26
+ // jsx(TaroTextTagName, {
27
+ // className: "txt",
28
+ // style: calcDynamicStyle(__inner_style__(), "txt", null),
29
+ // children: "wo2rld!"
30
+ // }),
31
+ // jsx(TaroTextTagName, {
32
+ // className: "txt2",
33
+ // style: calcDynamicStyle(__inner_style__(), "txt2", null),
34
+ // children: "Hello wo2rld!"
35
+ // })
36
+ // ]
37
+ // })
38
+ // ]
39
+ // }), __nesting_style__);
40
+ // }
41
+
42
+ import { ReactElement } from 'react'
43
+
44
+ type TMappingNode = {
45
+ children: TMapping
46
+ descendants: TMapping
47
+ }
48
+
49
+ type TMapping = Record<string, TMappingNode>
50
+
51
+ type TSelectorNode = {
52
+ mapping: TMapping
53
+ node: ReactElement
54
+ }
55
+
56
+ export type NestingStyle = {
57
+ selectors: string[]
58
+ declaration: Record<string, any>
59
+ }[]
60
+
61
+ // 构建映射表
62
+ function depthTraversal(root: ReactElement) {
63
+ const class_mapping: TMapping = {}
64
+ // 记录别名,防止冲突
65
+ const selector_alias: Record<string, string[]> = {}
66
+ // 统计重名次数:{ txt: 1, cc: 2 }
67
+ const selector_alias_count: Record<string, number> = {}
68
+
69
+ const traverse = (tree) => {
70
+ if (tree) {
71
+ // 拆分classnames
72
+ const classnames = tree.props.className.split(' ')
73
+
74
+ const result: Record<string, TMapping> = {}
75
+
76
+ // 后代选择器
77
+ const descendant_map = {
78
+ children: {},
79
+ descendants: {}
80
+ }
81
+
82
+ if (tree.props.children) {
83
+ // 遍历叶子节点
84
+ if (tree.props.children instanceof Array) {
85
+ for (let i = 0; i < tree.props.children.length; i++) {
86
+ // 收集叶子节点所拥有的类名
87
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
88
+ processLeaf(tree.props.children[i], descendant_map)
89
+ }
90
+ } else if (typeof tree.props.children !== 'string'){
91
+ // 收集叶子节点所拥有的类名
92
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
93
+ processLeaf(tree.props.children, descendant_map)
94
+ }
95
+ }
96
+ for (let i = 0; i < classnames.length; i++) {
97
+ const cls = classnames[i]
98
+ let name = cls
99
+ if (selector_alias_count[name]) {
100
+ // 存在重名,需要使用别名替代
101
+ const oldName = name
102
+ name = `___${name}${selector_alias_count[oldName]}`
103
+ selector_alias_count[oldName] = selector_alias_count[oldName] + 1
104
+ selector_alias[oldName] ? selector_alias[oldName].push(name) : (selector_alias[oldName] = [name])
105
+ }
106
+ selector_alias_count[name] = 1
107
+
108
+ class_mapping[name] = descendant_map
109
+ result[name] = {
110
+ ref: descendant_map,
111
+ node: tree
112
+ }
113
+ }
114
+
115
+ return result
116
+ }
117
+ }
118
+
119
+ const processLeaf = (leaf, descendant_map: TMappingNode) => {
120
+ if (!leaf) return
121
+ const leaf_map = traverse(leaf)
122
+ if (!leaf_map) return
123
+ // 直接后代
124
+ Object.assign(descendant_map.children, leaf_map)
125
+ // 子孙后代
126
+ Object.assign(descendant_map.descendants, leaf_map)
127
+ const keys = Object.keys(leaf_map)
128
+ for (let i = 0; i < keys.length; i++) {
129
+ const leaf_child_map = class_mapping[keys[i]]
130
+ if (leaf_child_map) {
131
+ Object.assign(descendant_map.descendants, leaf_child_map.children)
132
+ }
133
+ }
134
+ }
135
+
136
+ traverse(root)
137
+ return {
138
+ mapping: class_mapping,
139
+ alias: selector_alias
140
+ }
141
+ }
142
+
143
+ // 将嵌套样式与节点合并
144
+ function combineStyle(nestingStyle: NestingStyle, class_mapping: TMapping, alias: Record<string, string[]>) {
145
+
146
+ const findElement = (selector_string, combinator_type, selector_mapping) => {
147
+ let selector_list = [selector_string]
148
+ const selector_nodes: TSelectorNode[] = []
149
+ // 判断是否存在别名
150
+ if (alias[selector_string]) {
151
+ selector_list = selector_list.concat(alias[selector_string])
152
+ }
153
+
154
+ for (let i = 0; i < selector_list.length; i++) {
155
+ const selector = selector_list[i]
156
+ // 查询选择器的节点
157
+ const object = selector_mapping[selector]
158
+ if (object) {
159
+ selector_nodes.push({
160
+ mapping: (object.ref || object)[combinator_type === '>' ? 'children' : 'descendants'],
161
+ node: object.node
162
+ })
163
+ }
164
+ }
165
+ return selector_nodes
166
+ }
167
+ const findSelector = (selectors, current_mapping): TSelectorNode[] => {
168
+ const new_selectors = selectors.slice()
169
+ const selector_string = new_selectors.shift()
170
+ const combinator_type = new_selectors.shift()
171
+ const _elements = findElement(selector_string, combinator_type, current_mapping)
172
+ if (_elements.length) {
173
+ if (new_selectors.length) {
174
+ let elements: TSelectorNode[] = []
175
+ _elements.forEach(element => {
176
+ elements = elements.concat(findSelector(new_selectors.slice(), element.mapping))
177
+ })
178
+ return elements
179
+ } else {
180
+ return _elements
181
+ }
182
+ } else {
183
+ return []
184
+ }
185
+ }
186
+ // 合并样式
187
+ nestingStyle.forEach(({ selectors, declaration }) => {
188
+ // 获取选中的节点列表
189
+ const selectors_elements = findSelector(selectors, class_mapping)
190
+ for (let i = 0; i < selectors_elements.length; i++) {
191
+ const ele = selectors_elements[i].node
192
+ if (ele) {
193
+ if (ele.props.style) {
194
+ Object.assign(ele.props.style, declaration)
195
+ } else {
196
+ ele.props.style = declaration
197
+ }
198
+ }
199
+ }
200
+ })
201
+ }
202
+
203
+ // 合并嵌套样式
204
+ // 1、构建映射表,生成一份扁平的样式表结构
205
+ // 2、遍历嵌套样式,根据选择器查找节点,合并样式
206
+ function __combine_nesting_style__(react_tree: ReactElement, styles: NestingStyle) {
207
+ // 循环一遍,构建出一颗JSX映射表
208
+ const { mapping, alias } = depthTraversal(react_tree)
209
+ // 合并样式
210
+ combineStyle(styles, mapping, alias)
211
+ return react_tree
212
+ }
213
+
214
+ export default __combine_nesting_style__
@@ -1,4 +1,5 @@
1
1
  import { eventSource } from '@tarojs/runtime/dist/runtime.esm'
2
+ import { EMPTY_OBJ, toCamelCase } from '@tarojs/shared'
2
3
 
3
4
  import { ATTRIBUTES_CALLBACK_TRIGGER_MAP, ID } from '../../constant'
4
5
  import { findChildNodeWithDFS } from '../../utils'
@@ -24,6 +25,7 @@ export class TaroElement<T extends StandardProps = StandardProps> extends TaroNo
24
25
  public _innerHTML = ''
25
26
  public _nodeInfo: TaroAny = {}
26
27
  public readonly tagName: string
28
+ public dataset: Record<string, unknown> = EMPTY_OBJ
27
29
  public _attrs: T & TaroExtraProps = {} as T & TaroExtraProps
28
30
 
29
31
  constructor(tagName: string) {
@@ -71,6 +73,21 @@ export class TaroElement<T extends StandardProps = StandardProps> extends TaroNo
71
73
  }
72
74
 
73
75
  public setAttribute (name: string, value: TaroAny): void {
76
+ switch (name) {
77
+ case ID:
78
+ eventSource.delete(this._attrs.id)
79
+ eventSource.set(value, this as TaroAny)
80
+ break
81
+ default:
82
+ if (name.startsWith('data-')) {
83
+ if (this.dataset === EMPTY_OBJ) {
84
+ this.dataset = Object.create(null)
85
+ }
86
+ this.dataset[toCamelCase(name.replace(/^data-/, ''))] = value
87
+ }
88
+ break
89
+ }
90
+
74
91
  if (name === ID) {
75
92
  eventSource.delete(this._attrs.id)
76
93
  eventSource.set(value, this as TaroAny)
@@ -130,8 +147,6 @@ export class TaroElement<T extends StandardProps = StandardProps> extends TaroNo
130
147
  }, true) || []
131
148
  }
132
149
 
133
- // TODO dataset
134
-
135
150
  public set innerHTML (value: string) {
136
151
  if (this.nodeType === NodeType.ELEMENT_NODE && this.ownerDocument) {
137
152
  const ele = this.ownerDocument.createElement('inner-html')
@@ -39,6 +39,9 @@ export function initHarmonyElement () {
39
39
  case 'image': return new TaroImageElement()
40
40
  case 'text': return new TaroTextElement()
41
41
  case 'button': return new TaroButtonElement()
42
+ case 'movable-area': return new TaroMovableAreaElement()
43
+ case 'movable-view': return new TaroMovableViewElement()
44
+ case 'progress': return new TaroProgressElement()
42
45
  case 'scroll-view': return new TaroScrollViewElement()
43
46
  case 'checkbox-group': return new TaroCheckboxGroupElement()
44
47
  case 'input': return new TaroInputElement()
@@ -1,12 +1,194 @@
1
-
2
1
  import { TaroElement } from './element'
3
2
 
4
3
  import type { MovableViewProps } from '@tarojs/components/types'
5
4
 
5
+ type Tsize = {
6
+ w: number
7
+ h: number
8
+ }
9
+ type Tpoint = {
10
+ x: number
11
+ y: number
12
+ }
13
+
14
+ function calcPosition(postion: number, start: number, end: number) {
15
+ if (postion <= end && postion >= start) {
16
+ return postion
17
+ } else if (postion < start) {
18
+ return start
19
+ } else {
20
+ return end
21
+ }
22
+ }
23
+
6
24
  @Observed
7
- export class TaroMovableViewElement extends TaroElement<MovableViewProps & {'animation': undefined}> {
25
+ export class TaroMovableViewElement extends TaroElement<MovableViewProps & { animation: undefined }> {
26
+ _scaleValue = 1
27
+ _scalevalueTemp = 1
28
+
29
+ // 父级区别的大小
30
+ _area?: Tsize
31
+ // 自己元素的大小
32
+ _selfSize?: Tsize
33
+
34
+ // 元素的位置
35
+ _position: Tpoint = {
36
+ x: 0,
37
+ y: 0,
38
+ }
39
+
40
+ _positionTemp: Tpoint = {
41
+ x: 0,
42
+ y: 0,
43
+ }
8
44
 
9
45
  constructor() {
10
46
  super('MovableView')
11
47
  }
48
+
49
+ get _outOfBounds() {
50
+ if (this.getAttribute('outOfBounds')) {
51
+ return this.selfSize ? this.selfSize.w / 3 : 0
52
+ }
53
+ return 0
54
+ }
55
+
56
+ set area(val: Tsize) {
57
+ this._area = val
58
+ }
59
+
60
+ get area(): Tsize | undefined {
61
+ return this._area
62
+ }
63
+
64
+ startScale() {
65
+ this._scalevalueTemp = this._scaleValue
66
+ }
67
+
68
+ doScale(val: number) {
69
+ const scale = this.getAttribute('scale')
70
+
71
+ // 禁止缩放的时候不生效
72
+ if (scale) {
73
+ this.scaleValue = val * this._scalevalueTemp
74
+ }
75
+ }
76
+
77
+ set scaleValue(val: number) {
78
+ if (this.checkScaleValueInBounds(val)) {
79
+ this._scaleValue = val
80
+
81
+ this.checkPositionBoundary(this.position, val)
82
+
83
+ const bindscale = this.getAttribute('bindscale')
84
+ typeof bindscale === 'function' && bindscale({ ...this.position, scale: this.scaleValue })
85
+ }
86
+ }
87
+
88
+
89
+ get scaleValue() {
90
+ return this._scaleValue
91
+ }
92
+
93
+ startMove() {
94
+ this._positionTemp = this._position
95
+ }
96
+
97
+ doMove(val: Tpoint) {
98
+ if (!this.area || !this.selfSize) return
99
+ if (this.getAttribute('disabled')) return
100
+ const direction = this.getAttribute('direction')
101
+
102
+ // 容器的宽高终点
103
+ const areaWidthEnd = this.area.w - this.selfSize.w * this.scaleValue
104
+ const areaHeightEnd = this.area.h - this.selfSize.h * this.scaleValue
105
+
106
+ const incrementWidth = (this.scaleValue - 1) * this.selfSize.w
107
+ const incrementHeight = (this.scaleValue - 1) * this.selfSize.h
108
+
109
+ let x = this._positionTemp.x
110
+ let y = this._positionTemp.y
111
+ if (['all', 'horizontal'].includes(direction)) {
112
+ const nextX = this._positionTemp.x + val.x * this.scaleValue
113
+ x = calcPosition(
114
+ nextX,
115
+ incrementWidth * 0.5 - this._outOfBounds,
116
+ areaWidthEnd + incrementWidth * 0.5 + this._outOfBounds
117
+ )
118
+ }
119
+
120
+ if (['all', 'vertical'].includes(direction)) {
121
+ const nextY = this._positionTemp.y + val.y * this.scaleValue
122
+ y = calcPosition(
123
+ nextY,
124
+ incrementHeight * 0.5 - this._outOfBounds,
125
+ areaHeightEnd + incrementHeight * 0.5 + this._outOfBounds
126
+ )
127
+ }
128
+ const bindchange = this.getAttribute('bindchange')
129
+ if (typeof bindchange === 'function') {
130
+ bindchange({ x, y, source: 'touch' })
131
+ }
132
+ this.position = {
133
+ x: x,
134
+ y: y,
135
+ }
136
+ }
137
+
138
+ get position() {
139
+ return this._position
140
+ }
141
+
142
+ set position(val: Tpoint) {
143
+ this._position = val
144
+ }
145
+
146
+ set selfSize(val: Tsize) {
147
+ this._selfSize = val
148
+ }
149
+
150
+ get selfSize(): Tsize | undefined {
151
+ return this._selfSize
152
+ }
153
+
154
+ checkPositionBoundary(position: Tpoint, scale: number) {
155
+ if (!this.area || !this.selfSize) {
156
+ return { x: 0, y: 0 }
157
+ }
158
+
159
+ const areaWidthEnd = this.area.w - this.selfSize.w * scale
160
+ const areaHeightEnd = this.area.h - this.selfSize.h * scale
161
+
162
+ const incrementWidth = (scale - 1) * this.selfSize.w
163
+ const incrementHeight = (scale - 1) * this.selfSize.h
164
+
165
+ this.position = {
166
+ x: calcPosition(position.x, incrementWidth * 0.5, areaWidthEnd + incrementWidth * 0.5),
167
+ y: calcPosition(position.y, incrementHeight * 0.5, areaHeightEnd + incrementHeight * 0.5),
168
+ }
169
+ }
170
+
171
+ checkScaleValueInBounds(currentScale: number) {
172
+ const scaleMin = this.getAttribute('scaleMin')
173
+ const scaleMax = this.getAttribute('scaleMax')
174
+
175
+ if (scaleMin && Number(scaleMin) >= 0.1 && currentScale < Number(scaleMin)) {
176
+ return false
177
+ } else if (scaleMax && Number(scaleMax) >= 0.1 && currentScale > Number(scaleMax)) {
178
+ return false
179
+ }
180
+
181
+ return true
182
+ }
183
+
184
+ public setAttribute(name: string, value: any): void {
185
+ if (name === 'x') {
186
+ this.checkPositionBoundary({ x: value, y: this.position.y }, this.scaleValue)
187
+ }
188
+ if (name === 'y') {
189
+ this.checkPositionBoundary({ x: this.position.x, y: value }, this.scaleValue)
190
+ }
191
+
192
+ super.setAttribute(name, value)
193
+ }
12
194
  }
@@ -192,7 +192,7 @@ export class BORDER_STYLE_MAP {
192
192
  export function getNodeMarginOrPaddingData (dataValue: string) {
193
193
  let res: any = {}
194
194
  if (dataValue) {
195
- const values = dataValue.trim().split(new RegExp('\\s+'))
195
+ const values = dataValue.toString().trim().split(new RegExp('\\s+'))
196
196
  switch (values.length) {
197
197
  case 1:
198
198
  res = { top: values[0], right: values[0], bottom: values[0], left: values[0] }
@@ -10,6 +10,7 @@ export * from './dom/element'
10
10
  export * from './dom/event'
11
11
  export * from './dom/node'
12
12
  export * from './dom/stylesheet'
13
+ export * from './dom/cssNesting'
13
14
  export * from './interface'
14
15
  export * from './utils'
15
16
  export * from './utils/info'
@@ -267,33 +267,45 @@ const preloadSkylineView = /* @__PURE__ */ temporarilyNotSupport('preloadSkyline
267
267
  const preloadAssets = /* @__PURE__ */ temporarilyNotSupport('preloadAssets');
268
268
 
269
269
  let display;
270
+ let navigationIndicatorRect;
270
271
  let safeArea = null;
271
- try {
272
- display = _display.getDefaultDisplaySync();
273
- // @ts-ignore
274
- display.getCutoutInfo((err, { boundingRects = [], waterfallDisplayAreaRects = {} } = {}) => {
275
- var _a, _b, _c, _d, _e, _f, _g, _h;
276
- if (err === null || err === void 0 ? void 0 : err.code) {
277
- console.error('Failed to get cutout info', JSON.stringify(err));
278
- return;
272
+ let statusBarHeight;
273
+ let windowRect;
274
+ Current.contextPromise.then((context) => {
275
+ const win = window.__ohos.getLastWindow(context);
276
+ win.then(mainWindow => {
277
+ const topRect = mainWindow.getWindowAvoidArea(window.__ohos.AvoidAreaType.TYPE_SYSTEM).topRect;
278
+ navigationIndicatorRect = mainWindow.getWindowAvoidArea(window.__ohos.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect;
279
+ statusBarHeight = topRect.top + topRect.height;
280
+ windowRect = mainWindow.getWindowProperties().windowRect;
281
+ try {
282
+ display = _display.getDefaultDisplaySync();
283
+ // @ts-ignore
284
+ display.getCutoutInfo((err, { boundingRects = [], waterfallDisplayAreaRects = {} } = {}) => {
285
+ var _a, _b, _c, _d, _e, _f;
286
+ if (err === null || err === void 0 ? void 0 : err.code) {
287
+ console.error('Failed to get cutout info', JSON.stringify(err));
288
+ return;
289
+ }
290
+ const top = Math.max(...boundingRects.map(rect => rect.top + rect.height), ((_a = waterfallDisplayAreaRects.top) === null || _a === void 0 ? void 0 : _a.top) + ((_b = waterfallDisplayAreaRects.top) === null || _b === void 0 ? void 0 : _b.height), statusBarHeight);
291
+ const bottom = display.height - Math.min((_c = waterfallDisplayAreaRects.bottom) === null || _c === void 0 ? void 0 : _c.top, navigationIndicatorRect === null || navigationIndicatorRect === void 0 ? void 0 : navigationIndicatorRect.top);
292
+ const left = ((_d = waterfallDisplayAreaRects.left) === null || _d === void 0 ? void 0 : _d.left) + ((_e = waterfallDisplayAreaRects.left) === null || _e === void 0 ? void 0 : _e.width);
293
+ const right = display.width - ((_f = waterfallDisplayAreaRects.right) === null || _f === void 0 ? void 0 : _f.left);
294
+ safeArea = {
295
+ top,
296
+ bottom,
297
+ left,
298
+ right,
299
+ height: bottom - top,
300
+ width: right - left,
301
+ };
302
+ });
303
+ }
304
+ catch (e) {
305
+ console.error('Failed to get display', e);
279
306
  }
280
- const top = Math.max(...boundingRects.map(rect => rect.top * 2 + rect.height), ((_a = waterfallDisplayAreaRects.top) === null || _a === void 0 ? void 0 : _a.top) + ((_b = waterfallDisplayAreaRects.top) === null || _b === void 0 ? void 0 : _b.height));
281
- const bottom = (((_c = waterfallDisplayAreaRects.bottom) === null || _c === void 0 ? void 0 : _c.top) + ((_d = waterfallDisplayAreaRects.bottom) === null || _d === void 0 ? void 0 : _d.height)) || display.height;
282
- const left = ((_e = waterfallDisplayAreaRects.left) === null || _e === void 0 ? void 0 : _e.left) + ((_f = waterfallDisplayAreaRects.left) === null || _f === void 0 ? void 0 : _f.width);
283
- const right = (((_g = waterfallDisplayAreaRects.right) === null || _g === void 0 ? void 0 : _g.left) + ((_h = waterfallDisplayAreaRects.right) === null || _h === void 0 ? void 0 : _h.width)) || display.width;
284
- safeArea = {
285
- top,
286
- bottom,
287
- left,
288
- right,
289
- height: bottom - top,
290
- width: right - left,
291
- };
292
307
  });
293
- }
294
- catch (e) {
295
- console.error('Failed to get display', e);
296
- }
308
+ });
297
309
  /* 同步版本 */
298
310
  const getSystemInfoSync = function () {
299
311
  var _a, _b;
@@ -322,12 +334,12 @@ const getSystemInfoSync = function () {
322
334
  res.safeArea = safeArea; // 在竖屏正方向下的安全区域 General.SafeAreaResult
323
335
  res.screenHeight = display === null || display === void 0 ? void 0 : display.height; // 屏幕高度,单位px number
324
336
  res.screenWidth = display === null || display === void 0 ? void 0 : display.width; // 屏幕宽度,单位px number
325
- res.statusBarHeight = safeArea === null || safeArea === void 0 ? void 0 : safeArea.top; // 状态栏的高度,单位px number
337
+ res.statusBarHeight = statusBarHeight; // 状态栏的高度,单位px number
326
338
  res.system = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.osFullName; // 操作系统及版本 string
327
339
  // Note: 更新配置时才能记录
328
340
  res.theme = ((_b = AppStorage.get('__TARO_APP_CONFIG')) === null || _b === void 0 ? void 0 : _b.colorMode) === ConfigurationConstant.ColorMode.COLOR_MODE_DARK ? 'dark' : 'light'; // 系统当前主题,取值为light或dark 'light' | 'dark'
329
- res.windowHeight = display === null || display === void 0 ? void 0 : display.height; // 可使用窗口高度,单位px number
330
- res.windowWidth = display === null || display === void 0 ? void 0 : display.width; // 可使用窗口宽度,单位px number
341
+ res.windowHeight = windowRect === null || windowRect === void 0 ? void 0 : windowRect.height; // 可使用窗口高度,单位px number
342
+ res.windowWidth = windowRect === null || windowRect === void 0 ? void 0 : windowRect.width; // 可使用窗口宽度,单位px number
331
343
  res.version = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.displayVersion; // 版本号 string
332
344
  return res;
333
345
  };