1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
3 /*
4 * This file is part of The Croco Library
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2.1 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 * USA
19 *
20 * Author: Dodji Seketeli.
21 * See COPYRIGHTS file for copyright information.
22 */
23
24
25 #include <string.h>
26 #include "cr-declaration.h"
27 #include "cr-statement.h"
28 #include "cr-parser.h"
29
30 /**
31 *@CRDeclaration:
32 *
33 *The definition of the #CRDeclaration class.
34 */
35
36 /**
37 * dump:
38 *@a_this: the current instance of #CRDeclaration.
39 *@a_fp: the destination file pointer.
40 *@a_indent: the number of indentation white char.
41 *
42 *Dumps (serializes) one css declaration to a file.
43 */
44 static void
dump(CRDeclaration const * a_this,FILE * a_fp,glong a_indent)45 dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
46 {
47 guchar *str = NULL;
48
49 g_return_if_fail (a_this);
50
51 str = (guchar *) cr_declaration_to_string (a_this, a_indent);
52 if (str) {
53 fprintf (a_fp, "%s", str);
54 g_free (str);
55 str = NULL;
56 }
57 }
58
59 /**
60 * cr_declaration_new:
61 * @a_statement: the statement this declaration belongs to. can be NULL.
62 *@a_property: the property string of the declaration
63 *@a_value: the value expression of the declaration.
64 *Constructor of #CRDeclaration.
65 *
66 *Returns the newly built instance of #CRDeclaration, or NULL in
67 *case of error.
68 *
69 *The returned CRDeclaration takes ownership of @a_property and @a_value.
70 *(E.g. cr_declaration_destroy on this CRDeclaration will also free
71 *@a_property and @a_value.)
72 */
73 CRDeclaration *
cr_declaration_new(CRStatement * a_statement,CRString * a_property,CRTerm * a_value)74 cr_declaration_new (CRStatement * a_statement,
75 CRString * a_property, CRTerm * a_value)
76 {
77 CRDeclaration *result = NULL;
78
79 g_return_val_if_fail (a_property, NULL);
80
81 if (a_statement)
82 g_return_val_if_fail (a_statement
83 && ((a_statement->type == RULESET_STMT)
84 || (a_statement->type
85 == AT_FONT_FACE_RULE_STMT)
86 || (a_statement->type
87 == AT_PAGE_RULE_STMT)), NULL);
88
89 result = g_try_malloc (sizeof (CRDeclaration));
90 if (!result) {
91 cr_utils_trace_info ("Out of memory");
92 return NULL;
93 }
94 memset (result, 0, sizeof (CRDeclaration));
95 result->property = a_property;
96 result->value = a_value;
97
98 if (a_value) {
99 cr_term_ref (a_value);
100 }
101 result->parent_statement = a_statement;
102 return result;
103 }
104
105 /**
106 * cr_declaration_parse_from_buf:
107 *@a_statement: the parent css2 statement of this
108 *this declaration. Must be non NULL and of type
109 *RULESET_STMT (must be a ruleset).
110 *@a_str: the string that contains the statement.
111 *@a_enc: the encoding of a_str.
112 *
113 *Parses a text buffer that contains
114 *a css declaration.
115 *Returns the parsed declaration, or NULL in case of error.
116 */
117 CRDeclaration *
cr_declaration_parse_from_buf(CRStatement * a_statement,const guchar * a_str,enum CREncoding a_enc)118 cr_declaration_parse_from_buf (CRStatement * a_statement,
119 const guchar * a_str, enum CREncoding a_enc)
120 {
121 enum CRStatus status = CR_OK;
122 CRTerm *value = NULL;
123 CRString *property = NULL;
124 CRDeclaration *result = NULL;
125 CRParser *parser = NULL;
126 gboolean important = FALSE;
127
128 g_return_val_if_fail (a_str, NULL);
129 if (a_statement)
130 g_return_val_if_fail (a_statement->type == RULESET_STMT,
131 NULL);
132
133 parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
134 g_return_val_if_fail (parser, NULL);
135
136 status = cr_parser_try_to_skip_spaces_and_comments (parser);
137 if (status != CR_OK)
138 goto cleanup;
139
140 status = cr_parser_parse_declaration (parser, &property,
141 &value, &important);
142 if (status != CR_OK || !property)
143 goto cleanup;
144
145 result = cr_declaration_new (a_statement, property, value);
146 if (result) {
147 property = NULL;
148 value = NULL;
149 result->important = important;
150 }
151
152 cleanup:
153
154 if (parser) {
155 cr_parser_destroy (parser);
156 parser = NULL;
157 }
158
159 if (property) {
160 cr_string_destroy (property);
161 property = NULL;
162 }
163
164 if (value) {
165 cr_term_destroy (value);
166 value = NULL;
167 }
168
169 return result;
170 }
171
172 /**
173 * cr_declaration_parse_list_from_buf:
174 *@a_str: the input buffer that contains the list of declaration to
175 *parse.
176 *@a_enc: the encoding of a_str
177 *
178 *Parses a ';' separated list of properties declaration.
179 *Returns the parsed list of declaration, NULL if parsing failed.
180 */
181 CRDeclaration *
cr_declaration_parse_list_from_buf(const guchar * a_str,enum CREncoding a_enc)182 cr_declaration_parse_list_from_buf (const guchar * a_str,
183 enum CREncoding a_enc)
184 {
185
186 enum CRStatus status = CR_OK;
187 CRTerm *value = NULL;
188 CRString *property = NULL;
189 CRDeclaration *result = NULL,
190 *cur_decl = NULL;
191 CRParser *parser = NULL;
192 CRTknzr *tokenizer = NULL;
193 gboolean important = FALSE;
194
195 g_return_val_if_fail (a_str, NULL);
196
197 parser = cr_parser_new_from_buf ((guchar*)a_str, strlen ((const char *) a_str), a_enc, FALSE);
198 g_return_val_if_fail (parser, NULL);
199 status = cr_parser_get_tknzr (parser, &tokenizer);
200 if (status != CR_OK || !tokenizer) {
201 if (status == CR_OK)
202 status = CR_ERROR;
203 goto cleanup;
204 }
205 status = cr_parser_try_to_skip_spaces_and_comments (parser);
206 if (status != CR_OK)
207 goto cleanup;
208
209 status = cr_parser_parse_declaration (parser, &property,
210 &value, &important);
211 if (status != CR_OK || !property) {
212 if (status != CR_OK)
213 status = CR_ERROR;
214 goto cleanup;
215 }
216 result = cr_declaration_new (NULL, property, value);
217 if (result) {
218 property = NULL;
219 value = NULL;
220 result->important = important;
221 }
222 /*now, go parse the other declarations */
223 for (;;) {
224 guint32 c = 0;
225
226 cr_parser_try_to_skip_spaces_and_comments (parser);
227 status = cr_tknzr_peek_char (tokenizer, &c);
228 if (status != CR_OK) {
229 if (status == CR_END_OF_INPUT_ERROR)
230 status = CR_OK;
231 goto cleanup;
232 }
233 if (c == ';') {
234 status = cr_tknzr_read_char (tokenizer, &c);
235 } else {
236 break;
237 }
238 important = FALSE;
239 cr_parser_try_to_skip_spaces_and_comments (parser);
240 status = cr_parser_parse_declaration (parser, &property,
241 &value, &important);
242 if (status != CR_OK || !property) {
243 if (status == CR_END_OF_INPUT_ERROR) {
244 status = CR_OK;
245 }
246 break;
247 }
248 cur_decl = cr_declaration_new (NULL, property, value);
249 if (cur_decl) {
250 cur_decl->important = important;
251 result = cr_declaration_append (result, cur_decl);
252 property = NULL;
253 value = NULL;
254 cur_decl = NULL;
255 } else {
256 break;
257 }
258 }
259
260 cleanup:
261
262 if (parser) {
263 cr_parser_destroy (parser);
264 parser = NULL;
265 }
266
267 if (property) {
268 cr_string_destroy (property);
269 property = NULL;
270 }
271
272 if (value) {
273 cr_term_destroy (value);
274 value = NULL;
275 }
276
277 if (status != CR_OK && result) {
278 cr_declaration_destroy (result);
279 result = NULL;
280 }
281 return result;
282 }
283
284 /**
285 * cr_declaration_append:
286 *@a_this: the current declaration list.
287 *@a_new: the declaration to append.
288 *
289 *Appends a new declaration to the current declarations list.
290 *Returns the declaration list with a_new appended to it, or NULL
291 *in case of error.
292 */
293 CRDeclaration *
cr_declaration_append(CRDeclaration * a_this,CRDeclaration * a_new)294 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
295 {
296 CRDeclaration *cur = NULL;
297
298 g_return_val_if_fail (a_new, NULL);
299
300 if (!a_this)
301 return a_new;
302
303 for (cur = a_this; cur && cur->next; cur = cur->next) ;
304
305 cur->next = a_new;
306 a_new->prev = cur;
307
308 return a_this;
309 }
310
311 /**
312 * cr_declaration_unlink:
313 *@a_decls: the declaration to unlink.
314 *
315 *Unlinks the declaration from the declaration list.
316 *case of a successful completion, NULL otherwise.
317 *
318 *Returns a pointer to the unlinked declaration in
319 */
320 CRDeclaration *
cr_declaration_unlink(CRDeclaration * a_decl)321 cr_declaration_unlink (CRDeclaration * a_decl)
322 {
323 CRDeclaration *result = a_decl;
324
325 g_return_val_if_fail (result, NULL);
326
327 /*
328 *some sanity checks first
329 */
330 if (a_decl->prev) {
331 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
332
333 }
334 if (a_decl->next) {
335 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
336 }
337
338 /*
339 *now, the real unlinking job.
340 */
341 if (a_decl->prev) {
342 a_decl->prev->next = a_decl->next;
343 }
344 if (a_decl->next) {
345 a_decl->next->prev = a_decl->prev;
346 }
347 if (a_decl->parent_statement) {
348 CRDeclaration **children_decl_ptr = NULL;
349
350 switch (a_decl->parent_statement->type) {
351 case RULESET_STMT:
352 if (a_decl->parent_statement->kind.ruleset) {
353 children_decl_ptr =
354 &a_decl->parent_statement->
355 kind.ruleset->decl_list;
356 }
357
358 break;
359
360 case AT_FONT_FACE_RULE_STMT:
361 if (a_decl->parent_statement->kind.font_face_rule) {
362 children_decl_ptr =
363 &a_decl->parent_statement->
364 kind.font_face_rule->decl_list;
365 }
366 break;
367 case AT_PAGE_RULE_STMT:
368 if (a_decl->parent_statement->kind.page_rule) {
369 children_decl_ptr =
370 &a_decl->parent_statement->
371 kind.page_rule->decl_list;
372 }
373
374 default:
375 break;
376 }
377 if (children_decl_ptr
378 && *children_decl_ptr && *children_decl_ptr == a_decl)
379 *children_decl_ptr = (*children_decl_ptr)->next;
380 }
381
382 a_decl->next = NULL;
383 a_decl->prev = NULL;
384 a_decl->parent_statement = NULL;
385
386 return result;
387 }
388
389 /**
390 * cr_declaration_prepend:
391 * @a_this: the current declaration list.
392 * @a_new: the declaration to prepend.
393 *
394 * prepends a declaration to the current declaration list.
395 *
396 * Returns the list with a_new prepended or NULL in case of error.
397 */
398 CRDeclaration *
cr_declaration_prepend(CRDeclaration * a_this,CRDeclaration * a_new)399 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
400 {
401 CRDeclaration *cur = NULL;
402
403 g_return_val_if_fail (a_new, NULL);
404
405 if (!a_this)
406 return a_new;
407
408 a_this->prev = a_new;
409 a_new->next = a_this;
410
411 for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
412
413 return cur;
414 }
415
416 /**
417 * cr_declaration_append2:
418 *@a_this: the current declaration list.
419 *@a_prop: the property string of the declaration to append.
420 *@a_value: the value of the declaration to append.
421 *
422 *Appends a declaration to the current declaration list.
423 *Returns the list with the new property appended to it, or NULL in
424 *case of an error.
425 */
426 CRDeclaration *
cr_declaration_append2(CRDeclaration * a_this,CRString * a_prop,CRTerm * a_value)427 cr_declaration_append2 (CRDeclaration * a_this,
428 CRString * a_prop, CRTerm * a_value)
429 {
430 CRDeclaration *new_elem = NULL;
431
432 if (a_this) {
433 new_elem = cr_declaration_new (a_this->parent_statement,
434 a_prop, a_value);
435 } else {
436 new_elem = cr_declaration_new (NULL, a_prop, a_value);
437 }
438
439 g_return_val_if_fail (new_elem, NULL);
440
441 return cr_declaration_append (a_this, new_elem);
442 }
443
444 /**
445 * cr_declaration_dump:
446 *@a_this: the current instance of #CRDeclaration.
447 *@a_fp: the destination file.
448 *@a_indent: the number of indentation white char.
449 *@a_one_per_line: whether to put one declaration per line of not .
450 *
451 *
452 *Dumps a declaration list to a file.
453 */
454 void
cr_declaration_dump(CRDeclaration const * a_this,FILE * a_fp,glong a_indent,gboolean a_one_per_line)455 cr_declaration_dump (CRDeclaration const * a_this, FILE * a_fp, glong a_indent,
456 gboolean a_one_per_line)
457 {
458 CRDeclaration const *cur = NULL;
459
460 g_return_if_fail (a_this);
461
462 for (cur = a_this; cur; cur = cur->next) {
463 if (cur->prev) {
464 if (a_one_per_line == TRUE)
465 fprintf (a_fp, ";\n");
466 else
467 fprintf (a_fp, "; ");
468 }
469 dump (cur, a_fp, a_indent);
470 }
471 }
472
473 /**
474 * cr_declaration_dump_one:
475 *@a_this: the current instance of #CRDeclaration.
476 *@a_fp: the destination file.
477 *@a_indent: the number of indentation white char.
478 *
479 *Dumps the first declaration of the declaration list to a file.
480 */
481 void
cr_declaration_dump_one(CRDeclaration const * a_this,FILE * a_fp,glong a_indent)482 cr_declaration_dump_one (CRDeclaration const * a_this, FILE * a_fp, glong a_indent)
483 {
484 g_return_if_fail (a_this);
485
486 dump (a_this, a_fp, a_indent);
487 }
488
489 /**
490 * cr_declaration_to_string:
491 *@a_this: the current instance of #CRDeclaration.
492 *@a_indent: the number of indentation white char
493 *to put before the actual serialisation.
494 *
495 *Serializes the declaration into a string
496 *Returns the serialized form the declaration. The caller must
497 *free the string using g_free().
498 */
499 gchar *
cr_declaration_to_string(CRDeclaration const * a_this,gulong a_indent)500 cr_declaration_to_string (CRDeclaration const * a_this, gulong a_indent)
501 {
502 GString *stringue = NULL;
503
504 gchar *str = NULL,
505 *result = NULL;
506
507 g_return_val_if_fail (a_this, NULL);
508
509 stringue = g_string_new (NULL);
510
511 if (a_this->property
512 && a_this->property->stryng
513 && a_this->property->stryng->str) {
514 str = g_strndup (a_this->property->stryng->str,
515 a_this->property->stryng->len);
516 if (str) {
517 cr_utils_dump_n_chars2 (' ', stringue,
518 a_indent);
519 g_string_append (stringue, str);
520 g_free (str);
521 str = NULL;
522 } else
523 goto error;
524
525 if (a_this->value) {
526 guchar *value_str = NULL;
527
528 value_str = cr_term_to_string (a_this->value);
529 if (value_str) {
530 g_string_append_printf (stringue, " : %s",
531 value_str);
532 g_free (value_str);
533 } else
534 goto error;
535 }
536 if (a_this->important == TRUE) {
537 g_string_append_printf (stringue, " %s",
538 "!important");
539 }
540 }
541 if (stringue && stringue->str) {
542 result = stringue->str;
543 g_string_free (stringue, FALSE);
544 }
545 return result;
546
547 error:
548 if (stringue) {
549 g_string_free (stringue, TRUE);
550 stringue = NULL;
551 }
552 if (str) {
553 g_free (str);
554 str = NULL;
555 }
556
557 return result;
558 }
559
560 /**
561 * cr_declaration_list_to_string:
562 *@a_this: the current instance of #CRDeclaration.
563 *@a_indent: the number of indentation white char
564 *to put before the actual serialisation.
565 *
566 *Serializes the declaration list into a string
567 */
568 guchar *
cr_declaration_list_to_string(CRDeclaration const * a_this,gulong a_indent)569 cr_declaration_list_to_string (CRDeclaration const * a_this, gulong a_indent)
570 {
571 CRDeclaration const *cur = NULL;
572 GString *stringue = NULL;
573 guchar *str = NULL,
574 *result = NULL;
575
576 g_return_val_if_fail (a_this, NULL);
577
578 stringue = g_string_new (NULL);
579
580 for (cur = a_this; cur; cur = cur->next) {
581 str = (guchar *) cr_declaration_to_string (cur, a_indent);
582 if (str) {
583 g_string_append_printf (stringue, "%s;", str);
584 g_free (str);
585 } else
586 break;
587 }
588 if (stringue && stringue->str) {
589 result = (guchar *) stringue->str;
590 g_string_free (stringue, FALSE);
591 }
592
593 return result;
594 }
595
596 /**
597 * cr_declaration_list_to_string2:
598 *@a_this: the current instance of #CRDeclaration.
599 *@a_indent: the number of indentation white char
600 *@a_one_decl_per_line: whether to output one doc per line or not.
601 *to put before the actual serialisation.
602 *
603 *Serializes the declaration list into a string
604 *Returns the serialized form the declararation.
605 */
606 guchar *
cr_declaration_list_to_string2(CRDeclaration const * a_this,gulong a_indent,gboolean a_one_decl_per_line)607 cr_declaration_list_to_string2 (CRDeclaration const * a_this,
608 gulong a_indent, gboolean a_one_decl_per_line)
609 {
610 CRDeclaration const *cur = NULL;
611 GString *stringue = NULL;
612 guchar *str = NULL,
613 *result = NULL;
614
615 g_return_val_if_fail (a_this, NULL);
616
617 stringue = g_string_new (NULL);
618
619 for (cur = a_this; cur; cur = cur->next) {
620 str = (guchar *) cr_declaration_to_string (cur, a_indent);
621 if (str) {
622 if (a_one_decl_per_line == TRUE) {
623 if (cur->next)
624 g_string_append_printf (stringue,
625 "%s;\n", str);
626 else
627 g_string_append (stringue,
628 (const gchar *) str);
629 } else {
630 if (cur->next)
631 g_string_append_printf (stringue,
632 "%s;", str);
633 else
634 g_string_append (stringue,
635 (const gchar *) str);
636 }
637 g_free (str);
638 } else
639 break;
640 }
641 if (stringue && stringue->str) {
642 result = (guchar *) stringue->str;
643 g_string_free (stringue, FALSE);
644 }
645
646 return result;
647 }
648
649 /**
650 * cr_declaration_nr_props:
651 *@a_this: the current instance of #CRDeclaration.
652 *Return the number of properties in the declaration
653 */
654 gint
cr_declaration_nr_props(CRDeclaration const * a_this)655 cr_declaration_nr_props (CRDeclaration const * a_this)
656 {
657 CRDeclaration const *cur = NULL;
658 int nr = 0;
659
660 g_return_val_if_fail (a_this, -1);
661
662 for (cur = a_this; cur; cur = cur->next)
663 nr++;
664 return nr;
665 }
666
667 /**
668 * cr_declaration_get_from_list:
669 *@a_this: the current instance of #CRDeclaration.
670 *@itemnr: the index into the declaration list.
671 *
672 *Use an index to get a CRDeclaration from the declaration list.
673 *
674 *Returns #CRDeclaration at position itemnr,
675 *if itemnr > number of declarations - 1,
676 *it will return NULL.
677 */
678 CRDeclaration *
cr_declaration_get_from_list(CRDeclaration * a_this,int itemnr)679 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
680 {
681 CRDeclaration *cur = NULL;
682 int nr = 0;
683
684 g_return_val_if_fail (a_this, NULL);
685
686 for (cur = a_this; cur; cur = cur->next)
687 if (nr++ == itemnr)
688 return cur;
689 return NULL;
690 }
691
692 /**
693 * cr_declaration_get_by_prop_name:
694 *@a_this: the current instance of #CRDeclaration.
695 *@a_prop: the property name to search for.
696 *
697 *Use property name to get a CRDeclaration from the declaration list.
698 *Returns #CRDeclaration with property name a_prop, or NULL if not found.
699 */
700 CRDeclaration *
cr_declaration_get_by_prop_name(CRDeclaration * a_this,const guchar * a_prop)701 cr_declaration_get_by_prop_name (CRDeclaration * a_this,
702 const guchar * a_prop)
703 {
704 CRDeclaration *cur = NULL;
705
706 g_return_val_if_fail (a_this, NULL);
707 g_return_val_if_fail (a_prop, NULL);
708
709 for (cur = a_this; cur; cur = cur->next) {
710 if (cur->property
711 && cur->property->stryng
712 && cur->property->stryng->str) {
713 if (!strcmp (cur->property->stryng->str,
714 (const char *) a_prop)) {
715 return cur;
716 }
717 }
718 }
719 return NULL;
720 }
721
722 /**
723 * cr_declaration_ref:
724 *@a_this: the current instance of #CRDeclaration.
725 *
726 *Increases the ref count of the current instance of #CRDeclaration.
727 */
728 void
cr_declaration_ref(CRDeclaration * a_this)729 cr_declaration_ref (CRDeclaration * a_this)
730 {
731 g_return_if_fail (a_this);
732
733 a_this->ref_count++;
734 }
735
736 /**
737 * cr_declaration_unref:
738 *@a_this: the current instance of #CRDeclaration.
739 *
740 *Decrements the ref count of the current instance of #CRDeclaration.
741 *If the ref count reaches zero, the current instance of #CRDeclaration
742 *if destroyed.
743 *Returns TRUE if @a_this was destroyed (ref count reached zero),
744 *FALSE otherwise.
745 */
746 gboolean
cr_declaration_unref(CRDeclaration * a_this)747 cr_declaration_unref (CRDeclaration * a_this)
748 {
749 g_return_val_if_fail (a_this, FALSE);
750
751 if (a_this->ref_count) {
752 a_this->ref_count--;
753 }
754
755 if (a_this->ref_count == 0) {
756 cr_declaration_destroy (a_this);
757 return TRUE;
758 }
759 return FALSE;
760 }
761
762 /**
763 * cr_declaration_destroy:
764 *@a_this: the current instance of #CRDeclaration.
765 *
766 *Destructor of the declaration list.
767 */
768 void
cr_declaration_destroy(CRDeclaration * a_this)769 cr_declaration_destroy (CRDeclaration * a_this)
770 {
771 CRDeclaration *cur = NULL;
772
773 g_return_if_fail (a_this);
774
775 /*
776 * Go to the last element of the list.
777 */
778 for (cur = a_this; cur->next; cur = cur->next)
779 g_assert (cur->next->prev == cur);
780
781 /*
782 * Walk backward the list and free each "next" element.
783 * Meanwhile, free each property/value pair contained in the list.
784 */
785 for (; cur; cur = cur->prev) {
786 g_free (cur->next);
787 cur->next = NULL;
788
789 if (cur->property) {
790 cr_string_destroy (cur->property);
791 cur->property = NULL;
792 }
793
794 if (cur->value) {
795 cr_term_destroy (cur->value);
796 cur->value = NULL;
797 }
798 }
799
800 g_free (a_this);
801 }
802