Passed
Push — master ( 9a9b82...74dc3c )
by Jean-Christophe
02:28
created

DataTable::getGroupByFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Ajax\semantic\widgets\datatable;
4
5
use Ajax\common\Widget;
6
use Ajax\JsUtils;
7
use Ajax\semantic\html\collections\table\HtmlTable;
8
use Ajax\semantic\html\elements\HtmlInput;
9
use Ajax\semantic\html\modules\checkbox\HtmlCheckbox;
10
use Ajax\semantic\html\base\constants\Direction;
11
use Ajax\service\JArray;
12
use Ajax\semantic\widgets\base\InstanceViewer;
13
use Ajax\semantic\html\collections\table\traits\TableTrait;
14
use Ajax\semantic\html\collections\HtmlMessage;
15
use Ajax\semantic\html\base\traits\BaseTrait;
16
use Ajax\service\JString;
17
use Ajax\common\html\HtmlDoubleElement;
18
use Ubiquity\utils\http\URequest;
0 ignored issues
show
Bug introduced by
The type Ubiquity\utils\http\URequest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
20
/**
21
 * DataTable widget for displaying list of objects
22
 * @version 1.0
23
 * @author jc
24
 * @since 2.2
25
 *
26
 */
27
class DataTable extends Widget {
28
	use TableTrait,DataTableFieldAsTrait,HasCheckboxesTrait,BaseTrait;
29
	protected $_searchField;
30
	protected $_urls;
31
	protected $_pagination;
32
	protected $_compileParts;
33
	protected $_deleteBehavior;
34
	protected $_editBehavior;
35
	protected $_displayBehavior;
36
	protected $_visibleHover=false;
37
	protected $_targetSelector;
38
	protected $_refreshSelector;
39
	protected $_emptyMessage;
40
	protected $_json;
41
	protected $_rowClass="_element";
42
	protected $_sortable;
43
	protected $_hiddenColumns;
44
	protected $_colWidths;
45
46
47
	public function __construct($identifier,$model,$modelInstance=NULL) {
48
		parent::__construct($identifier, $model,$modelInstance);
49
		$this->_init(new InstanceViewer($identifier), "table", new HtmlTable($identifier, 0,0), false);
50
		$this->_urls=[];
51
		$this->_emptyMessage=new HtmlMessage("","nothing to display");
52
		$this->_emptyMessage->setIcon("info circle");
53
	}
54
55
	public function run(JsUtils $js){
56
		$offset=$js->scriptCount();
57
		if($this->_hasCheckboxes && isset($js)){
58
			$this->_runCheckboxes($js);
59
		}
60
		if($this->_visibleHover){
61
			$js->execOn("mouseover", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'visible');",["preventDefault"=>false,"stopPropagation"=>true]);
62
			$js->execOn("mouseout", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'hidden');",["preventDefault"=>false,"stopPropagation"=>true]);
63
		}
64
		if(\is_array($this->_deleteBehavior))
65
			$this->_generateBehavior("delete",$this->_deleteBehavior, $js);
66
		if(\is_array($this->_editBehavior))
67
			$this->_generateBehavior("edit",$this->_editBehavior,$js);
68
		if(\is_array($this->_displayBehavior)){
69
			$this->_generateBehavior("display",$this->_displayBehavior,$js);
70
		}
71
		parent::run($js);
72
		if(isset($this->_pagination))
73
			$this->_associatePaginationBehavior($js,$offset);
74
		$this->_associateSearchFieldBehavior($js,$offset);
75
			
76
	}
77
78
	protected function _generateBehavior($op,$params,JsUtils $js){
79
		if(isset($this->_urls[$op])){
80
			$params=\array_merge($params,["attr"=>"data-ajax"]);
81
			$js->ajaxOnClick("#".$this->identifier." ._".$op, $this->_urls[$op],$this->getTargetSelector($op),$params);
82
		}
83
	}
84
85
	/**
86
	 * {@inheritDoc}
87
	 * @see TableTrait::getTable()
88
	 */
89
	protected function getTable() {
90
		return $this->content["table"];
91
	}
92
	
93
	public function refreshTR(){
94
		$this->getTable()->refreshTR();
95
		return $this;
96
	}
97
	
98
	public function refreshTD($fieldName,$jquery,$view){
99
		$index=$this->_getIndex($fieldName);
100
		$this->compile($jquery,$view);
101
		return $this->refreshTR()->getTable()->getCell(0,$index);
102
	}
103
	
104
105
106
	public function compile(JsUtils $js=NULL,&$view=NULL){
107
		if(!$this->_generated){
108
			if(isset($this->_buttonsColumn)){
109
				$this->_instanceViewer->sortColumnContent($this->_buttonsColumn, $this->_buttons);
110
			}
111
			$this->_instanceViewer->setInstance($this->_model);
112
			$captions=$this->_instanceViewer->getCaptions();
113
			$table=$this->content["table"];
114
			if($this->_hasCheckboxes){
115
				$this->_generateMainCheckbox($captions);
116
			}
117
			$table->setRowCount(0, \sizeof($captions));
118
			$this->_generateHeader($table,$captions);
119
120
			if(isset($this->_compileParts))
121
				$table->setCompileParts($this->_compileParts);
122
123
			$this->_generateContent($table);
124
125
			$this->compileExtraElements($table, $captions);
126
			$this->_compileSearchFieldBehavior($js);
127
128
			$this->content=JArray::sortAssociative($this->content, [PositionInTable::BEFORETABLE,"table",PositionInTable::AFTERTABLE]);
129
			$this->_compileForm();
130
			$this->_applyStyleAttributes($table);
131
			$this->_generated=true;
132
		}
133
		return parent::compile($js,$view);
134
	}
135
136
	protected function compileExtraElements($table,$captions){
137
138
		if($this->_hasCheckboxes && $table->hasPart("thead")){
139
			$table->getHeader()->getCell(0, 0)->addClass("no-sort");
140
		}
141
142
		if(isset($this->_toolbar)){
143
			$this->_setToolbarPosition($table, $captions);
144
		}
145
		if(isset($this->_pagination) && $this->_pagination->getVisible()){
146
			$this->_generatePagination($table);
147
		}
148
	}
149
150
	protected function _applyStyleAttributes($table){
151
		if(isset($this->_hiddenColumns))
152
			$this->_hideColumns();
153
			if(isset($this->_colWidths)){
154
				foreach ($this->_colWidths as $colIndex=>$width){
155
					$table->setColWidth($colIndex,$width);
156
				}
157
			}
158
	}
159
160
	protected function _hideColumns(){
161
		foreach ($this->_hiddenColumns as $colIndex){
162
			$this->_self->hideColumn($colIndex);
163
		}
164
		return $this;
165
	}
166
167
	protected function _generateHeader(HtmlTable $table,$captions){
168
		$table->setHeaderValues($captions);
169
		if(isset($this->_sortable)){
170
			$table->setSortable($this->_sortable);
171
		}
172
	}
173
174
175
176
	protected function _generateContent($table){
177
		$objects=$this->_modelInstance;
178
		if(isset($this->_pagination)){
179
			$objects=$this->_pagination->getObjects($this->_modelInstance);
180
		}
181
			InstanceViewer::setIndex(0);
182
			$fields=$this->_instanceViewer->getSimpleProperties();
183
			$groupByFields=$this->_instanceViewer->getGroupByFields();
184
			if(!is_array($groupByFields)){
185
				$table->fromDatabaseObjects($objects, function($instance) use($table,$fields){
186
					return $this->_generateRow($instance, $fields,$table);
187
				});
188
			}else{
189
				$activeValues=array_combine($groupByFields, array_fill(0, sizeof($groupByFields), null));
190
				$uuids=[];
191
				$table->fromDatabaseObjects($objects, function($instance) use($table,$fields,&$activeValues,$groupByFields,&$uuids){
192
					$this->_instanceViewer->setInstance($instance);
193
					foreach ($groupByFields as $index=>$gbField){
194
						$this->_generateGroupByRow($index, $gbField, $table,$fields,$activeValues, $uuids);
195
					}
196
					return $this->_generateRow($instance, $fields,$table,null,$uuids);
197
				});
198
			}
199
		if($table->getRowCount()==0){
200
			$result=$table->addRow();
201
			$result->mergeRow();
202
			$result->setValues([$this->_emptyMessage]);
203
		}
204
	}
205
	
206
	protected function _generateGroupByRow($index,$gbField,$table,$fields,&$activeValues,&$uuids){
207
		$newValue=$this->_instanceViewer->getValue($gbField);
208
		if($this->getElementContent($activeValues[$gbField])!==$this->getElementContent($newValue)){
209
			if($index==0){
210
				$uuids=[];
211
			}
212
			$uuid=uniqid("grp");
213
			$uuids[$gbField]=$uuid;
214
			$id=$this->_instanceViewer->getIdentifier();
215
			$result=$table->addMergeRow(sizeof($fields)+1,$newValue);
216
			$result->setIdentifier($this->identifier."-tr-gb-".$id);
217
			$result->setProperty("data-ajax",$id);
218
			$result->setProperty("data-group",$uuid);
219
			$result->addToProperty("class",$this->_rowClass);
220
			$activeValues[$gbField]=$newValue;
221
		}
222
	}
223
	
224
	private function getElementContent($elm){
225
		if($elm instanceof HtmlDoubleElement){
226
			return $elm->getTextContent();
227
		}
228
		return $elm;
229
	}
230
	
231
	public function getFieldValue($index){
232
		$index=$this->_getIndex($index);
233
		if(is_numeric($index)){
234
			$values= $this->_instanceViewer->getValues();
235
			if(isset($values[$index])){
236
				return $values[$index];
237
			}
238
		}
239
		return null;
240
	}
241
242
	protected function _generateRow($instance,$fields,&$table,$checkedClass=null,$uuids=null){
243
		$this->_instanceViewer->setInstance($instance);
244
		InstanceViewer::$index++;
245
		$values= $this->_instanceViewer->getValues();
246
		$id=$this->_instanceViewer->getIdentifier();
247
		$dataAjax=$id;
248
		$id=$this->cleanIdentifier($id);
249
		if($this->_hasCheckboxes){
250
			$ck=new HtmlCheckbox("ck-".$this->identifier."-".$id,"");
251
			$checked=false;
252
			if(isset($this->_checkedCallback)){
253
				$func=$this->_checkedCallback;
254
				$checked=$func($instance);
255
			}
256
			$ck->setChecked($checked);
257
			$ck->setOnChange("event.stopPropagation();");
258
			$field=$ck->getField();
259
			$field->setProperty("value",$dataAjax);
260
			$field->setProperty("name", "selection[]");
261
			if(isset($checkedClass))
262
				$field->setClass($checkedClass);
263
			\array_unshift($values, $ck);
264
		}
265
		$result=$table->newRow();
266
		$result->setIdentifier($this->identifier."-tr-".$id);
267
		$result->setProperty("data-ajax",$dataAjax);
268
		$result->setValues($values);
269
		$result->addToProperty("class",$this->_rowClass);
270
		$result->setPropertyValues("data-field", $fields);
271
		if(isset($uuids)){
272
			$result->setProperty("data-child",implode(" ", $uuids));
273
		}
274
		return $result;
275
	}
276
277
	protected function _generatePagination($table){
278
		if(isset($this->_toolbar)){
279
			if($this->_toolbarPosition==PositionInTable::FOOTER)
280
				$this->_toolbar->setFloated("left");
281
		}
282
		$footer=$table->getFooter();
283
		$footer->mergeCol();
284
		$footer->addValues($this->_pagination->generateMenu($this->identifier));
285
	}
286
287
	protected function _associatePaginationBehavior(JsUtils $js=NULL,$offset=null){
288
		if(isset($this->_urls["refresh"])){
289
			$menu=$this->_pagination->getMenu();
290
			if(isset($menu) && isset($js)){
291
				$js->postOnClick("#".$menu->getIdentifier()." .item",$this->_urls["refresh"],"{'p':$(this).attr('data-page'),'_model':'".JString::doubleBackSlashes($this->_model)."'}",$this->getRefreshSelector(),["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>false,"jsCallback"=>'$("#'.$this->identifier.'").trigger("pageChange");$("#'.$this->identifier.'").trigger("activeRowChange");']);
292
				$page=URequest::post("p");
293
				if(isset($page)){
294
					$js->execAtLast('$("#'.$this->getIdentifier().' .pagination").children("a.item").removeClass("active");$("#'.$this->getIdentifier().' .pagination").children("a.item[data-page='.$page.']:not(.no-active)").addClass("active");');
295
				}				
296
			}
297
		}
298
	}
299
	
300
	protected function _compileSearchFieldBehavior(JsUtils $js=NULL){
301
		if(isset($this->_searchField) && isset($js) && isset($this->_urls["refresh"])){
302
			$this->_searchField->postOn("change", $this->_urls["refresh"],"{'s':$(self).val(),'_model':'".JString::doubleBackSlashes($this->_model)."'}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>"internal","jsCallback"=>'$("#'.$this->identifier.'").trigger("searchTerminate",[$(self).val()]);']);
303
		}
304
	}
305
	protected function _associateSearchFieldBehavior(JsUtils $js=NULL,$offset=null){
306
		
307
	}
308
309
	protected function _getFieldName($index){
310
		$fieldName=parent::_getFieldName($index);
311
		if(\is_object($fieldName))
312
			$fieldName="field-".$index;
313
		return $fieldName."[]";
314
	}
315
316
	protected function _getFieldCaption($index){
317
		return null;
318
	}
319
320
	protected function _setToolbarPosition($table,$captions=NULL){
321
		switch ($this->_toolbarPosition){
322
			case PositionInTable::BEFORETABLE:
323
			case PositionInTable::AFTERTABLE:
324
				if(isset($this->_compileParts)===false){
325
					$this->content[$this->_toolbarPosition]=$this->_toolbar;
326
				}
327
				break;
328
			case PositionInTable::HEADER:
329
			case PositionInTable::FOOTER:
330
			case PositionInTable::BODY:
331
				$this->addToolbarRow($this->_toolbarPosition,$table, $captions);
332
				break;
333
		}
334
	}
335
336
	/**
337
	 * Associates a $callback function after the compilation of the field at $index position
338
	 * The $callback function can take the following arguments : $field=>the compiled field, $instance : the active instance of the object, $index: the field position
339
	 * @param int $index postion of the compiled field
340
	 * @param callable $callback function called after the field compilation
341
	 * @return DataTable
342
	 */
343
	public function afterCompile($index,$callback){
344
		$this->_instanceViewer->afterCompile($index,$callback);
345
		return $this;
346
	}
347
348
	private function addToolbarRow($part,$table,$captions){
349
		$hasPart=$table->hasPart($part);
350
		if($hasPart){
351
			$row=$table->getPart($part)->addRow(\sizeof($captions));
352
		}else{
353
			$row=$table->getPart($part)->getRow(0);
354
		}
355
		$row->mergeCol();
356
		$row->setValues([$this->_toolbar]);
357
	}
358
359
	/**
360
	 * {@inheritDoc}
361
	 * @see Widget::getHtmlComponent()
362
	 * @return HtmlTable
363
	 */
364
	public function getHtmlComponent(){
365
		return $this->content["table"];
366
	}
367
368
	public function getUrls() {
369
		return $this->_urls;
370
	}
371
372
	/**
373
	 * Sets the associative array of urls for refreshing, updating or deleting
374
	 * think of defining the update zone with the setTargetSelector method
375
	 * @param string|array $urls associative array with keys refresh: for refreshing with search field or pagination, edit : for updating a row, delete: for deleting a row
376
	 * @return DataTable
377
	 */
378
	public function setUrls($urls) {
379
		if(\is_array($urls)){
380
			$this->_urls["refresh"]=JArray::getValue($urls, "refresh",0);
381
			$this->_urls["edit"]=JArray::getValue($urls, "edit",1);
382
			$this->_urls["delete"]=JArray::getValue($urls, "delete",2);
383
			$this->_urls["display"]=JArray::getValue($urls, "display",3);
384
		}else{
385
			$this->_urls=["refresh"=>$urls,"edit"=>$urls,"delete"=>$urls,"display"=>$urls];
386
		}
387
		return $this;
388
	}
389
390
	/**
391
	 * Paginates the DataTable element with a Semantic HtmlPaginationMenu component
392
	 * @param number $page the active page number
393
	 * @param number $total_rowcount the total number of items
394
	 * @param number $items_per_page The number of items per page
395
	 * @param number $pages_visibles The number of visible pages in the Pagination component
396
	 * @return DataTable
397
	 */
398
	public function paginate($page,$total_rowcount,$items_per_page=10,$pages_visibles=null){
399
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page,$total_rowcount);
400
		return $this;
401
	}
402
403
	/**
404
	 * Auto Paginates the DataTable element with a Semantic HtmlPaginationMenu component
405
	 * @param number $page the active page number
406
	 * @param number $items_per_page The number of items per page
407
	 * @param number $pages_visibles The number of visible pages in the Pagination component
408
	 * @return DataTable
409
	 */
410
	public function autoPaginate($page=1,$items_per_page=10,$pages_visibles=4){
411
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page);
412
		return $this;
413
	}
414
415
416
417
	/**
418
	 * @param array $compileParts
419
	 * @return DataTable
420
	 */
421
	public function refresh($compileParts=["tbody"]){
422
		$this->_compileParts=$compileParts;
423
		return $this;
424
	}
425
426
427
	/**
428
	 * Adds a search input in toolbar
429
	 * @param string $position
430
	 * @return \Ajax\common\html\HtmlDoubleElement
431
	 */
432
	public function addSearchInToolbar($position=Direction::RIGHT){
433
		return $this->addInToolbar($this->getSearchField())->setPosition($position);
434
	}
435
436
	public function getSearchField(){
437
		if(isset($this->_searchField)===false){
438
			$this->_searchField=new HtmlInput("search-".$this->identifier,"search","","Search...");
439
			$this->_searchField->addIcon("search",Direction::RIGHT);
440
		}
441
		return $this->_searchField;
442
	}
443
444
	/**
445
	 * The callback function called after the insertion of each row when fromDatabaseObjects is called
446
	 * callback function takes the parameters $row : the row inserted and $object: the instance of model used
447
	 * @param callable $callback
448
	 * @return DataTable
449
	 */
450
	public function onNewRow($callback) {
451
		$this->content["table"]->onNewRow($callback);
452
		return $this;
453
	}
454
455
	/**
456
	 * Returns a form corresponding to the Datatable
457
	 * @return \Ajax\semantic\html\collections\form\HtmlForm
458
	 */
459
	public function asForm(){
460
		return $this->getForm();
461
	}
462
463
464
465
	protected function getTargetSelector($op) {
466
		$result=$this->_targetSelector;
467
		if(!isset($result[$op]))
468
			$result="#".$this->identifier;
469
		return $result[$op];
470
	}
471
472
	/**
473
	 * Sets the response element selector for Edit and Delete request with ajax
474
	 * @param string|array $_targetSelector string or associative array ["edit"=>"edit_selector","delete"=>"delete_selector"]
475
	 * @return DataTable
476
	 */
477
	public function setTargetSelector($_targetSelector) {
478
		if(!\is_array($_targetSelector)){
479
			$_targetSelector=["edit"=>$_targetSelector,"delete"=>$_targetSelector];
480
		}
481
		$this->_targetSelector=$_targetSelector;
482
		return $this;
483
	}
484
485
	public function getRefreshSelector() {
486
		if(isset($this->_refreshSelector))
487
			return $this->_refreshSelector;
488
		return "#".$this->identifier." tbody";
489
	}
490
491
	/**
492
	 * @param string $_refreshSelector
493
	 * @return DataTable
494
	 */
495
	public function setRefreshSelector($_refreshSelector) {
496
		$this->_refreshSelector=$_refreshSelector;
497
		return $this;
498
	}
499
500
	/**
501
	 * {@inheritDoc}
502
	 * @see \Ajax\common\Widget::show()
503
	 */
504
	public function show($modelInstance){
505
		if(\is_array($modelInstance)){
506
			if(isset($modelInstance[0]) && \is_array(array_values($modelInstance)[0]))
507
				$modelInstance=\json_decode(\json_encode($modelInstance), FALSE);
508
		}
509
		$this->_modelInstance=$modelInstance;
510
	}
511
512
	public function getRowClass() {
513
		return $this->_rowClass;
514
	}
515
516
	/**
517
	 * Sets the default row class (tr class)
518
	 * @param string $_rowClass
519
	 * @return DataTable
520
	 */
521
	public function setRowClass($_rowClass) {
522
		$this->_rowClass=$_rowClass;
523
		return $this;
524
	}
525
526
	/**
527
	 * Sets the message displayed when there is no record
528
	 * @param mixed $_emptyMessage
529
	 * @return DataTable
530
	 */
531
	public function setEmptyMessage($_emptyMessage) {
532
		$this->_emptyMessage=$_emptyMessage;
533
		return $this;
534
	}
535
536
	public function setSortable($colIndex=NULL) {
537
		$this->_sortable=$colIndex;
538
		return $this;
539
	}
540
541
	public function setActiveRowSelector($class="active",$event="click",$multiple=false){
542
		$this->_self->setActiveRowSelector($class,$event,$multiple);
543
		return $this;
544
	}
545
546
	public function hideColumn($colIndex){
547
		if(!\is_array($this->_hiddenColumns))
548
			$this->_hiddenColumns=[];
549
		$this->_hiddenColumns[]=$colIndex;
550
		return $this;
551
	}
552
553
	public function setColWidth($colIndex,$width){
554
		$this->_colWidths[$colIndex]=$width;
555
		return $this;
556
	}
557
	public function setColWidths($_colWidths) {
558
		$this->_colWidths = $_colWidths;
559
		return $this;
560
	}
561
562
	public function setColAlignment($colIndex,$alignment){
563
		$this->content["table"]->setColAlignment($colIndex,$alignment);
564
		return $this;
565
	}
566
	
567
	public function trigger($event,$params="[]"){
568
		return $this->getHtmlComponent()->trigger($event,$params);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getHtmlComponent(...rigger($event, $params) targeting Ajax\common\html\BaseHtml::trigger() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
569
	}
570
	
571
	public function onActiveRowChange($jsCode){
572
		$this->getHtmlComponent()->onActiveRowChange($jsCode);
573
		return $this;
574
	}
575
	/**
576
	 * @return mixed
577
	 */
578
	public function getDeleteBehavior() {
579
		return $this->_deleteBehavior;
580
	}
581
582
	/**
583
	 * @return mixed
584
	 */
585
	public function getEditBehavior() {
586
		return $this->_editBehavior;
587
	}
588
589
	/**
590
	 * @return mixed
591
	 */
592
	public function getDisplayBehavior() {
593
		return $this->_displayBehavior;
594
	}
595
	/**
596
	 * @param mixed $_displayBehavior
597
	 */
598
	public function setDisplayBehavior($_displayBehavior) {
599
		$this->_displayBehavior = $_displayBehavior;
600
	}
601
	/**
602
	 * @return mixed
603
	 */
604
	public function getGroupByFields() {
605
		return $this->_instanceViewer->getGroupByFields();
606
	}
607
608
	/**
609
	 * @param mixed $_groupByFields
610
	 */
611
	public function setGroupByFields($_groupByFields) {
612
		$this->_instanceViewer->setGroupByFields($_groupByFields);
613
	}
614
615
616
617
}
618