1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>R8 Configuration Analyzer</title>
7 <style>/* ==========================================================================
8 1. VARIABLES & THEME CONFIGURATION
9 ========================================================================== */
10 :root {
11 /* --- Colors: Backgrounds --- */
12 --bg-body: #f8fafc;
13 /* --- Colors: Borders --- */
14 --border-color: #e2e8f0;
15 /* --- Colors: Functional (Status) --- */
16 --text-green-600: #166534;
17 /* --- Colors: Palette (Extended) --- */
18 --text-blue-600: #1d4ed8;
19 /* --- Colors: Text --- */
20 --text-main: #0f172a;
21 /* --- Shadows --- */
22 --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
23 /* --- Typography --- */
24 --font-family-base:
25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
26 sans-serif;
27 --bg-active: #e5e7eb;
28 --bg-card: #ffffff;
29 --bg-green-light: #dcfce7;
30 --bg-header: #ffffff;
31 --bg-hover: #f3f4f6;
32 --bg-red-light: #fee2e2;
33 --bg-selected: #eff6ff;
34 --bg-subtle: #f9fafb;
35 --bg-tooltip: #1f2937;
36 --bg-yellow-light: #fef9c3;
37 --border-interactive: #d1d5db;
38 --border-light: #f1f5f9;
39 --font-family-mono:
40 "SF Mono", "Consolas", "Liberation Mono", Menlo, monospace;
41 --text-muted: #59697c;
42 --shadow-lg:
43 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
44 --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
45 --text-blue-700: #1d4ed8;
46 --text-gray-400: #5f6368;
47 --text-gray-500: #5f6368;
48 --text-gray-900: #111827;
49 --text-inverse: #ffffff;
50 --text-link-hover: #1e40af;
51 --text-link: #1d4ed8;
52 --text-orange-600: #c2410c;
53 --text-red-600: #b91c1c;
54 --text-subtle: #475569;
55 --text-yellow-600: #b06000;
56 }
57
58 /* ==========================================================================
59 2. RESET & BASE STYLES
60 ========================================================================== */
61 * {
62 box-sizing: border-box;
63 margin: 0;
64 padding: 0;
65 scrollbar-color: var(--border-color) transparent;
66 scrollbar-width: thin;
67 }
68
69 /* Custom Scrollbar */
70 ::-webkit-scrollbar {
71 height: 8px;
72 width: 8px;
73 }
74
75 ::-webkit-scrollbar-track {
76 background: transparent;
77 }
78
79 ::-webkit-scrollbar-thumb {
80 background-color: #cbd5e1;
81 border-radius: 4px;
82 }
83
84 ::-webkit-scrollbar-thumb:hover {
85 background-color: #94a3b8;
86 }
87
88 html,
89 body {
90 height: 100%;
91 overflow: hidden;
92 }
93
94 body {
95 background-color: var(--bg-body);
96 color: var(--text-main);
97 display: flex;
98 flex-direction: column;
99 font-family: var(--font-family-base);
100 -webkit-font-smoothing: antialiased;
101 line-height: 1.5;
102 }
103
104 a {
105 color: inherit;
106 text-decoration: none;
107 }
108
109 input {
110 font-family: inherit;
111 outline: none;
112 }
113
114 button {
115 background: none;
116 border: none;
117 cursor: pointer;
118 font-family: inherit;
119 outline: none;
120 }
121
122 /* ==========================================================================
123 3. UTILITY CLASSES
124 ========================================================================== */
125 .hidden {
126 display: none !important;
127 }
128
129 .flex {
130 display: flex;
131 }
132
133 .flex-col {
134 flex-direction: column;
135 }
136
137 .flex-1 {
138 flex: 1 1 0%;
139 }
140
141 .items-center {
142 align-items: center;
143 }
144
145 .justify-between {
146 justify-content: space-between;
147 }
148
149 .justify-center {
150 justify-content: center;
151 }
152
153 .relative {
154 position: relative;
155 }
156
157 .text-center {
158 text-align: center;
159 }
160
161 .text-left {
162 text-align: left;
163 }
164
165 .font-bold {
166 font-weight: 700;
167 }
168
169 .font-medium {
170 font-weight: 500;
171 }
172
173 .text-sm {
174 font-size: 0.875rem;
175 }
176
177 .text-xs {
178 font-size: 0.75rem;
179 }
180
181 .truncate {
182 overflow: hidden;
183 text-overflow: ellipsis;
184 white-space: nowrap;
185 }
186
187 .cursor-pointer {
188 cursor: pointer;
189 }
190
191 /* Colors */
192 .text-blue-600 {
193 color: var(--text-blue-600);
194 }
195
196 .text-green-600 {
197 color: var(--text-green-600);
198 }
199
200 .text-yellow-600 {
201 color: var(--text-yellow-600);
202 }
203
204 .text-orange-600 {
205 color: var(--text-orange-600);
206 }
207
208 .text-red-600 {
209 color: var(--text-red-600);
210 }
211
212 .text-gray-400 {
213 color: var(--text-gray-400);
214 }
215
216 .text-gray-500 {
217 color: var(--text-gray-500);
218 }
219
220 /* Backgrounds & Interaction */
221 .hover-bg-gray-100:hover {
222 background-color: var(--bg-hover) !important;
223 }
224
225 .bg-gray-200 {
226 background-color: var(--bg-active) !important;
227 color: var(--text-gray-900) !important;
228 font-weight: 500;
229 }
230
231 /* ==========================================================================
232 4. LAYOUT & CONTAINER
233 ========================================================================== */
234 header {
235 background-color: var(--bg-header);
236 border-bottom: 1px solid var(--border-color);
237 box-shadow: var(--shadow-sm);
238 flex-shrink: 0;
239 z-index: 50;
240 }
241
242 .container-custom {
243 display: flex;
244 flex-direction: column;
245 flex: 1;
246 margin: 0 auto;
247 max-width: 1800px;
248 min-height: 0;
249 overflow: visible;
250 padding: 1.5rem;
251 width: 100%;
252 }
253
254 main {
255 display: flex;
256 flex-direction: column;
257 flex: 1;
258 min-height: 0;
259 overflow: visible;
260 }
261
262 /* ==========================================================================
263 5. HEADER COMPONENTS
264 ========================================================================== */
265 .header-top-row {
266 align-items: center;
267 border-bottom: 1px solid var(--border-light);
268 display: flex;
269 justify-content: space-between;
270 margin: 0 auto;
271 max-width: 1800px;
272 padding: 0.5rem 1.5rem;
273 }
274
275 .header-branding {
276 /* border-right: 1px solid var(--border-color); */
277 display: flex;
278 flex-direction: column;
279 padding-right: 1rem;
280 }
281
282 .header-branding .header-title {
283 color: var(--text-main);
284 font-size: 1.25rem;
285 font-weight: 700;
286 letter-spacing: -0.025em;
287 }
288
289 p#report-date {
290 font-size: 0.8125rem;
291 color: var(--text-subtle);
292 margin-top: 0.125rem;
293 }
294
295 #ts-all-state {
296 align-items: center;
297 display: flex;
298 gap: 0.2rem;
299 justify-content: center;
300 }
301
302 .header-stats {
303 align-items: center;
304 display: flex;
305 }
306
307 .stat-item {
308 align-items: flex-start;
309 display: flex;
310 flex-direction: column;
311 margin-right: 2rem;
312 }
313
314 .header-stats .stat-item:nth-child(3) {
315 margin-right: 0.5rem;
316 }
317
318 .stat-label {
319 color: var(--text-muted);
320 font-size: 0.7rem;
321 font-weight: 500;
322 text-transform: uppercase;
323 }
324
325 .stat-value {
326 font-size: 0.95rem;
327 font-weight: 700;
328 }
329
330 .header-toolbar {
331 background-color: #ffffff;
332 }
333
334 .header-toolbar-inner {
335 align-items: center;
336 display: flex;
337 gap: 1rem;
338 height: 46px;
339 justify-content: space-between;
340 margin: 0 auto;
341 max-width: 1800px;
342 padding: 0 1.5rem;
343 width: 100%;
344 }
345
346 /* ==========================================================================
347 6. INTERACTIVE COMPONENTS (Buttons, Inputs, Chips)
348 ========================================================================== */
349 /* Icon Buttons */
350 .icon-btn {
351 background: transparent;
352 border-radius: 0.375rem;
353 border: 1px solid transparent;
354 color: var(--text-subtle);
355 cursor: pointer;
356 display: flex;
357 height: 32px;
358 align-items: center;
359 justify-content: center;
360 transition: all 0.2s;
361 width: 32px;
362 color: var(--text-subtle);
363 }
364
365 button#group-by-btn:hover,
366 .icon-btn:hover,
367 .icon-btn.active {
368 background-color: var(--bg-hover) !important;
369 color: var(--text-main) !important;
370 }
371
372 /* Filter Chips */
373 .filter-chip {
374 /* blue-50 */
375 color: #1e40af;
376 /* blue-800 */
377 border: 1px solid #dbeafe;
378 /* blue-100 */
379 border-radius: 9999px;
380 align-items: center;
381 background-color: #eff6ff;
382 cursor: pointer;
383 display: inline-flex;
384 font-size: 0.8125rem;
385 font-weight: 500;
386 gap: 0.375rem;
387 padding: 0.25rem 0.5rem;
388 transition: all 0.2s;
389 white-space: nowrap;
390 }
391
392 .filter-chip:hover {
393 background-color: #dbeafe;
394 }
395
396 .filter-chip.active {
397 box-shadow: 0 0 0 2px #2563eb;
398 }
399
400 .chip-close {
401 border-radius: 9999px;
402 color: #2563eb;
403 display: inline-flex;
404 margin-left: 0.25rem;
405 margin-right: -0.25rem;
406 padding: 0.125rem;
407 transition:
408 background-color 0.2s,
409 color 0.2s;
410 }
411
412 .chip-close:hover {
413 background-color: #bfdbfe;
414 color: #1e40af;
415 }
416
417 .filter-icon {
418 align-items: center;
419 color: var(--text-gray-500);
420 display: flex;
421 justify-content: center;
422 }
423
424 /* Add Filter Button */
425 .add-filter-btn {
426 align-items: center;
427 background: transparent;
428 border-radius: 0.375rem;
429 border: 1px dashed var(--border-interactive);
430 color: var(--text-gray-500);
431 cursor: pointer;
432 display: flex;
433 font-size: 0.8125rem;
434 font-weight: 500;
435 gap: 0.25rem;
436 padding: 0.25rem 0.5rem;
437 transition: all 0.2s;
438 }
439
440 .add-filter-btn:hover {
441 background-color: var(--bg-hover);
442 border-color: var(--text-gray-500);
443 color: var(--text-gray-900);
444 }
445
446 /* Segmented Controls */
447 .segmented-control {
448 background-color: var(--bg-hover);
449 border-radius: 0.375rem;
450 display: flex;
451 padding: 2px;
452 }
453
454 .segment-btn {
455 align-items: center;
456 background-color: transparent;
457 border-radius: 0.25rem;
458 border: none;
459 color: var(--text-gray-500);
460 cursor: pointer;
461 display: flex;
462 font-size: 0.8125rem;
463 font-weight: 500;
464 height: 24px;
465 justify-content: center;
466 padding: 0 1rem;
467 transition: all 0.2s ease;
468 }
469
470 .segment-btn:hover:not(.active) {
471 color: var(--text-gray-900);
472 }
473
474 .segment-btn.active {
475 background-color: #ffffff;
476 box-shadow: var(--shadow-sm);
477 color: var(--text-link);
478 }
479
480 /* Search Bar */
481 .search-bar-container {
482 display: flex;
483 align-items: center;
484 justify-content: center;
485 width: 32px;
486 height: 32px;
487 border-radius: 0.375rem;
488 border: 1px solid transparent;
489 background: transparent;
490 color: var(--text-subtle);
491 transition: all 0.2s;
492 cursor: pointer;
493 }
494
495 .search-bar-container:hover {
496 background-color: var(--bg-hover);
497 color: var(--text-main);
498 }
499
500 .search-bar-container.active {
501 border-color: var(--border-interactive);
502 background-color: #ffffff;
503 padding: 0.25rem 0.5rem;
504 width: auto;
505 height: auto;
506 }
507
508 .search-bar-input {
509 border: none;
510 outline: none;
511 padding: 0;
512 font-size: 0.8125rem;
513 width: 0;
514 transition:
515 width 0.2s,
516 padding 0.2s;
517 background: transparent;
518 }
519
520 .search-bar-container.active .search-bar-input {
521 width: 150px;
522 padding: 0.25rem 0.5rem;
523 }
524
525 .search-bar-icon {
526 color: var(--text-gray-500);
527 width: 16px;
528 height: 16px;
529 display: flex;
530 align-items: center;
531 justify-content: center;
532 }
533
534 /* Dropdowns */
535 .dropdown-menu {
536 /* Changed from hidden to allow submenus */
537 animation: fadeIn 0.1s ease-out;
538 background: var(--bg-card);
539 border-radius: 0.5rem;
540 border: 1px solid var(--border-color);
541 box-shadow: var(--shadow-lg);
542 left: 0;
543 margin-top: 0.5rem;
544 min-width: 180px;
545 overflow: visible;
546 position: absolute;
547 top: 100%;
548 z-index: 100;
549 }
550
551 .dropdown-menu.right-0 {
552 left: auto;
553 right: 0;
554 }
555
556 .dropdown-menu:not(.hidden) {
557 display: block;
558 }
559
560 /* Ensure visibility toggling works */
561 .dropdown-item {
562 color: var(--text-gray-900);
563 cursor: pointer;
564 display: block;
565 font-size: 0.875rem;
566 padding: 0.5rem 1rem;
567 transition: background-color 0.15s;
568 }
569
570 .dropdown-item:hover {
571 background-color: var(--bg-hover);
572 }
573
574 /* Popover Content */
575 .popover-search-container {
576 background: var(--bg-card);
577 border-bottom: 1px solid var(--border-color);
578 padding: 0.5rem;
579 position: sticky;
580 top: 0;
581 z-index: 2;
582 }
583
584 .popover-search {
585 border-radius: 0.375rem;
586 border: 1px solid var(--border-interactive);
587 font-size: 0.8125rem;
588 padding: 0.375rem 0.5rem;
589 width: 100%;
590 }
591
592 .popover-search:focus {
593 border-color: var(--text-link);
594 }
595
596 .popover-list {
597 max-height: 250px;
598 overflow-y: auto;
599 padding: 0.25rem 0;
600 }
601
602 .popover-item {
603 align-items: center;
604 color: var(--text-gray-900);
605 cursor: pointer;
606 display: flex;
607 font-size: 0.875rem;
608 gap: 0.5rem;
609 padding: 0.375rem 1rem;
610 transition: background-color 0.15s;
611 }
612
613 .popover-item:hover {
614 background-color: var(--bg-hover);
615 }
616
617 .active-item {
618 background-color: var(--bg-selected);
619 color: var(--text-link);
620 font-weight: 600;
621 }
622
623 .popover-checkbox {
624 accent-color: var(--text-link);
625 height: 1rem;
626 margin-top: 1px;
627 width: 1rem;
628 }
629
630 /* Action-Based Dropdown Styles */
631 .dropdown-action-zone {
632 align-items: center;
633 background-color: var(--bg-subtle);
634 border-bottom: 1px solid var(--border-color);
635 display: flex;
636 justify-content: space-between;
637 padding: 0.5rem 1rem;
638 position: sticky;
639 top: 0;
640 z-index: 10;
641 }
642
643 .dropdown-action-btn {
644 background: none;
645 border: none;
646 color: var(--text-link);
647 cursor: pointer;
648 font-size: 0.8125rem;
649 font-weight: 600;
650 padding: 0;
651 }
652
653 .dropdown-action-btn:hover {
654 color: var(--text-link-hover);
655 text-decoration: underline;
656 }
657
658 .dropdown-scroll-zone {
659 max-height: 250px;
660 overflow-y: auto;
661 padding: 0.25rem 0;
662 }
663
664 /* ==========================================================================
665 7. BREADCRUMBS
666 ========================================================================== */
667 .breadcrumb-row {
668 align-items: center;
669 display: flex;
670 height: 28px;
671 justify-content: space-between;
672 margin-bottom: 1rem;
673 }
674
675 .breadcrumb-pill {
676 border-radius: 0.375rem;
677 color: #4b5563;
678 cursor: pointer;
679 font-size: 0.875rem;
680 font-weight: 500;
681 padding: 0.25rem 0.5rem;
682 transition: all 0.2s;
683 }
684
685 .breadcrumb-pill:hover {
686 background-color: var(--bg-selected);
687 border-color: var(--text-link);
688 color: var(--text-link-hover);
689 }
690
691 .breadcrumb-text {
692 color: var(--text-gray-900);
693 font-size: 0.875rem;
694 font-weight: 600;
695 padding: 0.25rem 0.5rem;
696 }
697
698 div#flat-breadcrumbs .mx-1 {
699 opacity: 0.2;
700 }
701
702 /* ==========================================================================
703 8. TABLES & DATA GRID
704 ========================================================================== */
705 .table-container {
706 background: var(--bg-card);
707 border-radius: 0.5rem;
708 border: 1px solid var(--border-color);
709 box-shadow: var(--shadow-sm);
710 flex: 1;
711 overflow-x: auto;
712 }
713
714 table {
715 border-collapse: collapse;
716 min-width: 800px;
717 text-align: left;
718 width: 100%;
719 }
720
721 thead {
722 background: #f8fafc;
723 position: sticky;
724 top: 0;
725 z-index: 20;
726 }
727
728 th {
729 border-bottom: 1px solid var(--border-color);
730 color: var(--text-subtle);
731 font-size: 0.75rem;
732 font-weight: 600;
733 letter-spacing: 0.05em;
734 padding: 0.75rem 1rem;
735 text-transform: uppercase;
736 white-space: nowrap;
737 }
738
739 td {
740 border-bottom: 1px solid var(--border-color);
741 color: var(--text-main);
742 font-size: 0.875rem;
743 padding: 0.75rem 1rem;
744 transition: padding 0.2s ease;
745 white-space: nowrap;
746 }
747
748 tr {
749 background: var(--bg-card);
750 transition: background 0.2s;
751 }
752
753 tbody tr:hover {
754 background-color: var(--bg-selected) !important;
755 }
756
757 /* Sticky Columns (Name) */
758 .sticky-name {
759 background: inherit;
760 border-right: 1px solid var(--border-color);
761 box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.05);
762 left: 0;
763 max-width: 400px;
764 position: sticky;
765 white-space: normal;
766 width: 400px;
767 word-break: break-all;
768 z-index: 10;
769 }
770
771 thead th.sticky-name {
772 z-index: 30;
773 }
774
775 th.truncate,
776 td.truncate {
777 border-right: 1px solid var(--border-color);
778 max-width: 150px !important;
779 }
780
781 td.truncate span {
782 overflow: hidden;
783 text-overflow: ellipsis;
784 white-space: nowrap;
785 }
786
787 @keyframes fadeIn {
788 from {
789 opacity: 0;
790 transform: translateY(-4px);
791 }
792
793 to {
794 opacity: 1;
795 transform: translateY(0);
796 }
797 }
798
799 @keyframes blink-animation {
800 0% {
801 background-color: rgba(253, 224, 71, 0.5);
802 }
803
804 50% {
805 background-color: rgba(253, 224, 71, 0.1);
806 }
807
808 100% {
809 background-color: transparent;
810 }
811 }
812
813 /* CSS Tooltip */
814 [data-tooltip] {
815 position: relative;
816 }
817
818 [data-tooltip]:hover::after,
819 [data-tooltip]:focus::after,
820 [data-tooltip]:focus-visible::after {
821 animation: 0.15s ease-out;
822 background-color: var(--bg-tooltip);
823 border-radius: 0.25rem;
824 top: 150%;
825 box-shadow: var(--shadow-md);
826 color: var(--text-inverse);
827 content: attr(data-tooltip);
828 font-size: 0.75rem;
829 left: 50%;
830 padding: 0.25rem 0.5rem;
831 pointer-events: none;
832 position: absolute;
833 transform: translateX(-50%);
834 white-space: normal;
835 z-index: 1000;
836 width: 260px;
837 text-transform: none;
838 font-size: 12px;
839 font-weight: 300;
840 }
841
842 /* Impact Tags */
843 .impact-container {
844 display: flex;
845 flex-wrap: wrap;
846 gap: 0.5rem;
847 }
848
849 .impact-tag {
850 background-color: var(--bg-hover);
851 border-radius: 0.25rem;
852 border: 1px solid var(--border-color);
853 color: var(--text-gray-900);
854 font-size: 0.7rem;
855 font-weight: 600;
856 letter-spacing: 0.025em;
857 padding: 0.125rem 0.375rem;
858 text-transform: uppercase;
859 }
860
861 #report-view .breadcrumb-row {
862 margin-bottom: 1rem !important;
863 }
864
865 /* Floating Action Button */
866 .fab {
867 position: fixed;
868 bottom: 1.5rem;
869 right: 1.5rem;
870 padding: 0.5rem 1rem;
871 border-radius: 2rem;
872 background-color: #1a202c;
873 color: #ffffff;
874 display: none;
875 align-items: center;
876 gap: 0.5rem;
877 box-shadow: var(--shadow-lg);
878 cursor: pointer;
879 transition:
880 transform 0.2s,
881 background-color 0.2s;
882 z-index: 1000;
883 border: none;
884 font-size: 0.875rem;
885 font-weight: 600;
886 }
887
888 .fab:hover {
889 background-color: #2d3748;
890 transform: scale(1.05);
891 }
892
893 /* Side Panel */
894 .side-panel {
895 position: fixed;
896 top: 0;
897 right: 0;
898 width: 400px;
899 height: 100%;
900 background-color: #ffffff;
901 box-shadow: var(--shadow-lg);
902 z-index: 1001;
903 display: flex;
904 flex-direction: column;
905 transition: transform 0.3s ease-in-out;
906 transform: translateX(100%);
907 border-left: 1px solid var(--border-color);
908 }
909
910 .side-panel.open {
911 transform: translateX(0);
912 }
913
914 .side-panel-header {
915 padding: 1rem 1.5rem;
916 border-bottom: 1px solid var(--border-color);
917 display: flex;
918 justify-content: space-between;
919 align-items: center;
920 }
921
922 .side-panel-header .header-left {
923 display: flex;
924 align-items: center;
925 gap: 0.5rem;
926 }
927
928 .side-panel-header h2 {
929 font-size: 1.5rem;
930 font-weight: 700;
931 color: #000000;
932 }
933
934 .side-panel-content {
935 padding: 1.5rem;
936 overflow-y: auto;
937 flex: 1;
938 }
939
940 .help-section {
941 margin-bottom: 1.5rem;
942 }
943
944 .help-section-title {
945 font-size: 0.75rem;
946 font-weight: 700;
947 color: #718096;
948 text-transform: uppercase;
949 letter-spacing: 0.05em;
950 margin-bottom: 0.75rem;
951 }
952
953 .help-section p {
954 font-size: 0.875rem;
955 color: #2d3748;
956 line-height: 1.6;
957 }
958
959 /* Legend Cards (Accordion) */
960 .legend-list {
961 display: flex;
962 flex-direction: column;
963 gap: 0.5rem;
964 }
965
966 .legend-item {
967 background-color: #ffffff;
968 border: 1px solid #e2e8f0;
969 border-radius: 0.5rem;
970 cursor: pointer;
971 transition:
972 background-color 0.2s,
973 border-color 0.2s;
974 overflow: hidden;
975 }
976
977 .legend-item:hover {
978 background-color: #f8fafc;
979 border-color: #cbd5e1;
980 }
981
982 .legend-item-header {
983 display: flex;
984 justify-content: space-between;
985 align-items: center;
986 padding: 0.75rem 1rem;
987 }
988
989 .legend-item-title {
990 font-size: 0.875rem;
991 font-weight: 500;
992 color: #1a202c;
993 }
994
995 .legend-item-arrow {
996 color: #a0aec0;
997 transition: transform 0.2s;
998 }
999
1000 .legend-item.open .legend-item-arrow {
1001 transform: rotate(90deg);
1002 }
1003
1004 .legend-item-content {
1005 padding: 0 1rem 0.75rem 1rem;
1006 font-size: 0.875rem;
1007 color: #4a5568;
1008 line-height: 1.5;
1009 display: none;
1010 }
1011
1012 .legend-item.open .legend-item-content {
1013 display: block;
1014 }
1015
1016 /* Documentation Links (Cards) */
1017 .doc-list {
1018 display: flex;
1019 flex-direction: column;
1020 gap: 0.75rem;
1021 }
1022
1023 .doc-card {
1024 display: flex;
1025 justify-content: space-between;
1026 align-items: self-start;
1027 padding: 0.75rem 1rem;
1028 background-color: #ffffff;
1029 border: 1px solid #e2e8f0;
1030 border-radius: 0.5rem;
1031 text-decoration: none;
1032 transition:
1033 background-color 0.2s,
1034 border-color 0.2s;
1035 }
1036
1037 .doc-card:hover {
1038 background-color: #f8fafc;
1039 border-color: #cbd5e1;
1040 }
1041
1042 .doc-card-info {
1043 display: flex;
1044 flex-direction: column;
1045 gap: 0.25rem;
1046 }
1047
1048 .doc-card-title {
1049 font-size: 0.875rem;
1050 font-weight: 500;
1051 color: #2563eb;
1052 }
1053
1054 .doc-card-domain {
1055 font-size: 0.75rem;
1056 color: #718096;
1057 }
1058
1059 .doc-card-desc {
1060 font-size: 0.75rem;
1061 color: #4a5568;
1062 }
1063
1064 .doc-card-icon {
1065 color: #a0aec0;
1066 }
1067
1068 /* Tooltip Icon */
1069 .tooltip-icon {
1070 display: inline-flex;
1071 align-items: center;
1072 justify-content: center;
1073 width: 1rem;
1074 height: 1rem;
1075 color: #a0aec0;
1076 cursor: pointer;
1077 margin-left: 0.25rem;
1078 vertical-align: middle;
1079 border: 1px solid #a0aec0;
1080 border-radius: 50%;
1081 font-size: 0.75rem;
1082 }
1083
1084 .tooltip-icon:hover {
1085 color: #718096;
1086 }
1087
1088 .divider {
1089 border-top: 1px solid #e2e8f0;
1090 margin: 1.5rem 0;
1091 }
1092 </style>
1093 <script src="https://cdn.jsdelivr.net/npm/protobufjs@7.2.4/dist/protobuf.min.js"></script>
1094 </head>
1095
1096 <body>
1097 <header>
1098 <div class="header-top-row">
1099 <div
1100 class="flex items-center"
1101 style="display: flex; align-items: center"
1102 >
1103 <div class="header-branding">
1104 <h1 class="header-title" id="report-name">
1105 <a
1106 href="#"
1107 id="header-link"
1108 style="color: inherit; text-decoration: none"
1109 >R8 Configuration Analyzer</a
1110 >
1111 </h1>
1112 </div>
1113 </div>
1114 <div class="header-stats">
1115 <div class="stat-item">
1116 <span class="stat-label">Shrinking</span
1117 ><span class="stat-value" id="total-shrinking"></span>
1118 </div>
1119 <div class="stat-item">
1120 <span class="stat-label">Optimization</span
1121 ><span class="stat-value" id="total-optimization"></span>
1122 </div>
1123 <div class="stat-item">
1124 <span class="stat-label">Obfuscation</span
1125 ><span class="stat-value" id="total-obfuscation"></span>
1126 </div>
1127 <button
1128 type="button"
1129 class="tooltip-icon"
1130 id="lenses-tooltip-icon"
1131 style="cursor: pointer"
1132 data-tooltip="These numbers represent optimization ratios for code before optimization passes have occurred. For final optimization ratios, build your app bundle, and see r8.json."
1133 aria-label="These numbers represent optimization ratios for code before optimization passes have occurred. For final optimization ratios, build your app bundle, and see r8.json."
1134 >
1135 ?
1136 </button>
1137 </div>
1138 </div>
1139 <div class="header-toolbar">
1140 <div id="report-view-controls" class="header-toolbar-inner">
1141 <div
1142 class="flex-1"
1143 style="
1144 display: flex;
1145 align-items: center;
1146 gap: 1rem;
1147 justify-content: flex-start;
1148 "
1149 >
1150 <div class="filter-icon" title="Filter Options">
1151 <svg
1152 xmlns="http://www.w3.org/2000/svg"
1153 width="20"
1154 height="20"
1155 fill="none"
1156 viewBox="0 0 24 24"
1157 stroke="currentColor"
1158 stroke-width="2"
1159 >
1160 <path
1161 stroke-linecap="round"
1162 stroke-linejoin="round"
1163 d="M4 6h16M7 12h10M10 18h4"
1164 />
1165 </svg>
1166 </div>
1167 <div
1168 id="filter-chips-container"
1169 style="display: flex; align-items: center; gap: 0.5rem"
1170 >
1171 <div class="segmented-control" id="lens-tabs">
1172 <button class="segment-btn active" data-lens="All">
1173 All Rules
1174 </button>
1175 <button class="segment-btn" data-lens="Identical">
1176 Identical
1177 </button>
1178 <button class="segment-btn" data-lens="Subsumed">
1179 Subsumed
1180 </button>
1181 <button class="segment-btn" data-lens="Unused">Unused</button>
1182 </div>
1183 <button
1184 type="button"
1185 class="tooltip-icon"
1186 id="lenses-tooltip-icon"
1187 style="cursor: pointer; margin-right: 0.5rem"
1188 data-tooltip="Filter rules to a specific opportunity."
1189 aria-label="Filter rules to a specific opportunity."
1190 >
1191 ?
1192 </button>
1193 <div class="relative" id="add-filter-container">
1194 <button id="add-filter-btn" class="add-filter-btn">
1195 <svg
1196 xmlns="http://www.w3.org/2000/svg"
1197 width="16"
1198 height="16"
1199 fill="none"
1200 viewBox="0 0 24 24"
1201 stroke="currentColor"
1202 stroke-width="2"
1203 >
1204 <path
1205 stroke-linecap="round"
1206 stroke-linejoin="round"
1207 d="M12 4v16m8-8H4"
1208 />
1209 </svg>
1210 Add filter
1211 </button>
1212 <div
1213 id="add-filter-dropdown"
1214 class="dropdown-menu hidden"
1215 style="min-width: 10rem"
1216 >
1217 <div id="add-filter-list"></div>
1218 </div>
1219 </div>
1220 <div class="relative hidden" id="cls-chip-container">
1221 <button id="cls-filter-btn" class="filter-chip">
1222 <span id="cls-filter-text">Classes: All</span>
1223 </button>
1224 <div id="cls-dropdown" class="dropdown-menu hidden"></div>
1225 </div>
1226 <div class="relative hidden" id="field-chip-container">
1227 <button id="field-filter-btn" class="filter-chip">
1228 <span id="field-filter-text">Fields: All</span>
1229 </button>
1230 <div id="field-dropdown" class="dropdown-menu hidden"></div>
1231 </div>
1232 <div class="relative hidden" id="method-chip-container">
1233 <button id="method-filter-btn" class="filter-chip">
1234 <span id="method-filter-text">Methods: All</span>
1235 </button>
1236 <div id="method-dropdown" class="dropdown-menu hidden"></div>
1237 </div>
1238 </div>
1239 </div>
1240 <div
1241 class="flex-1"
1242 style="
1243 display: flex;
1244 align-items: center;
1245 gap: 0.75rem;
1246 justify-content: flex-end;
1247 "
1248 >
1249 <div id="search-container" class="search-bar-container relative">
1250 <div class="search-bar-icon" id="search-icon-btn">
1251 <svg
1252 xmlns="http://www.w3.org/2000/svg"
1253 width="18"
1254 height="18"
1255 fill="none"
1256 viewBox="0 0 24 24"
1257 stroke="currentColor"
1258 stroke-width="2"
1259 >
1260 <path
1261 stroke-linecap="round"
1262 stroke-linejoin="round"
1263 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
1264 ></path>
1265 </svg>
1266 </div>
1267 <input
1268 type="text"
1269 id="search-input"
1270 class="search-bar-input"
1271 placeholder="Search report..."
1272 />
1273 <button
1274 id="clear-search-btn"
1275 class="hidden"
1276 style="
1277 color: var(--text-gray-500);
1278 display: flex;
1279 align-items: center;
1280 justify-content: center;
1281 "
1282 >
1283 <svg
1284 xmlns="http://www.w3.org/2000/svg"
1285 width="18"
1286 height="18"
1287 fill="none"
1288 viewBox="0 0 24 24"
1289 stroke="currentColor"
1290 stroke-width="2"
1291 >
1292 <path
1293 stroke-linecap="round"
1294 stroke-linejoin="round"
1295 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
1296 ></path>
1297 </svg>
1298 </button>
1299 </div>
1300 <div class="relative" id="columns-toggle-container">
1301 <button
1302 id="toggle-columns-btn"
1303 class="icon-btn"
1304 title="Toggle Columns"
1305 >
1306 <svg
1307 xmlns="http://www.w3.org/2000/svg"
1308 width="20"
1309 height="20"
1310 fill="none"
1311 viewBox="0 0 24 24"
1312 stroke="currentColor"
1313 stroke-width="2"
1314 >
1315 <path
1316 stroke-linecap="round"
1317 stroke-linejoin="round"
1318 d="M9 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h4m0-18h10a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H9m0-18v18"
1319 ></path>
1320 </svg>
1321 </button>
1322 <div
1323 id="columns-dropdown"
1324 class="dropdown-menu right-0 hidden"
1325 style="min-width: 150px"
1326 >
1327 <label
1328 class="popover-item"
1329 style="
1330 display: flex;
1331 align-items: center;
1332 gap: 0.5rem;
1333 padding: 0.5rem 0.75rem;
1334 cursor: pointer;
1335 "
1336 >
1337 <input
1338 type="checkbox"
1339 class="popover-checkbox"
1340 id="toggle-blocked-by-rule-cb"
1341 checked
1342 />
1343 <span
1344 style="
1345 font-size: 0.75rem;
1346 font-weight: 500;
1347 color: var(--text-main);
1348 "
1349 >Blocked by Rule</span
1350 >
1351 </label>
1352 </div>
1353 </div>
1354 <div class="relative" id="group-by-container">
1355 <button
1356 id="group-by-btn"
1357 class="dropdown-btn"
1358 style="
1359 border: none;
1360 background: #f8f8f8;
1361 padding: 0.5rem 0.75rem;
1362 display: flex;
1363 align-items: center;
1364 border-radius: 4px;
1365 "
1366 >
1367 <span style="font-weight: 500; color: var(--text-gray-400)"
1368 >Group by:</span
1369 >
1370 <span
1371 id="group-by-text"
1372 style="
1373 font-weight: 600;
1374 color: var(--text-gray-500);
1375 margin-left: 0.25rem;
1376 "
1377 >Keep Rules</span
1378 >
1379 <svg
1380 xmlns="http://www.w3.org/2000/svg"
1381 width="16"
1382 height="16"
1383 fill="none"
1384 viewBox="0 0 24 24"
1385 stroke="currentColor"
1386 style="
1387 margin-left: 0.25rem;
1388 color: var(--text-gray-500);
1389 width: 16px;
1390 height: 16px;
1391 "
1392 >
1393 <path
1394 stroke-linecap="round"
1395 stroke-linejoin="round"
1396 stroke-width="2"
1397 d="M19 9l-7 7-7-7"
1398 />
1399 </svg>
1400 </button>
1401 <div
1402 id="group-by-dropdown"
1403 class="dropdown-menu right-0 hidden"
1404 style="min-width: 150px"
1405 >
1406 <div class="dropdown-item" data-value="modules">Keep Rules</div>
1407 <div class="dropdown-item" data-value="packages">
1408 Keep Rule Files
1409 </div>
1410 </div>
1411 </div>
1412 </div>
1413 </div>
1414 </div>
1415 </header>
1416 <div id="report-view" class="container-custom">
1417 <main>
1418 <div class="breadcrumb-row" style="margin-bottom: 2rem; height: auto">
1419 <div id="flat-breadcrumbs"></div>
1420 </div>
1421 <div
1422 id="stats-container"
1423 style="display: flex; gap: 1rem; padding: 0rem; margin-bottom: 1rem"
1424 >
1425 <!-- Shrinking Card -->
1426 <div
1427 class="stat-card"
1428 style="
1429 background: #ffffff;
1430 padding: 1rem;
1431 border-radius: 0.5rem;
1432 border: 1px solid var(--border-color);
1433 flex: 1;
1434 "
1435 >
1436 <div
1437 style="
1438 display: flex;
1439 justify-content: space-between;
1440 align-items: center;
1441 margin-bottom: 0.5rem;
1442 "
1443 >
1444 <div
1445 style="
1446 color: var(--text-muted);
1447 font-size: 0.75rem;
1448 text-transform: uppercase;
1449 font-weight: 600;
1450 display: flex;
1451 align-items: center;
1452 gap: 0.25rem;
1453 "
1454 >
1455 Shrinking
1456 <button
1457 type="button"
1458 class="tooltip-icon"
1459 data-tooltip="Percentage of the app's code base that is subject to removal if unused. The reported score is from before optimization passes have occurred. When R8 performs shrinking, it reduces the overall size of your application by identifying and eliminating code and resources that are not actually being used, ensuring your final build is as lean as possible."
1460 aria-label="Percentage of the app's code base that is subject to removal if unused. The reported score is from before optimization passes have occurred. When R8 performs shrinking, it reduces the overall size of your application by identifying and eliminating code and resources that are not actually being used, ensuring your final build is as lean as possible."
1461 >
1462 ?
1463 </button>
1464 </div>
1465 <div
1466 id="card-total-shrinking"
1467 style="
1468 font-size: 1rem;
1469 font-weight: 700;
1470 color: var(--text-yellow-600);
1471 "
1472 >
1473 --
1474 </div>
1475 </div>
1476 <div style="display: flex; flex-direction: column; gap: 0.5rem">
1477 <!-- Classes -->
1478 <div
1479 style="
1480 display: flex;
1481 align-items: center;
1482 justify-content: space-between;
1483 font-size: 0.75rem;
1484 "
1485 >
1486 <span style="color: var(--text-gray-500); width: 60px"
1487 >Classes</span
1488 >
1489 <div
1490 style="
1491 flex: 1;
1492 height: 4px;
1493 background: var(--bg-hover);
1494 border-radius: 2px;
1495 margin: 0 1rem;
1496 "
1497 >
1498 <div
1499 id="card-shrinking-classes-bar"
1500 style="
1501 height: 100%;
1502 background: var(--text-yellow-600);
1503 border-radius: 2px;
1504 width: 0%;
1505 "
1506 ></div>
1507 </div>
1508 <span
1509 id="card-shrinking-classes-val"
1510 style="color: var(--text-gray-900); font-weight: 500"
1511 >--</span
1512 >
1513 </div>
1514 <!-- Fields -->
1515 <div
1516 style="
1517 display: flex;
1518 align-items: center;
1519 justify-content: space-between;
1520 font-size: 0.75rem;
1521 "
1522 >
1523 <span style="color: var(--text-gray-500); width: 60px"
1524 >Fields</span
1525 >
1526 <div
1527 style="
1528 flex: 1;
1529 height: 4px;
1530 background: var(--bg-hover);
1531 border-radius: 2px;
1532 margin: 0 1rem;
1533 "
1534 >
1535 <div
1536 id="card-shrinking-fields-bar"
1537 style="
1538 height: 100%;
1539 background: var(--text-yellow-600);
1540 border-radius: 2px;
1541 width: 0%;
1542 "
1543 ></div>
1544 </div>
1545 <span
1546 id="card-shrinking-fields-val"
1547 style="color: var(--text-gray-900); font-weight: 500"
1548 >--</span
1549 >
1550 </div>
1551 <!-- Methods -->
1552 <div
1553 style="
1554 display: flex;
1555 align-items: center;
1556 justify-content: space-between;
1557 font-size: 0.75rem;
1558 "
1559 >
1560 <span style="color: var(--text-gray-500); width: 60px"
1561 >Methods</span
1562 >
1563 <div
1564 style="
1565 flex: 1;
1566 height: 4px;
1567 background: var(--bg-hover);
1568 border-radius: 2px;
1569 margin: 0 1rem;
1570 "
1571 >
1572 <div
1573 id="card-shrinking-methods-bar"
1574 style="
1575 height: 100%;
1576 background: var(--text-yellow-600);
1577 border-radius: 2px;
1578 width: 0%;
1579 "
1580 ></div>
1581 </div>
1582 <span
1583 id="card-shrinking-methods-val"
1584 style="color: var(--text-gray-900); font-weight: 500"
1585 >--</span
1586 >
1587 </div>
1588 </div>
1589 </div>
1590 <!-- Optimization Card -->
1591 <div
1592 class="stat-card"
1593 style="
1594 background: #ffffff;
1595 padding: 1rem;
1596 border-radius: 0.5rem;
1597 border: 1px solid var(--border-color);
1598 flex: 1;
1599 "
1600 >
1601 <div
1602 style="
1603 display: flex;
1604 justify-content: space-between;
1605 align-items: center;
1606 margin-bottom: 0.5rem;
1607 "
1608 >
1609 <div
1610 style="
1611 color: var(--text-muted);
1612 font-size: 0.75rem;
1613 text-transform: uppercase;
1614 font-weight: 600;
1615 display: flex;
1616 align-items: center;
1617 gap: 0.25rem;
1618 "
1619 >
1620 Optimization
1621 <button
1622 type="button"
1623 class="tooltip-icon"
1624 data-tooltip="Percentage of the app's code base that is subject to optimization, such as method inlining and class merging. The reported score is from before optimization passes have occurred. Optimizations improve the performance and size of the app."
1625 aria-label="Percentage of the app's code base that is subject to optimization, such as method inlining and class merging. The reported score is from before optimization passes have occurred. Optimizations improve the performance and size of the app."
1626 >
1627 ?
1628 </button>
1629 </div>
1630 <div
1631 id="card-total-optimization"
1632 style="
1633 font-size: 1rem;
1634 font-weight: 700;
1635 color: var(--text-yellow-600);
1636 "
1637 >
1638 --
1639 </div>
1640 </div>
1641 <div style="display: flex; flex-direction: column; gap: 0.5rem">
1642 <!-- Classes -->
1643 <div
1644 style="
1645 display: flex;
1646 align-items: center;
1647 justify-content: space-between;
1648 font-size: 0.75rem;
1649 "
1650 >
1651 <span style="color: var(--text-gray-500); width: 60px"
1652 >Classes</span
1653 >
1654 <div
1655 style="
1656 flex: 1;
1657 height: 4px;
1658 background: var(--bg-hover);
1659 border-radius: 2px;
1660 margin: 0 1rem;
1661 "
1662 >
1663 <div
1664 id="card-optimization-classes-bar"
1665 style="
1666 height: 100%;
1667 background: var(--text-yellow-600);
1668 border-radius: 2px;
1669 width: 0%;
1670 "
1671 ></div>
1672 </div>
1673 <span
1674 id="card-optimization-classes-val"
1675 style="color: var(--text-gray-900); font-weight: 500"
1676 >--</span
1677 >
1678 </div>
1679 <!-- Fields -->
1680 <div
1681 style="
1682 display: flex;
1683 align-items: center;
1684 justify-content: space-between;
1685 font-size: 0.75rem;
1686 "
1687 >
1688 <span style="color: var(--text-gray-500); width: 60px"
1689 >Fields</span
1690 >
1691 <div
1692 style="
1693 flex: 1;
1694 height: 4px;
1695 background: var(--bg-hover);
1696 border-radius: 2px;
1697 margin: 0 1rem;
1698 "
1699 >
1700 <div
1701 id="card-optimization-fields-bar"
1702 style="
1703 height: 100%;
1704 background: var(--text-yellow-600);
1705 border-radius: 2px;
1706 width: 0%;
1707 "
1708 ></div>
1709 </div>
1710 <span
1711 id="card-optimization-fields-val"
1712 style="color: var(--text-gray-900); font-weight: 500"
1713 >--</span
1714 >
1715 </div>
1716 <!-- Methods -->
1717 <div
1718 style="
1719 display: flex;
1720 align-items: center;
1721 justify-content: space-between;
1722 font-size: 0.75rem;
1723 "
1724 >
1725 <span style="color: var(--text-gray-500); width: 60px"
1726 >Methods</span
1727 >
1728 <div
1729 style="
1730 flex: 1;
1731 height: 4px;
1732 background: var(--bg-hover);
1733 border-radius: 2px;
1734 margin: 0 1rem;
1735 "
1736 >
1737 <div
1738 id="card-optimization-methods-bar"
1739 style="
1740 height: 100%;
1741 background: var(--text-yellow-600);
1742 border-radius: 2px;
1743 width: 0%;
1744 "
1745 ></div>
1746 </div>
1747 <span
1748 id="card-optimization-methods-val"
1749 style="color: var(--text-gray-900); font-weight: 500"
1750 >--</span
1751 >
1752 </div>
1753 </div>
1754 </div>
1755 <!-- Obfuscation Card -->
1756 <div
1757 class="stat-card"
1758 style="
1759 background: #ffffff;
1760 padding: 1rem;
1761 border-radius: 0.5rem;
1762 border: 1px solid var(--border-color);
1763 flex: 1;
1764 "
1765 >
1766 <div
1767 style="
1768 display: flex;
1769 justify-content: space-between;
1770 align-items: center;
1771 margin-bottom: 0.5rem;
1772 "
1773 >
1774 <div
1775 style="
1776 color: var(--text-muted);
1777 font-size: 0.75rem;
1778 text-transform: uppercase;
1779 font-weight: 600;
1780 display: flex;
1781 align-items: center;
1782 gap: 0.25rem;
1783 "
1784 >
1785 Obfuscation
1786 <button
1787 type="button"
1788 class="tooltip-icon"
1789 data-tooltip="Percentage of the app's code base that is subject to symbol renaming. The reported score is from before optimization passes have occurred. By tracking this score, you can gauge the effectiveness of R8's obfuscation, which reduces your application's metadata footprint to save memory."
1790 aria-label="Percentage of the app's code base that is subject to symbol renaming. The reported score is from before optimization passes have occurred. By tracking this score, you can gauge the effectiveness of R8's obfuscation, which reduces your application's metadata footprint to save memory."
1791 >
1792 ?
1793 </button>
1794 </div>
1795 <div
1796 id="card-total-obfuscation"
1797 style="
1798 font-size: 1rem;
1799 font-weight: 700;
1800 color: var(--text-yellow-600);
1801 "
1802 >
1803 --
1804 </div>
1805 </div>
1806 <div style="display: flex; flex-direction: column; gap: 0.5rem">
1807 <!-- Classes -->
1808 <div
1809 style="
1810 display: flex;
1811 align-items: center;
1812 justify-content: space-between;
1813 font-size: 0.75rem;
1814 "
1815 >
1816 <span style="color: var(--text-gray-500); width: 60px"
1817 >Classes</span
1818 >
1819 <div
1820 style="
1821 flex: 1;
1822 height: 4px;
1823 background: var(--bg-hover);
1824 border-radius: 2px;
1825 margin: 0 1rem;
1826 "
1827 >
1828 <div
1829 id="card-obfuscation-classes-bar"
1830 style="
1831 height: 100%;
1832 background: var(--text-yellow-600);
1833 border-radius: 2px;
1834 width: 0%;
1835 "
1836 ></div>
1837 </div>
1838 <span
1839 id="card-obfuscation-classes-val"
1840 style="color: var(--text-gray-900); font-weight: 500"
1841 >--</span
1842 >
1843 </div>
1844 <!-- Fields -->
1845 <div
1846 style="
1847 display: flex;
1848 align-items: center;
1849 justify-content: space-between;
1850 font-size: 0.75rem;
1851 "
1852 >
1853 <span style="color: var(--text-gray-500); width: 60px"
1854 >Fields</span
1855 >
1856 <div
1857 style="
1858 flex: 1;
1859 height: 4px;
1860 background: var(--bg-hover);
1861 border-radius: 2px;
1862 margin: 0 1rem;
1863 "
1864 >
1865 <div
1866 id="card-obfuscation-fields-bar"
1867 style="
1868 height: 100%;
1869 background: var(--text-yellow-600);
1870 border-radius: 2px;
1871 width: 0%;
1872 "
1873 ></div>
1874 </div>
1875 <span
1876 id="card-obfuscation-fields-val"
1877 style="color: var(--text-gray-900); font-weight: 500"
1878 >--</span
1879 >
1880 </div>
1881 <!-- Methods -->
1882 <div
1883 style="
1884 display: flex;
1885 align-items: center;
1886 justify-content: space-between;
1887 font-size: 0.75rem;
1888 "
1889 >
1890 <span style="color: var(--text-gray-500); width: 60px"
1891 >Methods</span
1892 >
1893 <div
1894 style="
1895 flex: 1;
1896 height: 4px;
1897 background: var(--bg-hover);
1898 border-radius: 2px;
1899 margin: 0 1rem;
1900 "
1901 >
1902 <div
1903 id="card-obfuscation-methods-bar"
1904 style="
1905 height: 100%;
1906 background: var(--text-yellow-600);
1907 border-radius: 2px;
1908 width: 0%;
1909 "
1910 ></div>
1911 </div>
1912 <span
1913 id="card-obfuscation-methods-val"
1914 style="color: var(--text-gray-900); font-weight: 500"
1915 >--</span
1916 >
1917 </div>
1918 </div>
1919 </div>
1920 </div>
1921 <div
1922 class="breadcrumb-row"
1923 style="
1924 display: flex;
1925 flex-direction: column;
1926 align-items: start;
1927 margin-bottom: 1.5rem;
1928 gap: 0.25rem;
1929 height: auto;
1930 "
1931 >
1932 <span
1933 class="breadcrumb-text"
1934 style="
1935 font-size: 0.875rem;
1936 font-weight: 600;
1937 color: var(--text-gray-900);
1938 "
1939 >Keep Rule Analysis</span
1940 >
1941 <div
1942 style="
1943 font-size: 0.75rem;
1944 color: var(--text-gray-500);
1945 font-weight: 400;
1946 text-transform: none;
1947 letter-spacing: normal;
1948 line-height: 1.4;
1949 padding: 0rem 0.5rem;
1950 "
1951 >
1952 Keep rules prevent R8 from optimizing designated code segments.
1953 Review the list below to identify and refine overly broad rules,
1954 which will directly improve overall application performance.
1955 </div>
1956 </div>
1957 <div class="table-container">
1958 <table id="main-table">
1959 <thead id="table-headers"></thead>
1960 <tbody id="table-data"></tbody>
1961 </table>
1962 </div>
1963 </main>
1964 </div>
1965 <div id="details-view" class="container-custom" style="display: none">
1966 <main>
1967 <div class="breadcrumb-row" style="margin-bottom: 2rem; height: auto">
1968 <div id="details-breadcrumbs"></div>
1969 </div>
1970 <div
1971 style="
1972 flex: 1;
1973 display: flex;
1974 flex-direction: column;
1975 gap: 1.5rem;
1976 min-height: 0;
1977 overflow-y: auto;
1978 padding-right: 0.5rem;
1979 "
1980 >
1981 <div class="table-container" style="flex: none">
1982 <div
1983 style="
1984 padding: 0.75rem 1rem;
1985 border-bottom: 1px solid var(--border-color);
1986 background-color: var(--bg-subtle);
1987 display: flex;
1988 justify-content: space-between;
1989 align-items: center;
1990 "
1991 >
1992 <h2
1993 style="
1994 font-size: 0.875rem;
1995 font-weight: 500;
1996 color: var(--text-gray-900);
1997 "
1998 >
1999 Keep Rule
2000 </h2>
2001 </div>
2002 <div id="details-rule-container" style="padding: 1rem">
2003 <!-- Content rendered by JS -->
2004 </div>
2005 </div>
2006 <div class="table-container" style="flex: none">
2007 <div
2008 style="
2009 padding: 0.75rem 1rem;
2010 border-bottom: 1px solid var(--border-color);
2011 background-color: var(--bg-subtle);
2012 display: flex;
2013 justify-content: space-between;
2014 align-items: center;
2015 "
2016 >
2017 <h2
2018 style="
2019 font-size: 0.875rem;
2020 font-weight: 500;
2021 color: var(--text-gray-900);
2022 "
2023 >
2024 Impact
2025 </h2>
2026 </div>
2027 <div id="details-impact-container" style="padding: 1rem">
2028 <!-- Content rendered by JS -->
2029 </div>
2030 </div>
2031 <div id="related-rules-container" style="flex: none">
2032 <!-- Content rendered by JS -->
2033 </div>
2034 <div
2035 style="
2036 flex: 1;
2037 display: grid;
2038 grid-template-columns: 1fr 1fr 1fr;
2039 gap: 1.5rem;
2040 min-height: 400px;
2041 overflow: hidden;
2042 "
2043 >
2044 <div
2045 class="table-container"
2046 style="display: flex; flex-direction: column"
2047 >
2048 <div
2049 style="
2050 padding: 0.75rem 1rem;
2051 border-bottom: 1px solid var(--border-color);
2052 background-color: var(--bg-subtle);
2053 display: flex;
2054 flex-direction: column;
2055 gap: 0.5rem;
2056 "
2057 >
2058 <div
2059 style="
2060 display: flex;
2061 justify-content: space-between;
2062 align-items: center;
2063 width: 100%;
2064 "
2065 >
2066 <div>
2067 <span
2068 style="
2069 font-size: 0.875rem;
2070 font-weight: 600;
2071 color: var(--text-gray-900);
2072 "
2073 >Kept Classes</span
2074 >
2075 <span
2076 style="
2077 color: var(--text-gray-500);
2078 font-size: 0.75rem;
2079 margin-left: 0.25rem;
2080 "
2081 id="details-classes-count"
2082 >· 0</span
2083 >
2084 </div>
2085 <button
2086 class="icon-btn search-toggle-btn"
2087 data-target="classes"
2088 style="color: var(--text-muted); padding: 2px"
2089 >
2090 <svg
2091 xmlns="http://www.w3.org/2000/svg"
2092 width="16"
2093 height="16"
2094 fill="none"
2095 viewBox="0 0 24 24"
2096 stroke="currentColor"
2097 stroke-width="2"
2098 >
2099 <path
2100 stroke-linecap="round"
2101 stroke-linejoin="round"
2102 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2103 />
2104 </svg>
2105 </button>
2106 </div>
2107 <div
2108 id="classes-search-container"
2109 style="display: none; width: 100%"
2110 >
2111 <input
2112 type="text"
2113 id="classes-search-input"
2114 placeholder="Search kept classes..."
2115 style="
2116 width: 100%;
2117 padding: 0.25rem 0.5rem;
2118 border: 1px solid var(--border-color);
2119 border-radius: 4px;
2120 font-size: 0.75rem;
2121 outline: none;
2122 background-color: var(--bg-body);
2123 color: var(--text-main);
2124 "
2125 />
2126 </div>
2127 </div>
2128 <div
2129 id="details-classes-content"
2130 style="flex: 1; overflow-y: auto; padding: 0.5rem"
2131 ></div>
2132 </div>
2133 <div
2134 class="table-container"
2135 style="display: flex; flex-direction: column"
2136 >
2137 <div
2138 style="
2139 padding: 0.75rem 1rem;
2140 border-bottom: 1px solid var(--border-color);
2141 background-color: var(--bg-subtle);
2142 display: flex;
2143 flex-direction: column;
2144 gap: 0.5rem;
2145 "
2146 >
2147 <div
2148 style="
2149 display: flex;
2150 justify-content: space-between;
2151 align-items: center;
2152 width: 100%;
2153 "
2154 >
2155 <div>
2156 <span
2157 style="
2158 font-size: 0.875rem;
2159 font-weight: 600;
2160 color: var(--text-gray-900);
2161 "
2162 >Kept Fields</span
2163 >
2164 <span
2165 style="
2166 color: var(--text-gray-500);
2167 font-size: 0.75rem;
2168 margin-left: 0.25rem;
2169 "
2170 id="details-fields-count"
2171 >· 0</span
2172 >
2173 </div>
2174 <button
2175 class="icon-btn search-toggle-btn"
2176 data-target="fields"
2177 style="color: var(--text-muted); padding: 2px"
2178 >
2179 <svg
2180 xmlns="http://www.w3.org/2000/svg"
2181 width="16"
2182 height="16"
2183 fill="none"
2184 viewBox="0 0 24 24"
2185 stroke="currentColor"
2186 stroke-width="2"
2187 >
2188 <path
2189 stroke-linecap="round"
2190 stroke-linejoin="round"
2191 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2192 />
2193 </svg>
2194 </button>
2195 </div>
2196 <div
2197 id="fields-search-container"
2198 style="display: none; width: 100%"
2199 >
2200 <input
2201 type="text"
2202 id="fields-search-input"
2203 placeholder="Search kept fields..."
2204 style="
2205 width: 100%;
2206 padding: 0.25rem 0.5rem;
2207 border: 1px solid var(--border-color);
2208 border-radius: 4px;
2209 font-size: 0.75rem;
2210 outline: none;
2211 background-color: var(--bg-body);
2212 color: var(--text-main);
2213 "
2214 />
2215 </div>
2216 </div>
2217 <div
2218 id="details-fields-content"
2219 style="flex: 1; overflow-y: auto; padding: 0.5rem"
2220 ></div>
2221 </div>
2222 <div
2223 class="table-container"
2224 style="display: flex; flex-direction: column"
2225 >
2226 <div
2227 style="
2228 padding: 0.75rem 1rem;
2229 border-bottom: 1px solid var(--border-color);
2230 background-color: var(--bg-subtle);
2231 display: flex;
2232 flex-direction: column;
2233 gap: 0.5rem;
2234 "
2235 >
2236 <div
2237 style="
2238 display: flex;
2239 justify-content: space-between;
2240 align-items: center;
2241 width: 100%;
2242 "
2243 >
2244 <div>
2245 <span
2246 style="
2247 font-size: 0.875rem;
2248 font-weight: 600;
2249 color: var(--text-gray-900);
2250 "
2251 >Kept Methods</span
2252 >
2253 <span
2254 style="
2255 color: var(--text-gray-500);
2256 font-size: 0.75rem;
2257 margin-left: 0.25rem;
2258 "
2259 id="details-methods-count"
2260 >· 0</span
2261 >
2262 </div>
2263 <button
2264 class="icon-btn search-toggle-btn"
2265 data-target="methods"
2266 style="color: var(--text-muted); padding: 2px"
2267 >
2268 <svg
2269 xmlns="http://www.w3.org/2000/svg"
2270 width="16"
2271 height="16"
2272 fill="none"
2273 viewBox="0 0 24 24"
2274 stroke="currentColor"
2275 stroke-width="2"
2276 >
2277 <path
2278 stroke-linecap="round"
2279 stroke-linejoin="round"
2280 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2281 />
2282 </svg>
2283 </button>
2284 </div>
2285 <div
2286 id="methods-search-container"
2287 style="display: none; width: 100%"
2288 >
2289 <input
2290 type="text"
2291 id="methods-search-input"
2292 placeholder="Search kept methods..."
2293 style="
2294 width: 100%;
2295 padding: 0.25rem 0.5rem;
2296 border: 1px solid var(--border-color);
2297 border-radius: 4px;
2298 font-size: 0.75rem;
2299 outline: none;
2300 background-color: var(--bg-body);
2301 color: var(--text-main);
2302 "
2303 />
2304 </div>
2305 </div>
2306 <div
2307 id="details-methods-content"
2308 style="flex: 1; overflow-y: auto; padding: 0.5rem"
2309 ></div>
2310 </div>
2311 </div>
2312 </div>
2313 </main>
2314 </div>
2315 <div id="file-details-view" class="container-custom" style="display: none">
2316 <main>
2317 <div class="breadcrumb-row" style="margin-bottom: 2rem; height: auto">
2318 <div id="file-details-breadcrumbs"></div>
2319 </div>
2320 <div
2321 style="
2322 flex: 1;
2323 display: flex;
2324 flex-direction: column;
2325 gap: 1.5rem;
2326 min-height: 0;
2327 overflow-y: auto;
2328 padding-right: 0.5rem;
2329 "
2330 >
2331 <div class="table-container" style="flex: none">
2332 <div
2333 style="
2334 padding: 1rem;
2335 border-bottom: 1px solid var(--border-color);
2336 background-color: var(--bg-gray-50);
2337 "
2338 >
2339 <h2 style="font-size: 1rem; font-weight: 700">Impact</h2>
2340 </div>
2341 <table style="width: 100%; border-collapse: collapse">
2342 <thead>
2343 <tr>
2344 <th
2345 class="text-center text-xs font-medium text-gray-500 border-b border-gray-200"
2346 style="padding: 0.75rem 0.5rem; text-transform: uppercase"
2347 >
2348 Kept Classes
2349 </th>
2350 <th
2351 class="text-center text-xs font-medium text-gray-500 border-l border-b border-gray-200"
2352 style="padding: 0.75rem 0.5rem; text-transform: uppercase"
2353 >
2354 Kept fields
2355 </th>
2356 <th
2357 class="text-center text-xs font-medium text-gray-500 border-l border-b border-gray-200"
2358 style="padding: 0.75rem 0.5rem; text-transform: uppercase"
2359 >
2360 Kept methods
2361 </th>
2362 </tr>
2363 </thead>
2364 <tbody id="file-details-impact-body"></tbody>
2365 </table>
2366 </div>
2367 <div
2368 class="table-container"
2369 style="flex: 1; display: flex; flex-direction: column"
2370 >
2371 <div
2372 style="
2373 padding: 1rem;
2374 border-bottom: 1px solid var(--border-color);
2375 background-color: var(--bg-gray-50);
2376 "
2377 >
2378 <h2 style="font-size: 1rem; font-weight: 700">Keep Rules</h2>
2379 </div>
2380 <div style="flex: 1; overflow-y: auto">
2381 <table style="width: 100%; border-collapse: collapse">
2382 <thead id="file-details-rules-header">
2383 <tr>
2384 <th
2385 rowspan="2"
2386 class="text-left bg-gray-50 z-30"
2387 style="
2388 padding: 1rem;
2389 width: 600px;
2390 min-width: 600px;
2391 border-bottom: 1px solid var(--border-color);
2392 "
2393 >
2394 Rule
2395 </th>
2396 <th
2397 colspan="4"
2398 class="text-center border-l border-gray-200 bg-gray-50"
2399 style="
2400 padding: 0.5rem;
2401 border-bottom: 1px solid var(--border-color);
2402 "
2403 >
2404 Kept Items
2405 <span
2406 style="
2407 color: var(--text-muted);
2408 text-transform: none;
2409 font-weight: 500;
2410 "
2411 >Higher is worse</span
2412 >
2413 </th>
2414 <th
2415 rowspan="2"
2416 colspan="3"
2417 class="text-center border-l border-gray-200 bg-gray-50"
2418 style="
2419 padding: 0.5rem;
2420 width: 150px;
2421 min-width: 150px;
2422 border-bottom: 1px solid var(--border-color);
2423 "
2424 >
2425 Steps blocked by rule
2426 </th>
2427 </tr>
2428 <tr>
2429 <th
2430 class="text-center text-xs font-medium text-gray-500 border-l border-gray-200"
2431 style="
2432 padding: 0.5rem;
2433 width: 100px;
2434 min-width: 100px;
2435 border-bottom: 1px solid var(--border-color);
2436 "
2437 >
2438 Total
2439 </th>
2440 <th
2441 class="text-center text-xs font-medium text-gray-500 border-l border-gray-200"
2442 style="
2443 padding: 0.5rem;
2444 width: 100px;
2445 min-width: 100px;
2446 border-bottom: 1px solid var(--border-color);
2447 "
2448 >
2449 Classes
2450 </th>
2451 <th
2452 class="text-center text-xs font-medium text-gray-500 border-l border-gray-200"
2453 style="
2454 padding: 0.5rem;
2455 width: 100px;
2456 min-width: 100px;
2457 border-bottom: 1px solid var(--border-color);
2458 "
2459 >
2460 Fields
2461 </th>
2462 <th
2463 class="text-center text-xs font-medium text-gray-500 border-l border-gray-200"
2464 style="
2465 padding: 0.5rem;
2466 width: 100px;
2467 min-width: 100px;
2468 border-bottom: 1px solid var(--border-color);
2469 "
2470 >
2471 Methods
2472 </th>
2473 </tr>
2474 </thead>
2475 <tbody id="file-details-rules-body"></tbody>
2476 </table>
2477 </div>
2478 </div>
2479 </div>
2480 </main>
2481 </div>
2482 <script id="keepradius-proto" type="text/plain">// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
2483 // for details. All rights reserved. Use of this source code is governed by a
2484 // BSD-style license that can be found in the LICENSE file.
2485 syntax = "proto3";
2486
2487 package com.android.tools.r8.keepradius.proto;
2488
2489 // Don't put all messages under the outer class.
2490 option java_multiple_files = true;
2491
2492 // Camel-case the outer class name.
2493 option java_outer_classname = "KeepRadiusProtos";
2494
2495 // Java package consistent with R8 convention.
2496 option java_package = "com.android.tools.r8.keepradius.proto";
2497
2498 // Information about a single keep rule and its immediate keep radius.
2499 message KeepRuleKeepRadius {
2500 int32 id = 1;
2501 // Textual representation of the keep rule.
2502 string source = 2;
2503 // What this keep rule prohibits (e.g., optimization).
2504 // Id into KeepConstraints.
2505 int32 constraints_id = 3;
2506 // Where this rule comes from.
2507 TextFileOrigin origin = 4;
2508 // Keep radius information.
2509 KeepRadius keep_radius = 5;
2510 // Keep rule tags.
2511 repeated KeepRuleTag tags = 6;
2512 }
2513
2514 enum KeepRuleTag {
2515 PACKAGE_WIDE = 0;
2516 }
2517
2518 message KeepRadius {
2519 // Which rules subsume this rule. Note: This information could in principle
2520 // be computed offline using the kept_by info of Kept*Info.
2521 // Ids into KeepAnnotationKeepRadius and KeepRuleKeepRadius.
2522 repeated int32 subsumed_by = 1;
2523 // The immediate keep radius of this rule.
2524 // - Ids into KeptClassInfo.
2525 repeated int32 class_keep_radius = 2;
2526 // - Ids into KeptFieldInfo.
2527 repeated int32 field_keep_radius = 3;
2528 // - Ids into KeptMethodInfo.
2529 repeated int32 method_keep_radius = 4;
2530 }
2531
2532 // The ids of GlobalKeepRuleKeepRadius and KeepRuleKeepRadius are
2533 // non-overlapping by design, so that a single int32 id can be used to
2534 // unambiguously reference a GlobalKeepRuleKeepRadius or KeepRuleKeepRadius.
2535 message GlobalKeepRuleKeepRadius {
2536 int32 id = 1;
2537 // Textual representation of the keep rule.
2538 string source = 2;
2539 // Where this rule comes from.
2540 TextFileOrigin origin = 3;
2541 }
2542
2543 // Information about the constraints of a single keep annotation or rule,
2544 // i.e., what is prohibited by it.
2545 //
2546 // This is encoded as a message to support keep constraints that cannot be
2547 // encoded as a bit in the future.
2548 message KeepConstraints {
2549 int32 id = 1;
2550 repeated KeepConstraint constraints = 2;
2551 }
2552
2553 enum KeepConstraint {
2554 DONT_OBFUSCATE = 0;
2555 DONT_OPTIMIZE = 1;
2556 DONT_SHRINK = 2;
2557 }
2558
2559 // Information about a class in the app/library and which keep rules that
2560 // keep it.
2561 message KeptClassInfo {
2562 int32 id = 1;
2563 // Id into TypeReference.
2564 int32 class_reference_id = 2;
2565 // Id into FileOrigin or ClassFileInJarOrigin.
2566 int32 file_origin_id = 3;
2567 // Ids into KeepRuleKeepRadius, telling which rules keep this class.
2568 // Allows reasoning about properties such as:
2569 // - Which keep rules are fully subsumed by other keep rules?
2570 // - Which keep rules keep the most classes/fields/methods
2571 // not kept by rules?
2572 repeated int32 kept_by = 4;
2573 }
2574
2575 message KeptFieldInfo {
2576 int32 id = 1;
2577 // Id into FieldReference.
2578 int32 field_reference_id = 2;
2579 // Id into FileOrigin or ClassFileInJarOrigin.
2580 int32 file_origin_id = 3;
2581 // Ids into KeepRuleKeepRadius, telling which rules keep this field.
2582 repeated int32 kept_by = 4;
2583 }
2584
2585 message KeptMethodInfo {
2586 int32 id = 1;
2587 // Id into MethodReference.
2588 int32 method_reference_id = 2;
2589 // Id into FileOrigin or ClassFileInJarOrigin.
2590 int32 file_origin_id = 3;
2591 // Ids into KeepRuleKeepRadius, telling which rules keep this method.
2592 repeated int32 kept_by = 4;
2593 }
2594
2595 // References:
2596
2597 message FieldReference {
2598 int32 id = 1;
2599 // The class on which the field is declared.
2600 // Id into TypeReference.
2601 int32 class_reference_id = 2;
2602 // The type of the field.
2603 // Id into type reference.
2604 int32 type_reference_id = 3;
2605 // The name of the field.
2606 string name = 4;
2607 }
2608
2609 message MethodReference {
2610 int32 id = 1;
2611 // The class on which the method is declared.
2612 // Id into TypeReference.
2613 int32 class_reference_id = 2;
2614 // The proto (return type, parameter types) of the method.
2615 // Id into ProtoReference.
2616 int32 proto_reference_id = 3;
2617 // The name of the method.
2618 string name = 4;
2619 }
2620
2621 message ProtoReference {
2622 int32 id = 1;
2623 // Id into TypeReferenceList.
2624 int32 parameters_id = 2;
2625 // Id into TypeReference.
2626 int32 return_type_id = 3;
2627 }
2628
2629 message TypeReference {
2630 int32 id = 1;
2631 // The descriptor, e.g.,
2632 // - Z
2633 // - Ljava/lang/Object;
2634 // - [[Ljava/lang/Object;
2635 string java_descriptor = 2;
2636 }
2637
2638 message TypeReferenceList {
2639 int32 id = 1;
2640 repeated int32 type_reference_ids = 2;
2641 }
2642
2643 // Origins:
2644
2645 message FileOrigin {
2646 int32 id = 1;
2647 string filename = 2;
2648 MavenCoordinate maven_coordinate = 3;
2649 bool provided_by_build_system = 4;
2650 }
2651
2652 // The ids of ClassFileInJarOrigin and FileOrigin are non-overlapping
2653 // by design, so that a single int32 id can be used to
2654 // unambiguously reference a ClassFileInJarOrigin or FileOrigin.
2655 message ClassFileInJarOrigin {
2656 int32 id = 1;
2657 int32 file_origin_id = 2;
2658 string entry = 3;
2659 }
2660
2661 // Intentionally doesn't declare an id since each keep rule has its own
2662 // TextFileOrigin, i.e., there is no sharing of TextFileOrigin.
2663 message TextFileOrigin {
2664 // Id into FileOrigin
2665 int32 file_origin_id = 1;
2666 int32 line_number = 2;
2667 int32 column_number = 3;
2668 }
2669
2670 message MavenCoordinate {
2671 int32 id = 1;
2672 string group_id = 2;
2673 string artifact_id = 3;
2674 string version = 4;
2675 }
2676
2677 message BuildInfo {
2678 int32 class_count = 1;
2679 int32 field_count = 2;
2680 int32 method_count = 3;
2681 int32 live_class_count = 4;
2682 int32 live_field_count = 5;
2683 int32 live_method_count = 6;
2684 }
2685
2686 // Container.
2687
2688 message KeepRadiusContainer {
2689 // Origins.
2690 repeated FileOrigin file_origin_table = 1;
2691 repeated ClassFileInJarOrigin class_file_in_jar_origin_table = 2;
2692 repeated MavenCoordinate maven_coordinate_table = 3;
2693
2694 // References.
2695 repeated FieldReference field_reference_table = 4;
2696 repeated MethodReference method_reference_table = 5;
2697 repeated ProtoReference proto_reference_table = 6;
2698 repeated TypeReference type_reference_table = 7;
2699 repeated TypeReferenceList type_reference_list_table = 8;
2700
2701 // Kept items.
2702 repeated KeptClassInfo kept_class_info_table = 9;
2703 repeated KeptFieldInfo kept_field_info_table = 10;
2704 repeated KeptMethodInfo kept_method_info_table = 11;
2705
2706 // Keep specifications.
2707 repeated KeepConstraints keep_constraints_table = 12;
2708 repeated KeepRuleKeepRadius keep_rule_keep_radius_table = 13;
2709 repeated GlobalKeepRuleKeepRadius global_keep_rule_keep_radius_table = 14;
2710
2711 // Build info.
2712 BuildInfo build_info = 15;
2713 }
2714 </script>
2715 <script id="keepradius-data" type="application/octet-stream">Cp8BEndDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXGVjNDlkYTIwNGVlZDUxZTVhYTEyZTQ5NzdjZWZhMDU4XHRyYW5zZm9ybWVkXGZyYWdtZW50LTEuMy42XHByb2d1YXJkLnR4dBokEhFhbmRyb2lkeC5mcmFnbWVudBoIZnJhZ21lbnQiBTEuMy42CnoIARJ2QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1w1YzQ1ZjU3OGEzNjk3ZmM4YjA2NjY3OGI5MGYwNmUwOVx0cmFuc2Zvcm1lZFxhY3Rpdml0eS0xLjYuMC1ydW50aW1lLmphcgp4CAISdEM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcODhjNjFlMTUyNWQ3OGE0NTI3YzUyMTFkYTA2N2Y3YmRcdHJhbnNmb3JtZWRcZW1vamkyLTEuMi4wLXJ1bnRpbWUuamFyCnoIAxJ2QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xmOTQ0MjdhZWUxOTMwNDU5ZTk0NjEzN2FhOGFmMzkzMVx0cmFuc2Zvcm1lZFxmcmFnbWVudC0xLjMuNi1ydW50aW1lLmphcgqkAQgEEp8BQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcbW9kdWxlcy0yXGZpbGVzLTIuMVxhbmRyb2lkeC5saWZlY3ljbGVcbGlmZWN5Y2xlLWNvbW1vblwyLjUuMVwxZmRiNzM0OTcwMWU5Y2YyZjBhNjlmYzEwNjQyYjZmZWY2YmIzZTEyXGxpZmVjeWNsZS1jb21tb24tMi41LjEuamFyCpEBCAUSjAFDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXGNhY2UyYjUwNTkyZWY0MzNhOTgxZTNjNWZkYzc1ZWE0XHRyYW5zZm9ybWVkXGxpZmVjeWNsZS12aWV3bW9kZWwtc2F2ZWRzdGF0ZS0yLjUuMS1ydW50aW1lLmphcgqKAQgGEoUBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1w0YmQ4ZDhjMDFjZjA5MmUyNDdkY2JmODE1YWZjNGIyY1x0cmFuc2Zvcm1lZFxsaWZlY3ljbGUtbGl2ZWRhdGEtY29yZS0yLjUuMS1ydW50aW1lLmphcgp8CAcSeEM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcMWEzNTA5NjE5ZWY3MTIyNzdkMTQzYWNiOTNhODNiZGJcdHJhbnNmb3JtZWRcc2F2ZWRzdGF0ZS0xLjIuMC1ydW50aW1lLmphcgq1AQgIEoABQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xmM2Y1NmZjOGFjM2E1NmZhNjY5NWI0YjgxZGRjM2ZiMFx0cmFuc2Zvcm1lZFxsaWZlY3ljbGUtcnVudGltZS0yLjUuMVxwcm9ndWFyZC50eHQaLhISYW5kcm9pZHgubGlmZWN5Y2xlGhFsaWZlY3ljbGUtcnVudGltZSIFMi41LjEKwgEICRK9AUFuZHJvaWQgR3JhZGxlIHBsdWdpbiA5LjMuMSAoZXh0cmFjdGVkIGZpbGU6IEM6XFVzZXJzXGpodXN0XERvY3VtZW50c1xqY3ItZ3VpZGUtcHJvLXNvdXJjZVxhbmRyb2lkXGFwcFxidWlsZFxpbnRlcm1lZGlhdGVzXGRlZmF1bHRfcHJvZ3VhcmRfZmlsZXNcZ2xvYmFsXHByb2d1YXJkLWFuZHJvaWQtb3B0aW1pemUudHh0LTkuMy4xKQq7AQgKErYBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcbW9kdWxlcy0yXGZpbGVzLTIuMVxvcmcuamV0YnJhaW5zLmtvdGxpbnhca290bGlueC1jb3JvdXRpbmVzLWFuZHJvaWRcMS42LjFcNGU2MWZjZGNjNTA4Y2JhYTM3YzRhMjg0YTUwMjA1ZDdjNzc2N2UzN1xrb3RsaW54LWNvcm91dGluZXMtYW5kcm9pZC0xLjYuMS5qYXIKogEICxKdAUM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXG1vZHVsZXMtMlxmaWxlcy0yLjFcb3JnLmpldGJyYWlucy5rb3RsaW5ca290bGluLXN0ZGxpYlwyLjIuMTBcMzBkZTZmYWExMjdhNGEwMTJkYjhlNzFiZjFiOWMwYTk5YjE0MDJiMlxrb3RsaW4tc3RkbGliLTIuMi4xMC5qYXIKxgEIDBKXAUM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcYmMzOWNlNmI1ZmM3MzkwM2NkYzUxN2ZlYmEwMDZhM2VcdHJhbnNmb3JtZWRcc2hyaW5rLXJ1bGVzXGxpYlxNRVRBLUlORlxwcm9ndWFyZFxhbmRyb2lkeC1hbm5vdGF0aW9ucy5wcm8aKBITYW5kcm9pZHguYW5ub3RhdGlvbhoKYW5ub3RhdGlvbiIFMS4zLjAKgwEIDRJ/QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xlN2RlZDJmMTc3MzNhZTRhMWEyZjJmOTA2MjI1ZTk1YVx0cmFuc2Zvcm1lZFxsaWZlY3ljbGUtcHJvY2Vzcy0yLjQuMS1ydW50aW1lLmphcgquAQgOEn5DOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXGU3MWUwYzM1ZTRhZTI3NDZlMjk4NGJiYmY2YmRiOGZhXHRyYW5zZm9ybWVkXHN0YXJ0dXAtcnVudGltZS0xLjEuMVxwcm9ndWFyZC50eHQaKhIQYW5kcm9pZHguc3RhcnR1cBoPc3RhcnR1cC1ydW50aW1lIgUxLjEuMQp2CA8SckM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcZjcxYzNkMjc4Y2E0NmUyZjk3ODI3ZmM3MTJmNmE4YTZcdHJhbnNmb3JtZWRcY29yZS0xLjkuMC1ydW50aW1lLmphcgqGAQgQEoEBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1w2NWYwNjZiN2MyZTBjZjg0MzM4N2E5NmNhOWNhNzBkOFx0cmFuc2Zvcm1lZFx2ZXJzaW9uZWRwYXJjZWxhYmxlLTEuMS4xLXJ1bnRpbWUuamFyCsMBCBESggFDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXGYzZGRkNDBiNWQ3MTQ5Njk3ODBhZDAwZmU1NmFjYjEwXHRyYW5zZm9ybWVkXHZlcnNpb25lZHBhcmNlbGFibGUtMS4xLjFccHJvZ3VhcmQudHh0GjoSHGFuZHJvaWR4LnZlcnNpb25lZHBhcmNlbGFibGUaE3ZlcnNpb25lZHBhcmNlbGFibGUiBTEuMS4xCnsIEhJ3QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1wzMGFkZTJkZGRiNDBhNmY4NDgwMTMyNDgwODZhNzcxNVx0cmFuc2Zvcm1lZFxhcHBjb21wYXQtMS42LjEtcnVudGltZS5qYXIKpAEIExJ4QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1wwZjllOTQzMWMxZjBhMWY1NTY5M2FhNzc0ODdmMTFjN1x0cmFuc2Zvcm1lZFxhcHBjb21wYXQtMS42LjFccHJvZ3VhcmQudHh0GiYSEmFuZHJvaWR4LmFwcGNvbXBhdBoJYXBwY29tcGF0IgUxLjYuMQpQCBQSTEM6XFVzZXJzXGpodXN0XERvY3VtZW50c1xqY3ItZ3VpZGUtcHJvLXNvdXJjZVxhbmRyb2lkXGFwcFxwcm9ndWFyZC1ydWxlcy5wcm8K5QEIFRKkAUM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcM2YzMzJiMzEzNDRmYWJjNmI2ZmFiYWRiYjZhNGJmNTNcdHJhbnNmb3JtZWRcc2hyaW5rLXJ1bGVzXGxpYlxNRVRBLUlORlxjb20uYW5kcm9pZC50b29sc1xyOC1mcm9tLTEuNi4wXGNvcm91dGluZXMucHJvGjoSFW9yZy5qZXRicmFpbnMua290bGlueBoaa290bGlueC1jb3JvdXRpbmVzLWFuZHJvaWQiBTEuNi4xCnkIFhJ1QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xiM2Q5NmNlNDMxYzU1ZTgyMjBmNGM0OGQ4ZDkwYzg0M1x0cmFuc2Zvcm1lZFx3ZWJraXQtMS4xMS4wLXJ1bnRpbWUuamFyCp0BCBcSdkM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcZmU1YzE5M2U5M2MwYmU4NzcwYTczMjM0YjI0YWVjOGRcdHJhbnNmb3JtZWRcd2Via2l0LTEuMTEuMFxwcm9ndWFyZC50eHQaIRIPYW5kcm9pZHgud2Via2l0GgZ3ZWJraXQiBjEuMTEuMAqYAQgYEpMBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcbW9kdWxlcy0yXGZpbGVzLTIuMVxhbmRyb2lkeC5hbm5vdGF0aW9uXGFubm90YXRpb25cMS4zLjBcMjFmNDlmNWY5Yjg1ZmM0OWRlNzEyNTM5Zjc5MTIzMTE5NzQwNTk1XGFubm90YXRpb24tMS4zLjAuamFyCpUBCBkSc0M6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcNDQ2M2NlY2Y0NDcyYTU4OTQ4OTdkZjJiY2ZiMmQzMzJcdHJhbnNmb3JtZWRcY29yZS0xLjkuMFxwcm9ndWFyZC50eHQaHBINYW5kcm9pZHguY29yZRoEY29yZSIFMS45LjAKtQEIGhKAAUM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcYWE4ZDUwYWIxYTE5NTE4Y2FhMTlmZTI0NDRiMGFlN2VcdHJhbnNmb3JtZWRcbGlmZWN5Y2xlLXByb2Nlc3MtMi40LjFccHJvZ3VhcmQudHh0Gi4SEmFuZHJvaWR4LmxpZmVjeWNsZRoRbGlmZWN5Y2xlLXByb2Nlc3MiBTIuNC4xCnwIGxJ4QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1w2NjkwMjY5N2IyZTU2MjJiMGE5OWJiNDYyMzZjY2Q3M1x0cmFuc2Zvcm1lZFxjdXN0b212aWV3LTEuMC4wLXJ1bnRpbWUuamFyCoMBCBwSf0M6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcY2YyYTMwNzcxYzRiMGQ5NTJlMzE3ZDY3MGU4ODc1YTJcdHJhbnNmb3JtZWRcbGlmZWN5Y2xlLXJ1bnRpbWUtMi41LjEtcnVudGltZS5qYXIKgAEIHRJ8QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xkZjNmZDgyMmI2OGRhMTdiZTk1MzA5Mzg3MzRmMzljN1x0cmFuc2Zvcm1lZFx2ZWN0b3JkcmF3YWJsZS0xLjEuMC1ydW50aW1lLmphcgrGAQgeEoYBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1w0NTkzMGRkMmZiMTU2ZDU2ODc4YWMwMGU3ZWQ5NzRhY1x0cmFuc2Zvcm1lZFx2ZWN0b3JkcmF3YWJsZS1hbmltYXRlZC0xLjEuMFxwcm9ndWFyZC50eHQaORIXYW5kcm9pZHgudmVjdG9yZHJhd2FibGUaF3ZlY3RvcmRyYXdhYmxlLWFuaW1hdGVkIgUxLjEuMArbAQgfEpkBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1xiOWViZWU3YzAyNDdhZWM1MzgwMmQ3YTRjMjNlMjM0Mlx0cmFuc2Zvcm1lZFxzaHJpbmstcnVsZXNcbGliXE1FVEEtSU5GXGNvbS5hbmRyb2lkLnRvb2xzXHI4XGNvcm91dGluZXMucHJvGjsSFW9yZy5qZXRicmFpbnMua290bGlueBoba290bGlueC1jb3JvdXRpbmVzLWNvcmUtanZtIgUxLjYuMQq9AQggErgBQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcbW9kdWxlcy0yXGZpbGVzLTIuMVxvcmcuamV0YnJhaW5zLmtvdGxpbnhca290bGlueC1jb3JvdXRpbmVzLWNvcmUtanZtXDEuNi4xXDk3ZmQ3NGNjZjU0YTg2M2QyMjE5NTZmZmNkMjE4MzVlMTY4ZTJhYWFca290bGlueC1jb3JvdXRpbmVzLWNvcmUtanZtLTEuNi4xLmphcgq1AQghErABQzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcbW9kdWxlcy0yXGZpbGVzLTIuMVxhbmRyb2lkeC5jb25zdHJhaW50bGF5b3V0XGNvbnN0cmFpbnRsYXlvdXQtY29yZVwxLjAuNFwyOWNkYmUwM2RlZDZiMDk4MGY2M2ZhNWRhMjU3OWE0MzBlOTExYzQwXGNvbnN0cmFpbnRsYXlvdXQtY29yZS0xLjAuNC5qYXIKggEIIhJ+QzpcVXNlcnNcamh1c3RcLmdyYWRsZVxjYWNoZXNcOS41LjBcdHJhbnNmb3Jtc1wzYmIyZDZjZTNkMjE2ZGI1OGYwODNhYTJmZTJmMjg0MVx0cmFuc2Zvcm1lZFxjb25zdHJhaW50bGF5b3V0LTIuMS40LXJ1bnRpbWUuamFyCrkBCCMSggFDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXDdlMmQzMDhmNDhkN2MyOWIwZDliMWRmMGQ1OWFmNzExXHRyYW5zZm9ybWVkXGxpZmVjeWNsZS12aWV3bW9kZWwtMi41LjFccHJvZ3VhcmQudHh0GjASEmFuZHJvaWR4LmxpZmVjeWNsZRoTbGlmZWN5Y2xlLXZpZXdtb2RlbCIFMi41LjEKzwEIJBKNAUM6XFVzZXJzXGpodXN0XC5ncmFkbGVcY2FjaGVzXDkuNS4wXHRyYW5zZm9ybXNcNjU0MzY0MWVmNTE2ZWQ4YmQ4ZjcwOGYwMmUzYTY2NWRcdHJhbnNmb3JtZWRcbGlmZWN5Y2xlLXZpZXdtb2RlbC1zYXZlZHN0YXRlLTIuNS4xXHByb2d1YXJkLnR4dBo7EhJhbmRyb2lkeC5saWZlY3ljbGUaHmxpZmVjeWNsZS12aWV3bW9kZWwtc2F2ZWRzdGF0ZSIFMi41LjEKeAglEnRDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXDAwYWZiNDE2NWIxOTU2MjcyZDc4MGFhNjI3ZjlmOTcxXHRyYW5zZm9ybWVkXGxvYWRlci0xLjAuMC1ydW50aW1lLmphcgqnAQgmEnlDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXGM3M2JjOGYzNmRmNGI1MmY1MjI4NmI4MjM1MDA0NWY3XHRyYW5zZm9ybWVkXHNhdmVkc3RhdGUtMS4yLjBccHJvZ3VhcmQudHh0GigSE2FuZHJvaWR4LnNhdmVkc3RhdGUaCnNhdmVkc3RhdGUiBTEuMi4wCoYBCCcSgQFDOlxVc2Vyc1xqaHVzdFwuZ3JhZGxlXGNhY2hlc1w5LjUuMFx0cmFuc2Zvcm1zXDMzZTQ1YjNiZDViZTdhODRiYTIyYjIwMDJkYzU4MzJiXHRyYW5zZm9ybWVkXGFwcGNvbXBhdC1yZXNvdXJjZXMtMS42LjEtcnVudGltZS5qYXIiCRAUGBUiA2tleSITCAEQExgWIgtfcHJlSGFuZGxlciIhCAIQNRgpIhlDQUNIRV9ESVJFQ1RPUllfQkFTRV9QQVRIIiAIAxA1GCkiGERBVEFfRElSRUNUT1JZX0JBU0VfUEFUSCIdCAQQNRgpIhVEQVRBX0RJUkVDVE9SWV9TVUZGSVgiGQgFEEUYJSIRVFlQRV9BUlJBWV9CVUZGRVIiEwgGEEUYJSILVFlQRV9TVFJJTkciIggHEEoYJSIaQVBQX1NPVVJDRV9BTkRfQVBQX1RSSUdHRVIiIggIEEoYJSIaQVBQX1NPVVJDRV9BTkRfV0VCX1RSSUdHRVIiEAgJEEoYJSIIRElTQUJMRUQiIggKEEoYJSIaV0VCX1NPVVJDRV9BTkRfV0VCX1RSSUdHRVIiFwgLEEsYJSIPRk9SQ0VfREFSS19PTkxZIhgIDBBLGCUiEE1FRElBX1FVRVJZX09OTFkiKggNEEsYJSIiUFJFRkVSX01FRElBX1FVRVJZX09WRVJfRk9SQ0VfREFSSyIQCA4QTBglIghESVNBQkxFRCIPCA8QTBglIgdFTkFCTEVEIiQIEBBMGCUiHEVOQUJMRURfV0lUSE9VVF9BUFBfSURFTlRJVFkiCwgREE0YJSIDQVBQIg8IEhBNGCUiB0JST1dTRVIiDAgTEE0YJSIETk9ORSIQCBQQswEYtAEiBnRoaXMkMSIQCBUQtAEYtQEiBnRoaXMkMCIRCBYQuQEYugEiB0NSRUFUT1IiEQgXELsBGLoBIgdDUkVBVE9SIhEIGBC8ARi6ASIHQ1JFQVRPUiIRCBkQvQEYugEiB0NSRUFUT1IiEQgaEL4BGLoBIgdDUkVBVE9SIhEIGxC/ARi6ASIHQ1JFQVRPUiIRCBwQwAEYugEiB0NSRUFUT1IiEQgdEMEBGLoBIgdDUkVBVE9SIhEIHhDCARi6ASIHQ1JFQVRPUiIRCB8QwwEYugEiB0NSRUFUT1IiEAggEC8YugEiB0NSRUFUT1IiEQghEM0BGM4BIgdfdGhyZWFkIhQIIhDNARglIgtkZWJ1Z1N0YXR1cyIRCCMQzwEYFiIIX2RlbGF5ZWQiFQgkEM8BGCUiDF9pc0NvbXBsZXRlZCIPCCUQzwEYFiIGX3F1ZXVlIhQIJhDQARjQASIKX2ltbWVkaWF0ZSIXCCcQ0QEY0gEiDWNob3Jlb2dyYXBoZXIiFwgoENMBGCUiDnJ1bm5pbmdXb3JrZXJzIg0IKRDUARgWIgRfY3VyIg4IKhDVARgWIgVfbmV4dCIPCCsQ1QEYbSIGX3N0YXRlIg8ILBDWARjXASIFYXJyYXkiFQgtENgBGCUiDGluZGV4SW5BcnJheSIZCC4Q2AEYFiIQbmV4dFBhcmtlZFdvcmtlciISCC8Q2AEYJSIJd29ya2VyQ3RsIhYIMBDZARglIg1faXNUZXJtaW5hdGVkIhUIMRDZARhtIgxjb250cm9sU3RhdGUiGwgyENkBGG0iEnBhcmtlZFdvcmtlcnNTdGFjayIeCDMQ2gEYJSIVYmxvY2tpbmdUYXNrc0luQnVmZmVyIhYINBDaARglIg1jb25zdW1lckluZGV4IhoINRDaARgWIhFsYXN0U2NoZWR1bGVkVGFzayIWCDYQ2gEYJSINcHJvZHVjZXJJbmRleCIRCDcQ6wEY7AEiByRWQUxVRVMiEAg4EOsBGOsBIgZPTl9BTlkiEwg5EOsBGOsBIglPTl9DUkVBVEUiFAg6EOsBGOsBIgpPTl9ERVNUUk9ZIhIIOxDrARjrASIIT05fUEFVU0UiEwg8EOsBGOsBIglPTl9SRVNVTUUiEgg9EOsBGOsBIghPTl9TVEFSVCIRCD4Q6wEY6wEiB09OX1NUT1AqCBAUIgRmb2xkKgsIARAUGAEiA2dldCoOCAIQFBgCIgZnZXRLZXkqEAgDEBQYAyIIbWludXNLZXkqDAgEEBQYBCIEcGx1cyoOCAUQExgFIgY8aW5pdD4qFwgGEBMYBiIPaGFuZGxlRXhjZXB0aW9uKhIIBxATGAciCnByZUhhbmRsZXIqDggIEB0YBSIGPGluaXQ+Kg4ICRAeGAUiBjxpbml0PioOCAoQIhgIIgY8aW5pdD4qDggLECIYCSIGPGluaXQ+Kg4IDBAiGAoiBjxpbml0PioOCA0QJhgFIgY8aW5pdD4qGAgOECYYCyIQY3JlYXRlRGlzcGF0Y2hlcioXCA8QJhgMIg9nZXRMb2FkUHJpb3JpdHkqEwgQECYYDSILaGludE9uRXJyb3IqDggRECoYBSIGPGluaXQ+KgwIEhAqGA4iBHJlYWQqDQgTECoYDyIFd3JpdGUqDggUECsYBSIGPGluaXQ+KgwIFRArGBAiBHJlYWQqDQgWECsYESIFd3JpdGUqDggXEC0YBSIGPGluaXQ+KgwIGBAtGA4iBHJlYWQqDQgZEC0YDyIFd3JpdGUqDggaEC4YBSIGPGluaXQ+KgwIGxAuGBAiBHJlYWQqDQgcEC4YESIFd3JpdGUqDQgdEDAYEiIFY2FjaGUqDAgeEDAYEyIEY2FsbCoWCB8QMBgUIg5nZXRTdHJlYW1UeXBlcyoPCCAQMBgVIgdnZXRUeXBlKhAIIRAwGBYiCG9uQ3JlYXRlKhEIIhAwGBciCW9uRHJhZ0VuZCoQCCMQMBgYIghvcGVuRmlsZSoNCCQQMBgZIgVxdWVyeSokCCUQMBgaIhxzZXRDbGVhckNhY2hlZERhdGFJbnRlcnZhbE1zKhwIJhAxGBsiFGdldFN1cHBvcnRlZEZlYXR1cmVzKhcIJxAyGBwiD2dldE9yQ3JlYXRlUGVlcioTCCgQMxgdIgtwb3N0TWVzc2FnZSoeCCkQMxgeIhZwb3N0TWVzc2FnZVdpdGhQYXlsb2FkKhgIKhA2GB8iEGdldENvb2tpZU1hbmFnZXIqIQgrEDYYICIZZ2V0R2VvTG9jYXRpb25QZXJtaXNzaW9ucyoPCCwQNhgNIgdnZXROYW1lKiIILRA2GCEiGmdldFNlcnZpY2VXb3JrZXJDb250cm9sbGVyKhUILhA2GCIiDWdldFdlYlN0b3JhZ2UqFQgvEDcYIyINZGVsZXRlUHJvZmlsZSoaCDAQNxgkIhJnZXRBbGxQcm9maWxlTmFtZXMqGggxEDcYJSISZ2V0T3JDcmVhdGVQcm9maWxlKhIIMhA3GCUiCmdldFByb2ZpbGUqGggzEDgYJiISY2xlYXJQcm94eU92ZXJyaWRlKhgINBA4GCciEHNldFByb3h5T3ZlcnJpZGUqGAg1EDgYKCIQc2V0UHJveHlPdmVycmlkZSoUCDYQORgXIgxiYWNrVG9TYWZldHkqDwg3EDkYFyIHcHJvY2VlZCoYCDgQORgXIhBzaG93SW50ZXJzdGl0aWFsKg4IORA6GAUiBnJlbW92ZSoeCDoQOxgpIhZzaG91bGRJbnRlcmNlcHRSZXF1ZXN0KiMIOxA8GCoiG2dldFNlcnZpY2VXb3JrZXJXZWJTZXR0aW5ncyoeCDwQPBgeIhZzZXRTZXJ2aWNlV29ya2VyQ2xpZW50Kh0IPRA9GBYiFWdldEFsbG93Q29udGVudEFjY2VzcyoaCD4QPRgWIhJnZXRBbGxvd0ZpbGVBY2Nlc3MqHAg/ED0YFiIUZ2V0QmxvY2tOZXR3b3JrTG9hZHMqFAhAED0YDCIMZ2V0Q2FjaGVNb2RlKi0IQRA9GCsiJWdldFJlcXVlc3RlZFdpdGhIZWFkZXJPcmlnaW5BbGxvd0xpc3QqHQhCED0YFyIVc2V0QWxsb3dDb250ZW50QWNjZXNzKhoIQxA9GBciEnNldEFsbG93RmlsZUFjY2VzcyocCEQQPRgXIhRzZXRCbG9ja05ldHdvcmtMb2FkcyoUCEUQPRgaIgxzZXRDYWNoZU1vZGUqLQhGED0YLCIlc2V0UmVxdWVzdGVkV2l0aEhlYWRlck9yaWdpbkFsbG93TGlzdConCEcQPhgtIh9nZXRTYWZlQnJvd3NpbmdQcml2YWN5UG9saWN5VXJsKhsISBA+GA0iE2dldFZhcmlhdGlvbnNIZWFkZXIqGAhJED4YLiIQaW5pdFNhZmVCcm93c2luZyodCEoQPhgWIhVpc011bHRpUHJvY2Vzc0VuYWJsZWQqIAhLED4YLyIYc2V0U2FmZUJyb3dzaW5nQWxsb3dsaXN0KiAITBA+GDAiGHNldFNhZmVCcm93c2luZ1doaXRlbGlzdCoRCE0QPxgWIglpc1RyYWNpbmcqDQhOED8YMSIFc3RhcnQqDAhPED8YMiIEc3RvcCoSCFAQQBgzIgpvbkNvbXBsZXRlKhAIURBBGDQiCG9uUmVzdWx0Kg8IUhBCGA0iB2dldERhdGEqGQhTEEIYKiIRZ2V0TWVzc2FnZVBheWxvYWQqEAhUEEIYNSIIZ2V0UG9ydHMqEQhVEEMYNiIJb25NZXNzYWdlKhUIVhBEGDciDW9uUG9zdE1lc3NhZ2UqGAhXEEYYOCIQZ2V0QXNBcnJheUJ1ZmZlcioTCFgQRhgNIgtnZXRBc1N0cmluZyoPCFkQRhgMIgdnZXRUeXBlKg0IWhBHGAUiBWNsb3NlKhMIWxBHGB4iC3Bvc3RNZXNzYWdlKh0IXBBHGB4iFXNldFdlYk1lc3NhZ2VDYWxsYmFjayodCF0QRxg5IhVzZXRXZWJNZXNzYWdlQ2FsbGJhY2sqFgheEEgYOiIOZ2V0RGVzY3JpcHRpb24qFAhfEEgYDCIMZ2V0RXJyb3JDb2RlKhIIYBBJGBYiCmlzUmVkaXJlY3QqHghhEE4YDCIWZ2V0QXR0cmlidXRpb25CZWhhdmlvciomCGIQThgMIh5nZXREaXNhYmxlZEFjdGlvbk1vZGVNZW51SXRlbXMqNwhjEE4YFiIvZ2V0RW50ZXJwcmlzZUF1dGhlbnRpY2F0aW9uQXBwTGlua1BvbGljeUVuYWJsZWQqFAhkEE4YDCIMZ2V0Rm9yY2VEYXJrKhwIZRBOGAwiFGdldEZvcmNlRGFya0JlaGF2aW9yKh0IZhBOGBYiFWdldE9mZnNjcmVlblByZVJhc3RlciotCGcQThgrIiVnZXRSZXF1ZXN0ZWRXaXRoSGVhZGVyT3JpZ2luQWxsb3dMaXN0Kh4IaBBOGBYiFmdldFNhZmVCcm93c2luZ0VuYWJsZWQqHwhpEE4YOyIXZ2V0VXNlckFnZW50TWV0YWRhdGFNYXAqMAhqEE4YDCIoZ2V0V2ViVmlld01lZGlhSW50ZWdyaXR5QXBpRGVmYXVsdFN0YXR1cyowCGsQThg7IihnZXRXZWJWaWV3TWVkaWFJbnRlZ3JpdHlBcGlPdmVycmlkZVJ1bGVzKhoIbBBOGAwiEmdldFdlYmF1dGhuU3VwcG9ydCogCG0QThgWIhhnZXRXaWxsU3VwcHJlc3NFcnJvclBhZ2UqJQhuEE4YFiIdaXNBbGdvcml0aG1pY0RhcmtlbmluZ0FsbG93ZWQqJghvEE4YFyIec2V0QWxnb3JpdGhtaWNEYXJrZW5pbmdBbGxvd2VkKh4IcBBOGBoiFnNldEF0dHJpYnV0aW9uQmVoYXZpb3IqJghxEE4YGiIec2V0RGlzYWJsZWRBY3Rpb25Nb2RlTWVudUl0ZW1zKjcIchBOGBciL3NldEVudGVycHJpc2VBdXRoZW50aWNhdGlvbkFwcExpbmtQb2xpY3lFbmFibGVkKhQIcxBOGBoiDHNldEZvcmNlRGFyayocCHQQThgaIhRzZXRGb3JjZURhcmtCZWhhdmlvciodCHUQThgXIhVzZXRPZmZzY3JlZW5QcmVSYXN0ZXIqLQh2EE4YLCIlc2V0UmVxdWVzdGVkV2l0aEhlYWRlck9yaWdpbkFsbG93TGlzdCoeCHcQThgXIhZzZXRTYWZlQnJvd3NpbmdFbmFibGVkKiMIeBBOGDwiG3NldFVzZXJBZ2VudE1ldGFkYXRhRnJvbU1hcCopCHkQThg9IiFzZXRXZWJWaWV3TWVkaWFJbnRlZ3JpdHlBcGlTdGF0dXMqGgh6EE4YGiISc2V0V2ViYXV0aG5TdXBwb3J0KiAIexBOGBciGHNldFdpbGxTdXBwcmVzc0Vycm9yUGFnZSobCHwQTxg+IhNvblBhZ2VDb21taXRWaXNpYmxlKhcIfRBPGD8iD29uUmVjZWl2ZWRFcnJvciobCH4QTxhAIhNvblJlY2VpdmVkSHR0cEVycm9yKhkIfxBPGEEiEW9uU2FmZUJyb3dzaW5nSGl0KhkIgAEQTxhCIhBvbldlYkF1dGhuSW50ZW50KiEIgQEQTxhDIhhzaG91bGRPdmVycmlkZVVybExvYWRpbmcqFgiCARBQGEQiDWdldENvb2tpZUluZm8qIwiDARBRGEUiGmFkZERvY3VtZW50U3RhcnRKYXZhU2NyaXB0Kh4IhAEQURhGIhVhZGRXZWJNZXNzYWdlTGlzdGVuZXIqIAiFARBRGDUiF2NyZWF0ZVdlYk1lc3NhZ2VDaGFubmVsKhMIhgEQURgqIgpnZXRQcm9maWxlKhsIhwEQURhHIhJnZXRXZWJDaHJvbWVDbGllbnQqGQiIARBRGEgiEGdldFdlYlZpZXdDbGllbnQqGwiJARBRGCoiEmdldFdlYlZpZXdSZW5kZXJlciohCIoBEFEYKiIYZ2V0V2ViVmlld1JlbmRlcmVyQ2xpZW50KiIIiwEQURhJIhlpbnNlcnRWaXN1YWxTdGF0ZUNhbGxiYWNrKhUIjAEQURgWIgxpc0F1ZGlvTXV0ZWQqHwiNARBRGEoiFnBvc3RNZXNzYWdlVG9NYWluRnJhbWUqIQiOARBRGB0iGHJlbW92ZVdlYk1lc3NhZ2VMaXN0ZW5lcioWCI8BEFEYFyINc2V0QXVkaW9NdXRlZCoTCJABEFEYHSIKc2V0UHJvZmlsZSohCJEBEFEYHiIYc2V0V2ViVmlld1JlbmRlcmVyQ2xpZW50KhYIkgEQUhhLIg1jcmVhdGVXZWJWaWV3KhwIkwEQUhgqIhNnZXREcm9wRGF0YVByb3ZpZGVyKhgIlAEQUhgqIg9nZXRQcm9maWxlU3RvcmUqGwiVARBSGCoiEmdldFByb3h5Q29udHJvbGxlciojCJYBEFIYKiIaZ2V0U2VydmljZVdvcmtlckNvbnRyb2xsZXIqEwiXARBSGCoiCmdldFN0YXRpY3MqHQiYARBSGBsiFGdldFN1cHBvcnRlZEZlYXR1cmVzKh0ImQEQUhgqIhRnZXRUcmFjaW5nQ29udHJvbGxlciojCJoBEFIYKiIaZ2V0V2Via2l0VG9Db21wYXRDb252ZXJ0ZXIqEgibARBTGBYiCXRlcm1pbmF0ZSodCJwBEFQYTCIUb25SZW5kZXJlclJlc3BvbnNpdmUqHwidARBUGEwiFm9uUmVuZGVyZXJVbnJlc3BvbnNpdmUqHQieARBVGE0iFGNvbnZlcnRDb29raWVNYW5hZ2VyKiQInwEQVRhOIhtjb252ZXJ0U2FmZUJyb3dzaW5nUmVzcG9uc2UqJAigARBVGE0iG2NvbnZlcnRTYWZlQnJvd3NpbmdSZXNwb25zZSolCKEBEFUYTiIcY29udmVydFNlcnZpY2VXb3JrZXJTZXR0aW5ncyolCKIBEFUYTSIcY29udmVydFNlcnZpY2VXb3JrZXJTZXR0aW5ncyoYCKMBEFUYTyIPY29udmVydFNldHRpbmdzKh4IpAEQVRhOIhVjb252ZXJ0V2ViTWVzc2FnZVBvcnQqHgilARBVGE0iFWNvbnZlcnRXZWJNZXNzYWdlUG9ydCogCKYBEFUYTiIXY29udmVydFdlYlJlc291cmNlRXJyb3IqIAinARBVGE0iF2NvbnZlcnRXZWJSZXNvdXJjZUVycm9yKiIIqAEQVRhQIhljb252ZXJ0V2ViUmVzb3VyY2VSZXF1ZXN0Kg8IqQEQeRgFIgY8aW5pdD4qIwiqARB5GFEiGmNvbXB1dGVBbmRTZXRUZXh0RGlyZWN0aW9uKg8IqwEQfBgFIgY8aW5pdD4qIwisARB8GFEiGmNvbXB1dGVBbmRTZXRUZXh0RGlyZWN0aW9uKiEIrQEQfBhSIhhpc0hvcml6b250YWxseVNjcm9sbGFibGUqDwiuARB9GAUiBjxpbml0PiojCK8BEH0YUSIaY29tcHV0ZUFuZFNldFRleHREaXJlY3Rpb24qIQiwARB9GFIiGGlzSG9yaXpvbnRhbGx5U2Nyb2xsYWJsZSocCLEBEH4YUyITaGFzT25DbGlja0xpc3RlbmVycyomCLIBEIABGFQiHGdldEFjY2Vzc2liaWxpdHlOb2RlUHJvdmlkZXIqHgizARCAARhTIhRnZXRGaXRzU3lzdGVtV2luZG93cyomCLQBEIABGFUiHGdldEltcG9ydGFudEZvckFjY2Vzc2liaWxpdHkqGgi1ARCAARhVIhBnZXRNaW5pbXVtSGVpZ2h0KhkItgEQgAEYVSIPZ2V0TWluaW11bVdpZHRoKiMItwEQgAEYViIZZ2V0UGFyZW50Rm9yQWNjZXNzaWJpbGl0eSolCLgBEIABGFUiG2dldFdpbmRvd1N5c3RlbVVpVmlzaWJpbGl0eSohCLkBEIABGFMiF2hhc092ZXJsYXBwaW5nUmVuZGVyaW5nKhsIugEQgAEYUyIRaGFzVHJhbnNpZW50U3RhdGUqJAi7ARCAARhXIhpwZXJmb3JtQWNjZXNzaWJpbGl0eUFjdGlvbiojCLwBEIABGFgiGXBvc3RJbnZhbGlkYXRlT25BbmltYXRpb24qIwi9ARCAARhZIhlwb3N0SW52YWxpZGF0ZU9uQW5pbWF0aW9uKhkIvgEQgAEYWiIPcG9zdE9uQW5pbWF0aW9uKiAIvwEQgAEYWyIWcG9zdE9uQW5pbWF0aW9uRGVsYXllZComCMABEIABGFwiHHJlbW92ZU9uR2xvYmFsTGF5b3V0TGlzdGVuZXIqIQjBARCAARhYIhdyZXF1ZXN0Rml0U3lzdGVtV2luZG93cyoXCMIBEIABGF0iDXNldEJhY2tncm91bmQqHgjDARCAARheIhRzZXRIYXNUcmFuc2llbnRTdGF0ZSomCMQBEIABGF8iHHNldEltcG9ydGFudEZvckFjY2Vzc2liaWxpdHkqGAjFARCGARgMIg5nZW5lcmF0ZVZpZXdJZCoUCMYBEIYBGGAiCmdldERpc3BsYXkqFQjHARCGARhVIgtnZXRMYWJlbEZvciocCMgBEIYBGFUiEmdldExheW91dERpcmVjdGlvbioXCMkBEIYBGFUiDWdldFBhZGRpbmdFbmQqGQjKARCGARhVIg9nZXRQYWRkaW5nU3RhcnQqGwjLARCGARhTIhFpc1BhZGRpbmdSZWxhdGl2ZSoVCMwBEIYBGF8iC3NldExhYmVsRm9yKhcIzQEQhgEYYSINc2V0TGF5ZXJQYWludCocCM4BEIYBGF8iEnNldExheW91dERpcmVjdGlvbiocCM8BEIYBGFkiEnNldFBhZGRpbmdSZWxhdGl2ZSokCNABEIkBGFUiGmdldEFjY2Vzc2liaWxpdHlMaXZlUmVnaW9uKhwI0QEQiQEYUyISaXNBdHRhY2hlZFRvV2luZG93KhMI0gEQiQEYUyIJaXNMYWlkT3V0KiMI0wEQiQEYUyIZaXNMYXlvdXREaXJlY3Rpb25SZXNvbHZlZCowCNQBEIkBGGIiJm5vdGlmeVN1YnRyZWVBY2Nlc3NpYmlsaXR5U3RhdGVDaGFuZ2VkKiQI1QEQiQEYXyIac2V0QWNjZXNzaWJpbGl0eUxpdmVSZWdpb24qHwjWARCJARhjIhVzZXRDb250ZW50Q2hhbmdlVHlwZXMqIwjXARCLARhkIhlkaXNwYXRjaEFwcGx5V2luZG93SW5zZXRzKh0I2AEQiwEYZCITb25BcHBseVdpbmRvd0luc2V0cyocCNkBEIsBGFgiEnJlcXVlc3RBcHBseUluc2V0cyoQCNoBEI0BGGUiBjxpbml0PiodCNsBEI0BGGQiE29uQXBwbHlXaW5kb3dJbnNldHMqKgjcARCPARhmIiBjYWxsQ29tcGF0SW5zZXRBbmltYXRpb25DYWxsYmFjayojCN0BEI8BGGciGWNvbXB1dGVTeXN0ZW1XaW5kb3dJbnNldHMqHQjeARCPARhoIhNkaXNwYXRjaE5lc3RlZEZsaW5nKiAI3wEQjwEYaSIWZGlzcGF0Y2hOZXN0ZWRQcmVGbGluZyohCOABEI8BGGoiF2Rpc3BhdGNoTmVzdGVkUHJlU2Nyb2xsKh4I4QEQjwEYayIUZGlzcGF0Y2hOZXN0ZWRTY3JvbGwqHwjiARCPARhsIhVnZXRCYWNrZ3JvdW5kVGludExpc3QqHwjjARCPARhtIhVnZXRCYWNrZ3JvdW5kVGludE1vZGUqFgjkARCPARhuIgxnZXRFbGV2YXRpb24qHQjlARCPARhvIhNnZXRSb290V2luZG93SW5zZXRzKhsI5gEQjwEYcCIRZ2V0VHJhbnNpdGlvbk5hbWUqGQjnARCPARhuIg9nZXRUcmFuc2xhdGlvbloqDgjoARCPARhuIgRnZXRaKiII6QEQjwEYUyIYaGFzTmVzdGVkU2Nyb2xsaW5nUGFyZW50KiUI6gEQjwEYUyIbaXNJbXBvcnRhbnRGb3JBY2Nlc3NpYmlsaXR5KiII6wEQjwEYUyIYaXNOZXN0ZWRTY3JvbGxpbmdFbmFibGVkKh8I7AEQjwEYcSIVc2V0QmFja2dyb3VuZFRpbnRMaXN0Kh8I7QEQjwEYciIVc2V0QmFja2dyb3VuZFRpbnRNb2RlKhYI7gEQjwEYcyIMc2V0RWxldmF0aW9uKiMI7wEQjwEYXiIZc2V0TmVzdGVkU2Nyb2xsaW5nRW5hYmxlZCooCPABEI8BGGUiHnNldE9uQXBwbHlXaW5kb3dJbnNldHNMaXN0ZW5lciobCPEBEI8BGHQiEXNldFRyYW5zaXRpb25OYW1lKhkI8gEQjwEYcyIPc2V0VHJhbnNsYXRpb25aKg4I8wEQjwEYcyIEc2V0WiobCPQBEI8BGHUiEXN0YXJ0TmVzdGVkU2Nyb2xsKhoI9QEQjwEYWCIQc3RvcE5lc3RlZFNjcm9sbCodCPYBEJYBGG8iE2dldFJvb3RXaW5kb3dJbnNldHMqHQj3ARCWARhVIhNnZXRTY3JvbGxJbmRpY2F0b3JzKh0I+AEQlgEYXyITc2V0U2Nyb2xsSW5kaWNhdG9ycyodCPkBEJYBGHYiE3NldFNjcm9sbEluZGljYXRvcnMqKAj6ARCXARh3Ih5hZGRPblVuaGFuZGxlZEtleUV2ZW50TGlzdGVuZXIqIwj7ARCXARh4IhlnZXRBY2Nlc3NpYmlsaXR5UGFuZVRpdGxlKiAI/AEQlwEYUyIWaXNBY2Nlc3NpYmlsaXR5SGVhZGluZyohCP0BEJcBGFMiF2lzU2NyZWVuUmVhZGVyRm9jdXNhYmxlKisI/gEQlwEYdyIhcmVtb3ZlT25VbmhhbmRsZWRLZXlFdmVudExpc3RlbmVyKhkI/wEQlwEYeSIPcmVxdWlyZVZpZXdCeUlkKiEIgAIQlwEYXiIXc2V0QWNjZXNzaWJpbGl0eUhlYWRpbmcqIwiBAhCXARh6IhlzZXRBY2Nlc3NpYmlsaXR5UGFuZVRpdGxlKiIIggIQlwEYXiIYc2V0U2NyZWVuUmVhZGVyRm9jdXNhYmxlKiIIgwIQmQEYeyIYZ2V0QWNjZXNzaWJpbGl0eURlbGVnYXRlKigIhAIQmQEYfCIeZ2V0U3lzdGVtR2VzdHVyZUV4Y2x1c2lvblJlY3RzKicIhQIQmQEYfSIdc2F2ZUF0dHJpYnV0ZURhdGFGb3JTdHlsZWFibGUqKAiGAhCZARh+Ih5zZXRTeXN0ZW1HZXN0dXJlRXhjbHVzaW9uUmVjdHMqHQiHAhCcARh4IhNnZXRTdGF0ZURlc2NyaXB0aW9uKh0IiAIQnAEYeiITc2V0U3RhdGVEZXNjcmlwdGlvbiokCIkCEJ0BGH8iGmdldFJlY2VpdmVDb250ZW50TWltZVR5cGVzKiAIigIQnQEYgAEiFXBlcmZvcm1SZWNlaXZlQ29udGVudComCIsCEJ0BGIEBIhtzZXRPblJlY2VpdmVDb250ZW50TGlzdGVuZXIqEAiMAhCgARgFIgY8aW5pdD4qEQiNAhCgARiCASIGPGluaXQ+KhAIjgIQoAEYgwEiBWJ1aWxkKiUIjwIQoAEYhAEiGmNyZWF0ZVdpbmRvd0luc2V0c0luc3RhbmNlKhoIkAIQoAEYhQEiD3NldFN0YWJsZUluc2V0cyogCJECEKABGIUBIhVzZXRTeXN0ZW1XaW5kb3dJbnNldHMqEAiSAhCiARgFIgY8aW5pdD4qEQiTAhCiARiCASIGPGluaXQ+KhAIlAIQogEYgwEiBWJ1aWxkKioIlQIQogEYhQEiH3NldE1hbmRhdG9yeVN5c3RlbUdlc3R1cmVJbnNldHMqGgiWAhCiARiFASIPc2V0U3RhYmxlSW5zZXRzKiEIlwIQogEYhQEiFnNldFN5c3RlbUdlc3R1cmVJbnNldHMqIAiYAhCiARiFASIVc2V0U3lzdGVtV2luZG93SW5zZXRzKiMImQIQogEYhQEiGHNldFRhcHBhYmxlRWxlbWVudEluc2V0cyoQCJoCEKMBGAUiBjxpbml0PioRCJsCEKMBGIIBIgY8aW5pdD4qEAicAhCkARgFIgY8aW5pdD4qEQidAhCkARiCASIGPGluaXQ+KhkIngIQpAEYBSIPYXBwbHlJbnNldFR5cGVzKhAInwIQpAEYgwEiBWJ1aWxkKioIoAIQpAEYhQEiH3NldE1hbmRhdG9yeVN5c3RlbUdlc3R1cmVJbnNldHMqGgihAhCkARiFASIPc2V0U3RhYmxlSW5zZXRzKiEIogIQpAEYhQEiFnNldFN5c3RlbUdlc3R1cmVJbnNldHMqIAijAhCkARiFASIVc2V0U3lzdGVtV2luZG93SW5zZXRzKiMIpAIQpAEYhQEiGHNldFRhcHBhYmxlRWxlbWVudEluc2V0cyoRCKUCEKUBGIYBIgY8aW5pdD4qEQimAhClARiHASIGPGluaXQ+KhwIpwIQpQEYWCISY29weVJvb3RWaWV3Qm91bmRzKh0IqAIQpQEYggEiEmNvcHlXaW5kb3dEYXRhSW50byoRCKkCEKUBGIgBIgZlcXVhbHMqFAiqAhClARiJASIJZ2V0SW5zZXRzKhQIqwIQpQEYigEiCWdldEluc2V0cyobCKwCEKUBGIoBIhBnZXRJbnNldHNGb3JUeXBlKh4IrQIQpQEYiwEiE2dldFJvb3RTdGFibGVJbnNldHMqIAiuAhClARiLASIVZ2V0U3lzdGVtV2luZG93SW5zZXRzKhsIrwIQpQEYjAEiEGdldFZpc2libGVJbnNldHMqEAiwAhClARiNASIFaW5zZXQqEQixAhClARgWIgdpc1JvdW5kKh0IsgIQpQEYBSITbG9hZFJlZmxlY3Rpb25GaWVsZCoeCLMCEKUBGI4BIhNzZXRPdmVycmlkZGVuSW5zZXRzKhoItAIQpQEYhQEiD3NldFJvb3RWaWV3RGF0YSoeCLUCEKUBGIIBIhNzZXRSb290V2luZG93SW5zZXRzKhEItgIQpwEYhgEiBjxpbml0PioRCLcCEKcBGI8BIgY8aW5pdD4qHgi4AhCnARiDASITY29uc3VtZVN0YWJsZUluc2V0cyokCLkCEKcBGIMBIhljb25zdW1lU3lzdGVtV2luZG93SW5zZXRzKhoIugIQpwEYiwEiD2dldFN0YWJsZUluc2V0cyoUCLsCEKcBGBYiCmlzQ29uc3VtZWQqGgi8AhCnARiFASIPc2V0U3RhYmxlSW5zZXRzKhEIvQIQqAEYhgEiBjxpbml0PioRCL4CEKgBGJABIgY8aW5pdD4qHwi/AhCoARiDASIUY29uc3VtZURpc3BsYXlDdXRvdXQqEQjAAhCoARiIASIGZXF1YWxzKhsIwQIQqAEYkQEiEGdldERpc3BsYXlDdXRvdXQqEgjCAhCoARgMIghoYXNoQ29kZSoRCMMCEKoBGIYBIgY8aW5pdD4qEQjEAhCqARiSASIGPGluaXQ+KioIxQIQqgEYiwEiH2dldE1hbmRhdG9yeVN5c3RlbUdlc3R1cmVJbnNldHMqIQjGAhCqARiLASIWZ2V0U3lzdGVtR2VzdHVyZUluc2V0cyojCMcCEKoBGIsBIhhnZXRUYXBwYWJsZUVsZW1lbnRJbnNldHMqEAjIAhCqARiNASIFaW5zZXQqGgjJAhCqARiFASIPc2V0U3RhYmxlSW5zZXRzKhEIygIQqwEYhgEiBjxpbml0PioRCMsCEKsBGJMBIgY8aW5pdD4qHAjMAhCrARhYIhJjb3B5Um9vdFZpZXdCb3VuZHMqFAjNAhCrARiJASIJZ2V0SW5zZXRzKhEIzgIQrAEYggEiBjxpbml0PiofCM8CEKwBGIMBIhRjb25zdW1lRGlzcGxheUN1dG91dCoeCNACEKwBGIMBIhNjb25zdW1lU3RhYmxlSW5zZXRzKiQI0QIQrAEYgwEiGWNvbnN1bWVTeXN0ZW1XaW5kb3dJbnNldHMqHAjSAhCsARhYIhJjb3B5Um9vdFZpZXdCb3VuZHMqHQjTAhCsARiCASISY29weVdpbmRvd0RhdGFJbnRvKhEI1AIQrAEYiAEiBmVxdWFscyobCNUCEKwBGJEBIhBnZXREaXNwbGF5Q3V0b3V0KhQI1gIQrAEYiQEiCWdldEluc2V0cyoqCNcCEKwBGIsBIh9nZXRNYW5kYXRvcnlTeXN0ZW1HZXN0dXJlSW5zZXRzKhoI2AIQrAEYiwEiD2dldFN0YWJsZUluc2V0cyohCNkCEKwBGIsBIhZnZXRTeXN0ZW1HZXN0dXJlSW5zZXRzKiAI2gIQrAEYiwEiFWdldFN5c3RlbVdpbmRvd0luc2V0cyojCNsCEKwBGIsBIhhnZXRUYXBwYWJsZUVsZW1lbnRJbnNldHMqEgjcAhCsARgMIghoYXNoQ29kZSoQCN0CEKwBGI0BIgVpbnNldCoUCN4CEKwBGBYiCmlzQ29uc3VtZWQqEQjfAhCsARgWIgdpc1JvdW5kKh4I4AIQrAEYjgEiE3NldE92ZXJyaWRkZW5JbnNldHMqGgjhAhCsARiFASIPc2V0Um9vdFZpZXdEYXRhKh4I4gIQrAEYggEiE3NldFJvb3RXaW5kb3dJbnNldHMqGgjjAhCsARiFASIPc2V0U3RhYmxlSW5zZXRzKhkI5AIQrQEYlAEiDnRvUGxhdGZvcm1UeXBlKhEI5QIQrgEYlQEiBm9uUHVsbCoRCOYCELABGJYBIgZjcmVhdGUqFgjnAhCwARiXASILZ2V0RGlzdGFuY2UqGQjoAhCwARiYASIOb25QdWxsRGlzdGFuY2UqGAjpAhCxARhYIg5zZXRDb250ZW50VmlldyoYCOoCELIBGFgiDnNldENvbnRlbnRWaWV3KhwI6wIQtgEYmQEiEW9uQWN0aXZpdHlDcmVhdGVkKh4I7AIQtgEYmgEiE29uQWN0aXZpdHlEZXN0cm95ZWQqGwjtAhC2ARiaASIQb25BY3Rpdml0eVBhdXNlZCocCO4CELYBGJoBIhFvbkFjdGl2aXR5UmVzdW1lZComCO8CELYBGJkBIhtvbkFjdGl2aXR5U2F2ZUluc3RhbmNlU3RhdGUqHAjwAhC2ARiaASIRb25BY3Rpdml0eVN0YXJ0ZWQqHAjxAhC2ARiaASIRb25BY3Rpdml0eVN0b3BwZWQqEAjyAhC4ARgFIgY8aW5pdD4qHAjzAhC4ARiZASIRb25BY3Rpdml0eUNyZWF0ZWQqJgj0AhC4ARiZASIbb25BY3Rpdml0eVNhdmVJbnN0YW5jZVN0YXRlKhwI9QIQuAEYmgEiEW9uQWN0aXZpdHlTdG9wcGVkKhEI9gIQswEYmwEiBjxpbml0PiogCPcCELMBGJoBIhVvbkFjdGl2aXR5UG9zdFJlc3VtZWQqIAj4AhCzARiaASIVb25BY3Rpdml0eVBvc3RTdGFydGVkKhEI+QIQtAEYnAEiBjxpbml0PiocCPoCELQBGJkBIhFvbkFjdGl2aXR5Q3JlYXRlZCobCPsCELQBGJoBIhBvbkFjdGl2aXR5UGF1c2VkKh8I/AIQtAEYmQEiFG9uQWN0aXZpdHlQcmVDcmVhdGVkKhwI/QIQtAEYmgEiEW9uQWN0aXZpdHlTdG9wcGVkKhAI/gIQxAEYBSIGPGluaXQ+KhwI/wIQxAEYmQEiEW9uQWN0aXZpdHlDcmVhdGVkKh4IgAMQxAEYmgEiE29uQWN0aXZpdHlEZXN0cm95ZWQqGwiBAxDEARiaASIQb25BY3Rpdml0eVBhdXNlZCogCIIDEMQBGJkBIhVvbkFjdGl2aXR5UG9zdENyZWF0ZWQqIAiDAxDEARiaASIVb25BY3Rpdml0eVBvc3RSZXN1bWVkKiAIhAMQxAEYmgEiFW9uQWN0aXZpdHlQb3N0U3RhcnRlZCohCIUDEMQBGJoBIhZvbkFjdGl2aXR5UHJlRGVzdHJveWVkKh4IhgMQxAEYmgEiE29uQWN0aXZpdHlQcmVQYXVzZWQqHwiHAxDEARiaASIUb25BY3Rpdml0eVByZVN0b3BwZWQqHAiIAxDEARiaASIRb25BY3Rpdml0eVJlc3VtZWQqJgiJAxDEARiZASIbb25BY3Rpdml0eVNhdmVJbnN0YW5jZVN0YXRlKhwIigMQxAEYmgEiEW9uQWN0aXZpdHlTdGFydGVkKhwIiwMQxAEYmgEiEW9uQWN0aXZpdHlTdG9wcGVkKhUIjAMQxAEYmgEiCnJlZ2lzdGVySW4qFwiNAxDFARidASIMZ2V0RmlsbEFscGhhKhYIjgMQxQEYDCIMZ2V0RmlsbENvbG9yKhkIjwMQxQEYnQEiDmdldFN0cm9rZUFscGhhKhgIkAMQxQEYDCIOZ2V0U3Ryb2tlQ29sb3IqGQiRAxDFARidASIOZ2V0U3Ryb2tlV2lkdGgqGQiSAxDFARidASIOZ2V0VHJpbVBhdGhFbmQqHAiTAxDFARidASIRZ2V0VHJpbVBhdGhPZmZzZXQqGwiUAxDFARidASIQZ2V0VHJpbVBhdGhTdGFydCoXCJUDEMUBGJ4BIgxzZXRGaWxsQWxwaGEqFgiWAxDFARgaIgxzZXRGaWxsQ29sb3IqGQiXAxDFARieASIOc2V0U3Ryb2tlQWxwaGEqGAiYAxDFARgaIg5zZXRTdHJva2VDb2xvcioZCJkDEMUBGJ4BIg5zZXRTdHJva2VXaWR0aCoZCJoDEMUBGJ4BIg5zZXRUcmltUGF0aEVuZCocCJsDEMUBGJ4BIhFzZXRUcmltUGF0aE9mZnNldCobCJwDEMUBGJ4BIhBzZXRUcmltUGF0aFN0YXJ0KhYInQMQxgEYDSIMZ2V0R3JvdXBOYW1lKhkIngMQxgEYnwEiDmdldExvY2FsTWF0cml4KhQInwMQxgEYnQEiCWdldFBpdm90WCoUCKADEMYBGJ0BIglnZXRQaXZvdFkqFgihAxDGARidASILZ2V0Um90YXRpb24qFAiiAxDGARidASIJZ2V0U2NhbGVYKhQIowMQxgEYnQEiCWdldFNjYWxlWSoYCKQDEMYBGJ0BIg1nZXRUcmFuc2xhdGVYKhgIpQMQxgEYnQEiDWdldFRyYW5zbGF0ZVkqFAimAxDGARieASIJc2V0UGl2b3RYKhQIpwMQxgEYngEiCXNldFBpdm90WSoWCKgDEMYBGJ4BIgtzZXRSb3RhdGlvbioUCKkDEMYBGJ4BIglzZXRTY2FsZVgqFAiqAxDGARieASIJc2V0U2NhbGVZKhgIqwMQxgEYngEiDXNldFRyYW5zbGF0ZVgqGAisAxDGARieASINc2V0VHJhbnNsYXRlWSoWCK0DEMgBGKABIgtnZXRQYXRoRGF0YSoVCK4DEMgBGA0iC2dldFBhdGhOYW1lKhYIrwMQyAEYoQEiC3NldFBhdGhEYXRhKhMIsAMQygEYnQEiCGdldEFscGhhKhYIsQMQygEYDCIMZ2V0Um9vdEFscGhhKhMIsgMQygEYngEiCHNldEFscGhhKhYIswMQygEYGiIMc2V0Um9vdEFscGhhKiMItAMQywEYDCIZZ2V0Q2hhbmdpbmdDb25maWd1cmF0aW9ucyojCLUDEMwBGAwiGWdldENoYW5naW5nQ29uZmlndXJhdGlvbnMqEgi2AxDbARiiASIHdmFsdWVPZioRCLcDENsBGKMBIgZ2YWx1ZXMqEgi4AxDdARikASIHdmFsdWVPZioRCLkDEN0BGKUBIgZ2YWx1ZXMqEgi6AxDfARimASIHdmFsdWVPZioRCLsDEN8BGKcBIgZ2YWx1ZXMqEgi8AxDhARioASIHdmFsdWVPZioRCL0DEOEBGKkBIgZ2YWx1ZXMqEgi+AxDjARiqASIHdmFsdWVPZioRCL8DEOMBGKsBIgZ2YWx1ZXMqEgjAAxDlARisASIHdmFsdWVPZioRCMEDEOUBGK0BIgZ2YWx1ZXMqEgjCAxDnARiuASIHdmFsdWVPZioRCMMDEOcBGK8BIgZ2YWx1ZXMqEgjEAxDpARiwASIHdmFsdWVPZioRCMUDEOkBGLEBIgZ2YWx1ZXMqEgjGAxDrARiyASIHdmFsdWVPZioRCMcDEOsBGLMBIgZ2YWx1ZXMqEgjIAxDtARi0ASIHdmFsdWVPZioRCMkDEO0BGLUBIgZ2YWx1ZXMqEgjKAxDvARi2ASIHdmFsdWVPZioRCMsDEO8BGLcBIgZ2YWx1ZXMqIwjMAxDxARg6IhlnZXRBY2Nlc3NpYmlsaXR5Q2xhc3NOYW1lKhYIzQMQ8QEYuAEiC2dldEl0ZW1EYXRhKhYIzgMQ8QEYFyIMc2V0Q2hlY2thYmxlKhQIzwMQ8QEYFyIKc2V0Q2hlY2tlZCobCNADEPEBGBciEXNldEV4cGFuZGVkRm9ybWF0KhII0QMQ8QEYuQEiB3NldEljb24qGQjSAxDxARi6ASIOc2V0SXRlbUludm9rZXIqGwjTAxDxARi7ASIQc2V0UG9wdXBDYWxsYmFjayoTCNQDEPEBGLwBIghzZXRUaXRsZSodCNUDEPUBGAwiE2dldFdpbmRvd0FuaW1hdGlvbnMqFgjWAxD2ARi9ASILZ2V0SW5mbGF0ZXIqFgjXAxD2ARi4ASILZ2V0SXRlbURhdGEqFgjYAxD2ARgXIgxzZXRDaGVja2FibGUqFAjZAxD2ARgXIgpzZXRDaGVja2VkKhoI2gMQ9gEYFyIQc2V0Rm9yY2VTaG93SWNvbiogCNsDEPYBGBciFnNldEdyb3VwRGl2aWRlckVuYWJsZWQqEgjcAxD2ARi5ASIHc2V0SWNvbiogCN0DEPYBGBciFnNldFN1Yk1lbnVBcnJvd1Zpc2libGUqEwjeAxD2ARi8ASIIc2V0VGl0bGUqGgjfAxD4ARi+ASIPZ2V0VGFiQ29udGFpbmVyKh8I4AMQ+AEYuQEiFHNldFByaW1hcnlCYWNrZ3JvdW5kKh0I4QMQ+AEYuQEiEnNldFNwbGl0QmFja2dyb3VuZCofCOIDEPgBGLkBIhRzZXRTdGFja2VkQmFja2dyb3VuZCoaCOMDEPgBGL8BIg9zZXRUYWJDb250YWluZXIqGgjkAxD4ARgXIhBzZXRUcmFuc2l0aW9uaW5nKhcI5QMQ+AEYGiINc2V0VmlzaWJpbGl0eSofCOYDEPoBGAwiFWdldEFuaW1hdGVkVmlzaWJpbGl0eSoaCOcDEPoBGAwiEGdldENvbnRlbnRIZWlnaHQqFQjoAxD6ARg6IgtnZXRTdWJ0aXRsZSoSCOkDEPoBGDoiCGdldFRpdGxlKhoI6gMQ+gEYGiIQc2V0Q29udGVudEhlaWdodCoXCOsDEPoBGFgiDXNldEN1c3RvbVZpZXcqFgjsAxD6ARi8ASILc2V0U3VidGl0bGUqEwjtAxD6ARi8ASIIc2V0VGl0bGUqGgjuAxD6ARgXIhBzZXRUaXRsZU9wdGlvbmFsKhcI7wMQ+gEYGiINc2V0VmlzaWJpbGl0eSogCPADEPsBGAwiFmdldEFjdGlvbkJhckhpZGVPZmZzZXQqHQjxAxD7ARgMIhNnZXROZXN0ZWRTY3JvbGxBeGVzKhII8gMQ+wEYOiIIZ2V0VGl0bGUqIAjzAxD7ARgaIhZzZXRBY3Rpb25CYXJIaWRlT2Zmc2V0KikI9AMQ+wEYwAEiHnNldEFjdGlvbkJhclZpc2liaWxpdHlDYWxsYmFjayofCPUDEPsBGBciFXNldEhhc05vbkVtYmVkZGVkVGFicyonCPYDEPsBGBciHXNldEhpZGVPbkNvbnRlbnRTY3JvbGxFbmFibGVkKhEI9wMQ+wEYGiIHc2V0SWNvbioSCPgDEPsBGLkBIgdzZXRJY29uKhEI+QMQ+wEYGiIHc2V0TG9nbyoYCPoDEPsBGBciDnNldE92ZXJsYXlNb2RlKiEI+wMQ+wEYFyIXc2V0U2hvd2luZ0ZvckFjdGlvbk1vZGUqFgj8AxD7ARgaIgxzZXRVaU9wdGlvbnMqHAj9AxD7ARjBASIRc2V0V2luZG93Q2FsbGJhY2sqGQj+AxD7ARi8ASIOc2V0V2luZG93VGl0bGUqEgj/AxD+ARjCASIHZ2V0TWVudSoaCIAEEP4BGMMBIg9nZXRPdmVyZmxvd0ljb24qFwiBBBD+ARgMIg1nZXRQb3B1cFRoZW1lKh0IggQQ/gEYDCITZ2V0V2luZG93QW5pbWF0aW9ucyopCIMEEP4BGBciH3NldEV4cGFuZGVkQWN0aW9uVmlld3NFeGNsdXNpdmUqJQiEBBD+ARjEASIac2V0T25NZW51SXRlbUNsaWNrTGlzdGVuZXIqGgiFBBD+ARi5ASIPc2V0T3ZlcmZsb3dJY29uKh0IhgQQ/gEYFyITc2V0T3ZlcmZsb3dSZXNlcnZlZCoXCIcEEP4BGBoiDXNldFBvcHVwVGhlbWUqFwiIBBD+ARjFASIMc2V0UHJlc2VudGVyKi8IiQQQggIYxgEiJGdldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayonCIoEEIICGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIiwQQggIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgiMBBCCAhjHASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgiNBBCCAhjIASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqIAiOBBCCAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8IjwQQggIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKi8IkAQQggIYyQEiJHNldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayonCJEEEIICGBoiHXNldERyb3BEb3duQmFja2dyb3VuZFJlc291cmNlKh8IkgQQggIYFyIVc2V0RW1vamlDb21wYXRFbmFibGVkKhkIkwQQggIYygEiDnNldEtleUxpc3RlbmVyKicIlAQQggIYywEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwiVBBCCAhjMASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSouCJYEEIICGMsBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCJcEEIICGMwBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSogCJgEEIUCGAwiFmdldEF1dG9TaXplTWF4VGV4dFNpemUqIAiZBBCFAhgMIhZnZXRBdXRvU2l6ZU1pblRleHRTaXplKiQImgQQhQIYDCIaZ2V0QXV0b1NpemVTdGVwR3JhbnVsYXJpdHkqKAibBBCFAhjNASIdZ2V0QXV0b1NpemVUZXh0QXZhaWxhYmxlU2l6ZXMqHQicBBCFAhgMIhNnZXRBdXRvU2l6ZVRleHRUeXBlKi8InQQQhQIYxgEiJGdldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayohCJ4EEIUCGM4BIhZnZXRFbW9qaVRleHRWaWV3SGVscGVyKicInwQQhQIYxwEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwigBBCFAhjIASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSouCKEEEIUCGMcBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCKIEEIUCGMgBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSoUCKMEEIUCGBciCnNldEFsbENhcHMqKQikBBCFAhgaIh9zZXRBdXRvU2l6ZVRleHRUeXBlV2l0aERlZmF1bHRzKiAIpQQQhQIYuQEiFXNldEJhY2tncm91bmREcmF3YWJsZSofCKYEEIUCGBoiFXNldEJhY2tncm91bmRSZXNvdXJjZSovCKcEEIUCGMkBIiRzZXRDdXN0b21TZWxlY3Rpb25BY3Rpb25Nb2RlQ2FsbGJhY2sqHwioBBCFAhgXIhVzZXRFbW9qaUNvbXBhdEVuYWJsZWQqFQipBBCFAhjPASIKc2V0RmlsdGVycyobCKoEEIUCGBciEXNldFN1cHBvcnRBbGxDYXBzKicIqwQQhQIYywEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwisBBCFAhjMASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSouCK0EEIUCGMsBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCK4EEIUCGMwBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSogCK8EEIgCGAwiFmdldENvbXBvdW5kUGFkZGluZ0xlZnQqIQiwBBCIAhjOASIWZ2V0RW1vamlUZXh0Vmlld0hlbHBlcionCLEEEIgCGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIsgQQiAIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqIwizBBCIAhjHASIYZ2V0U3VwcG9ydEJ1dHRvblRpbnRMaXN0KiMItAQQiAIYyAEiGGdldFN1cHBvcnRCdXR0b25UaW50TW9kZSouCLUEEIgCGMcBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCLYEEIgCGMgBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSoUCLcEEIgCGBciCnNldEFsbENhcHMqIAi4BBCIAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8IuQQQiAIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKhsIugQQiAIYGiIRc2V0QnV0dG9uRHJhd2FibGUqHAi7BBCIAhi5ASIRc2V0QnV0dG9uRHJhd2FibGUqHwi8BBCIAhgXIhVzZXRFbW9qaUNvbXBhdEVuYWJsZWQqFQi9BBCIAhjPASIKc2V0RmlsdGVycyonCL4EEIgCGMsBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIvwQQiAIYzAEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqIwjABBCIAhjLASIYc2V0U3VwcG9ydEJ1dHRvblRpbnRMaXN0KiMIwQQQiAIYzAEiGHNldFN1cHBvcnRCdXR0b25UaW50TW9kZSouCMIEEIgCGMsBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCMMEEIgCGMwBIiNzZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSovCMQEEIkCGMYBIiRnZXRDdXN0b21TZWxlY3Rpb25BY3Rpb25Nb2RlQ2FsbGJhY2sqIQjFBBCJAhjOASIWZ2V0RW1vamlUZXh0Vmlld0hlbHBlcionCMYEEIkCGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIxwQQiQIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqJgjIBBCJAhjHASIbZ2V0U3VwcG9ydENoZWNrTWFya1RpbnRMaXN0KiYIyQQQiQIYyAEiG2dldFN1cHBvcnRDaGVja01hcmtUaW50TW9kZSouCMoEEIkCGMcBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCMsEEIkCGMgBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSoUCMwEEIkCGBciCnNldEFsbENhcHMqIAjNBBCJAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8IzgQQiQIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKh4IzwQQiQIYGiIUc2V0Q2hlY2tNYXJrRHJhd2FibGUqHwjQBBCJAhi5ASIUc2V0Q2hlY2tNYXJrRHJhd2FibGUqLwjRBBCJAhjJASIkc2V0Q3VzdG9tU2VsZWN0aW9uQWN0aW9uTW9kZUNhbGxiYWNrKh8I0gQQiQIYFyIVc2V0RW1vamlDb21wYXRFbmFibGVkKicI0wQQiQIYywEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwjUBBCJAhjMASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSomCNUEEIkCGMsBIhtzZXRTdXBwb3J0Q2hlY2tNYXJrVGludExpc3QqJgjWBBCJAhjMASIbc2V0U3VwcG9ydENoZWNrTWFya1RpbnRNb2RlKi4I1wQQiQIYywEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRMaXN0Ki4I2AQQiQIYzAEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRNb2RlKi8I2QQQigIYxgEiJGdldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayoZCNoEEIoCGNABIg5nZXRTdXBlckNhbGxlcionCNsEEIoCGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicI3AQQigIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgjdBBCKAhjHASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgjeBBCKAhjIASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqEgjfBBCKAhjRASIHZ2V0VGV4dCoRCOAEEIoCGDoiB2dldFRleHQqHAjhBBCKAhjSASIRZ2V0VGV4dENsYXNzaWZpZXIqIAjiBBCKAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8I4wQQigIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKi8I5AQQigIYyQEiJHNldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayofCOUEEIoCGBciFXNldEVtb2ppQ29tcGF0RW5hYmxlZCoZCOYEEIoCGMoBIg5zZXRLZXlMaXN0ZW5lcionCOcEEIoCGMsBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicI6AQQigIYzAEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgjpBBCKAhjLASIjc2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgjqBBCKAhjMASIjc2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqHAjrBBCKAhjTASIRc2V0VGV4dENsYXNzaWZpZXIqJwjsBBCOAhjHASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TGlzdConCO0EEI4CGMgBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRNb2RlKiII7gQQjgIYxwEiF2dldFN1cHBvcnRJbWFnZVRpbnRMaXN0KiII7wQQjgIYyAEiF2dldFN1cHBvcnRJbWFnZVRpbnRNb2RlKiAI8AQQjgIYuQEiFXNldEJhY2tncm91bmREcmF3YWJsZSofCPEEEI4CGBoiFXNldEJhY2tncm91bmRSZXNvdXJjZSoZCPIEEI4CGNQBIg5zZXRJbWFnZUJpdG1hcCobCPMEEI4CGLkBIhBzZXRJbWFnZURyYXdhYmxlKhcI9AQQjgIYGiINc2V0SW1hZ2VMZXZlbCoaCPUEEI4CGBoiEHNldEltYWdlUmVzb3VyY2UqFgj2BBCOAhjVASILc2V0SW1hZ2VVUkkqJwj3BBCOAhjLASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TGlzdConCPgEEI4CGMwBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRNb2RlKiII+QQQjgIYywEiF3NldFN1cHBvcnRJbWFnZVRpbnRMaXN0KiII+gQQjgIYzAEiF3NldFN1cHBvcnRJbWFnZVRpbnRNb2RlKicI+wQQkAIYxwEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwj8BBCQAhjIASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSoiCP0EEJACGMcBIhdnZXRTdXBwb3J0SW1hZ2VUaW50TGlzdCoiCP4EEJACGMgBIhdnZXRTdXBwb3J0SW1hZ2VUaW50TW9kZSogCP8EEJACGLkBIhVzZXRCYWNrZ3JvdW5kRHJhd2FibGUqHwiABRCQAhgaIhVzZXRCYWNrZ3JvdW5kUmVzb3VyY2UqGQiBBRCQAhjUASIOc2V0SW1hZ2VCaXRtYXAqGwiCBRCQAhi5ASIQc2V0SW1hZ2VEcmF3YWJsZSoXCIMFEJACGBoiDXNldEltYWdlTGV2ZWwqGgiEBRCQAhgaIhBzZXRJbWFnZVJlc291cmNlKhYIhQUQkAIY1QEiC3NldEltYWdlVVJJKicIhgUQkAIYywEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwiHBRCQAhjMASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSoiCIgFEJACGMsBIhdzZXRTdXBwb3J0SW1hZ2VUaW50TGlzdCoiCIkFEJACGMwBIhdzZXRTdXBwb3J0SW1hZ2VUaW50TW9kZSonCIoFEJECGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIiwUQkQIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgiMBRCRAhjHASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgiNBRCRAhjIASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqIAiOBRCRAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8IjwUQkQIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKicIkAUQkQIYGiIdc2V0RHJvcERvd25CYWNrZ3JvdW5kUmVzb3VyY2UqHwiRBRCRAhgXIhVzZXRFbW9qaUNvbXBhdEVuYWJsZWQqGQiSBRCRAhjKASIOc2V0S2V5TGlzdGVuZXIqJwiTBRCRAhjLASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TGlzdConCJQFEJECGMwBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRNb2RlKi4IlQUQkQIYywEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRMaXN0Ki4IlgUQkQIYzAEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRNb2RlKiAIlwUQkgIYDCIWZ2V0Q29tcG91bmRQYWRkaW5nTGVmdCohCJgFEJICGM4BIhZnZXRFbW9qaVRleHRWaWV3SGVscGVyKicImQUQkgIYxwEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwiaBRCSAhjIASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSojCJsFEJICGMcBIhhnZXRTdXBwb3J0QnV0dG9uVGludExpc3QqIwicBRCSAhjIASIYZ2V0U3VwcG9ydEJ1dHRvblRpbnRNb2RlKi4InQUQkgIYxwEiI2dldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRMaXN0Ki4IngUQkgIYyAEiI2dldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRNb2RlKhQInwUQkgIYFyIKc2V0QWxsQ2FwcyogCKAFEJICGLkBIhVzZXRCYWNrZ3JvdW5kRHJhd2FibGUqHwihBRCSAhgaIhVzZXRCYWNrZ3JvdW5kUmVzb3VyY2UqGwiiBRCSAhgaIhFzZXRCdXR0b25EcmF3YWJsZSocCKMFEJICGLkBIhFzZXRCdXR0b25EcmF3YWJsZSofCKQFEJICGBciFXNldEVtb2ppQ29tcGF0RW5hYmxlZCoVCKUFEJICGM8BIgpzZXRGaWx0ZXJzKicIpgUQkgIYywEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwinBRCSAhjMASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSojCKgFEJICGMsBIhhzZXRTdXBwb3J0QnV0dG9uVGludExpc3QqIwipBRCSAhjMASIYc2V0U3VwcG9ydEJ1dHRvblRpbnRNb2RlKi4IqgUQkgIYywEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRMaXN0Ki4IqwUQkgIYzAEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRNb2RlKiUIrAUQkwIYDCIbZ2V0RHJvcERvd25Ib3Jpem9udGFsT2Zmc2V0KiMIrQUQkwIYDCIZZ2V0RHJvcERvd25WZXJ0aWNhbE9mZnNldCoaCK4FEJMCGAwiEGdldERyb3BEb3duV2lkdGgqGwivBRCTAhjWASIQZ2V0SW50ZXJuYWxQb3B1cCodCLAFEJMCGMMBIhJnZXRQb3B1cEJhY2tncm91bmQqGgixBRCTAhjXASIPZ2V0UG9wdXBDb250ZXh0KhMIsgUQkwIYOiIJZ2V0UHJvbXB0KicIswUQkwIYxwEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwi0BRCTAhjIASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSoVCLUFEJMCGNgBIgpzZXRBZGFwdGVyKhUItgUQkwIY2QEiCnNldEFkYXB0ZXIqIAi3BRCTAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8IuAUQkwIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKiUIuQUQkwIYGiIbc2V0RHJvcERvd25Ib3Jpem9udGFsT2Zmc2V0KiMIugUQkwIYGiIZc2V0RHJvcERvd25WZXJ0aWNhbE9mZnNldCoaCLsFEJMCGBoiEHNldERyb3BEb3duV2lkdGgqJQi8BRCTAhi5ASIac2V0UG9wdXBCYWNrZ3JvdW5kRHJhd2FibGUqJAi9BRCTAhgaIhpzZXRQb3B1cEJhY2tncm91bmRSZXNvdXJjZSoUCL4FEJMCGLwBIglzZXRQcm9tcHQqJwi/BRCTAhjLASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TGlzdConCMAFEJMCGMwBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRNb2RlKiAIwQUQlwIYDCIWZ2V0QXV0b1NpemVNYXhUZXh0U2l6ZSogCMIFEJcCGAwiFmdldEF1dG9TaXplTWluVGV4dFNpemUqJAjDBRCXAhgMIhpnZXRBdXRvU2l6ZVN0ZXBHcmFudWxhcml0eSooCMQFEJcCGM0BIh1nZXRBdXRvU2l6ZVRleHRBdmFpbGFibGVTaXplcyodCMUFEJcCGAwiE2dldEF1dG9TaXplVGV4dFR5cGUqLwjGBRCXAhjGASIkZ2V0Q3VzdG9tU2VsZWN0aW9uQWN0aW9uTW9kZUNhbGxiYWNrKiEIxwUQlwIYzgEiFmdldEVtb2ppVGV4dFZpZXdIZWxwZXIqJQjIBRCXAhgMIhtnZXRGaXJzdEJhc2VsaW5lVG9Ub3BIZWlnaHQqJwjJBRCXAhgMIh1nZXRMYXN0QmFzZWxpbmVUb0JvdHRvbUhlaWdodCoZCMoFEJcCGNoBIg5nZXRTdXBlckNhbGxlcionCMsFEJcCGMcBIhxnZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicIzAUQlwIYyAEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgjNBRCXAhjHASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgjOBRCXAhjIASIjZ2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqEQjPBRCXAhg6IgdnZXRUZXh0KhwI0AUQlwIY0gEiEWdldFRleHRDbGFzc2lmaWVyKiUI0QUQlwIY2wEiGmdldFRleHRNZXRyaWNzUGFyYW1zQ29tcGF0KhQI0gUQlwIYFyIKc2V0QWxsQ2FwcyopCNMFEJcCGBoiH3NldEF1dG9TaXplVGV4dFR5cGVXaXRoRGVmYXVsdHMqIAjUBRCXAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8I1QUQlwIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKi8I1gUQlwIYyQEiJHNldEN1c3RvbVNlbGVjdGlvbkFjdGlvbk1vZGVDYWxsYmFjayofCNcFEJcCGBciFXNldEVtb2ppQ29tcGF0RW5hYmxlZCoVCNgFEJcCGM8BIgpzZXRGaWx0ZXJzKiUI2QUQlwIYGiIbc2V0Rmlyc3RCYXNlbGluZVRvVG9wSGVpZ2h0KicI2gUQlwIYGiIdc2V0TGFzdEJhc2VsaW5lVG9Cb3R0b21IZWlnaHQqFwjbBRCXAhgaIg1zZXRMaW5lSGVpZ2h0Kh0I3AUQlwIY3AEiEnNldFByZWNvbXB1dGVkVGV4dConCN0FEJcCGMsBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRMaXN0KicI3gUQlwIYzAEiHHNldFN1cHBvcnRCYWNrZ3JvdW5kVGludE1vZGUqLgjfBRCXAhjLASIjc2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludExpc3QqLgjgBRCXAhjMASIjc2V0U3VwcG9ydENvbXBvdW5kRHJhd2FibGVzVGludE1vZGUqHAjhBRCXAhjTASIRc2V0VGV4dENsYXNzaWZpZXIqGAjiBRCXAhjdASINc2V0VGV4dEZ1dHVyZSolCOMFEJcCGN4BIhpzZXRUZXh0TWV0cmljc1BhcmFtc0NvbXBhdCohCOQFEJwCGM4BIhZnZXRFbW9qaVRleHRWaWV3SGVscGVyKicI5QUQnAIYxwEiHGdldFN1cHBvcnRCYWNrZ3JvdW5kVGludExpc3QqJwjmBRCcAhjIASIcZ2V0U3VwcG9ydEJhY2tncm91bmRUaW50TW9kZSouCOcFEJwCGMcBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TGlzdCouCOgFEJwCGMgBIiNnZXRTdXBwb3J0Q29tcG91bmREcmF3YWJsZXNUaW50TW9kZSoUCOkFEJwCGBciCnNldEFsbENhcHMqIAjqBRCcAhi5ASIVc2V0QmFja2dyb3VuZERyYXdhYmxlKh8I6wUQnAIYGiIVc2V0QmFja2dyb3VuZFJlc291cmNlKh8I7AUQnAIYFyIVc2V0RW1vamlDb21wYXRFbmFibGVkKhUI7QUQnAIYzwEiCnNldEZpbHRlcnMqJwjuBRCcAhjLASIcc2V0U3VwcG9ydEJhY2tncm91bmRUaW50TGlzdConCO8FEJwCGMwBIhxzZXRTdXBwb3J0QmFja2dyb3VuZFRpbnRNb2RlKi4I8AUQnAIYywEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRMaXN0Ki4I8QUQnAIYzAEiI3NldFN1cHBvcnRDb21wb3VuZERyYXdhYmxlc1RpbnRNb2RlKhoI8gUQnQIYFyIQc2V0QWxsb3dTdGFja2luZyoUCPMFEJ0CGBciCnNldFN0YWNrZWQqHgj0BRCeAhjfASITZ2V0Rml4ZWRIZWlnaHRNYWpvcioeCPUFEJ4CGN8BIhNnZXRGaXhlZEhlaWdodE1pbm9yKh0I9gUQngIY3wEiEmdldEZpeGVkV2lkdGhNYWpvciodCPcFEJ4CGN8BIhJnZXRGaXhlZFdpZHRoTWlub3IqGwj4BRCeAhjfASIQZ2V0TWluV2lkdGhNYWpvciobCPkFEJ4CGN8BIhBnZXRNaW5XaWR0aE1pbm9yKhwI+gUQngIY4AEiEXNldEF0dGFjaExpc3RlbmVyKiAI+wUQoQIYFyIWc2V0TGlzdFNlbGVjdGlvbkhpZGRlbiooCPwFEKICGOEBIh1zZXRPbkZpdFN5c3RlbVdpbmRvd3NMaXN0ZW5lciooCP0FEKQCGOEBIh1zZXRPbkZpdFN5c3RlbVdpbmRvd3NMaXN0ZW5lcioVCP4FEKUCGAwiC2dldEJhc2VsaW5lKiYI/wUQpQIYDCIcZ2V0QmFzZWxpbmVBbGlnbmVkQ2hpbGRJbmRleCodCIAGEKUCGMMBIhJnZXREaXZpZGVyRHJhd2FibGUqGwiBBhClAhgMIhFnZXREaXZpZGVyUGFkZGluZyoZCIIGEKUCGAwiD2dldERpdmlkZXJXaWR0aCoUCIMGEKUCGAwiCmdldEdyYXZpdHkqGAiEBhClAhgMIg5nZXRPcmllbnRhdGlvbioZCIUGEKUCGAwiD2dldFNob3dEaXZpZGVycyoeCIYGEKUCGAwiFGdldFZpcnR1YWxDaGlsZENvdW50KhcIhwYQpQIYnQEiDGdldFdlaWdodFN1bSocCIgGEKUCGBciEnNldEJhc2VsaW5lQWxpZ25lZComCIkGEKUCGBoiHHNldEJhc2VsaW5lQWxpZ25lZENoaWxkSW5kZXgqHQiKBhClAhi5ASISc2V0RGl2aWRlckRyYXdhYmxlKhsIiwYQpQIYGiIRc2V0RGl2aWRlclBhZGRpbmcqFAiMBhClAhgaIgpzZXRHcmF2aXR5Kh4IjQYQpQIYGiIUc2V0SG9yaXpvbnRhbEdyYXZpdHkqKwiOBhClAhgXIiFzZXRNZWFzdXJlV2l0aExhcmdlc3RDaGlsZEVuYWJsZWQqGAiPBhClAhgaIg5zZXRPcmllbnRhdGlvbioZCJAGEKUCGBoiD3NldFNob3dEaXZpZGVycyocCJEGEKUCGBoiEnNldFZlcnRpY2FsR3Jhdml0eSoXCJIGEKUCGJ4BIgxzZXRXZWlnaHRTdW0qGwiTBhCmAhjiASIQc2V0SG92ZXJMaXN0ZW5lcioWCJQGEKYCGLkBIgtzZXRTZWxlY3RvciolCJUGEKgCGAwiG2dldFNlYXJjaFZpZXdUZXh0TWluV2lkdGhEcCoaCJYGEKgCGBciEHNldEltZVZpc2liaWxpdHkqGAiXBhCoAhjjASINc2V0U2VhcmNoVmlldyoWCJgGEKgCGBoiDHNldFRocmVzaG9sZCoWCJkGECIYDCINZ2V0SW1lT3B0aW9ucyoVCJoGECIYDCIMZ2V0SW5wdXRUeXBlKhQImwYQIhgMIgtnZXRNYXhXaWR0aCobCJwGECIYDCISZ2V0UHJlZmVycmVkSGVpZ2h0KhoInQYQIhgMIhFnZXRQcmVmZXJyZWRXaWR0aCoRCJ4GECIYOiIIZ2V0UXVlcnkqFQifBhAiGDoiDGdldFF1ZXJ5SGludColCKAGECIYDCIcZ2V0U3VnZ2VzdGlvbkNvbW1pdEljb25SZXNJZCofCKEGECIYDCIWZ2V0U3VnZ2VzdGlvblJvd0xheW91dCofCKIGECIY5AEiFWdldFN1Z2dlc3Rpb25zQWRhcHRlcioaCKMGECIY5QEiEHNldEFwcFNlYXJjaERhdGEqFQikBhAiGBciDHNldEljb25pZmllZCoeCKUGECIYFyIVc2V0SWNvbmlmaWVkQnlEZWZhdWx0KhYIpgYQIhgaIg1zZXRJbWVPcHRpb25zKhUIpwYQIhgaIgxzZXRJbnB1dFR5cGUqFAioBhAiGBoiC3NldE1heFdpZHRoKhwIqQYQIhjmASISc2V0T25DbG9zZUxpc3RlbmVyKisIqgYQIhjnASIhc2V0T25RdWVyeVRleHRGb2N1c0NoYW5nZUxpc3RlbmVyKiAIqwYQIhjoASIWc2V0T25RdWVyeVRleHRMaXN0ZW5lcioiCKwGECIY6QEiGHNldE9uU2VhcmNoQ2xpY2tMaXN0ZW5lciohCK0GECIY6gEiF3NldE9uU3VnZ2VzdGlvbkxpc3RlbmVyKhIIrgYQIhi8ASIIc2V0UXVlcnkqFgivBhAiGLwBIgxzZXRRdWVyeUhpbnQqIgiwBhAiGBciGXNldFF1ZXJ5UmVmaW5lbWVudEVuYWJsZWQqGwixBhAiGOsBIhFzZXRTZWFyY2hhYmxlSW5mbyofCLIGECIYFyIWc2V0U3VibWl0QnV0dG9uRW5hYmxlZCofCLMGECIY7AEiFXNldFN1Z2dlc3Rpb25zQWRhcHRlcionCLQGELACGDoiHWdldENvbGxhcHNlQ29udGVudERlc2NyaXB0aW9uKhoItQYQsAIYwwEiD2dldENvbGxhcHNlSWNvbiocCLYGELACGAwiEmdldENvbnRlbnRJbnNldEVuZConCLcGELACGAwiHWdldENvbnRlbnRJbnNldEVuZFdpdGhBY3Rpb25zKh0IuAYQsAIYDCITZ2V0Q29udGVudEluc2V0TGVmdCoeCLkGELACGAwiFGdldENvbnRlbnRJbnNldFJpZ2h0Kh4IugYQsAIYDCIUZ2V0Q29udGVudEluc2V0U3RhcnQqLAi7BhCwAhgMIiJnZXRDb250ZW50SW5zZXRTdGFydFdpdGhOYXZpZ2F0aW9uKiMIvAYQsAIYDCIZZ2V0Q3VycmVudENvbnRlbnRJbnNldEVuZCokCL0GELACGAwiGmdldEN1cnJlbnRDb250ZW50SW5zZXRMZWZ0KiUIvgYQsAIYDCIbZ2V0Q3VycmVudENvbnRlbnRJbnNldFJpZ2h0KiUIvwYQsAIYDCIbZ2V0Q3VycmVudENvbnRlbnRJbnNldFN0YXJ0Kh4IwAYQsAIY7QEiE2dldEN1cnJlbnRNZW51SXRlbXMqEgjBBhCwAhjDASIHZ2V0TG9nbyocCMIGELACGDoiEmdldExvZ29EZXNjcmlwdGlvbioSCMMGELACGMIBIgdnZXRNZW51KhoIxAYQsAIY7gEiD2dldE1lbnVJbmZsYXRlciobCMUGELACGL4BIhBnZXROYXZCdXR0b25WaWV3KikIxgYQsAIYOiIfZ2V0TmF2aWdhdGlvbkNvbnRlbnREZXNjcmlwdGlvbiocCMcGELACGMMBIhFnZXROYXZpZ2F0aW9uSWNvbiomCMgGELACGO8BIhtnZXRPdXRlckFjdGlvbk1lbnVQcmVzZW50ZXIqGgjJBhCwAhjDASIPZ2V0T3ZlcmZsb3dJY29uKhoIygYQsAIY1wEiD2dldFBvcHVwQ29udGV4dCoXCMsGELACGAwiDWdldFBvcHVwVGhlbWUqFQjMBhCwAhg6IgtnZXRTdWJ0aXRsZSoeCM0GELACGPABIhNnZXRTdWJ0aXRsZVRleHRWaWV3KhIIzgYQsAIYOiIIZ2V0VGl0bGUqHgjPBhCwAhgMIhRnZXRUaXRsZU1hcmdpbkJvdHRvbSobCNAGELACGAwiEWdldFRpdGxlTWFyZ2luRW5kKh0I0QYQsAIYDCITZ2V0VGl0bGVNYXJnaW5TdGFydCobCNIGELACGAwiEWdldFRpdGxlTWFyZ2luVG9wKhsI0wYQsAIY8AEiEGdldFRpdGxlVGV4dFZpZXcqFQjUBhCwAhjxASIKZ2V0V3JhcHBlcionCNUGELACGBciHXNldEJhY2tJbnZva2VkQ2FsbGJhY2tFbmFibGVkKicI1gYQsAIYGiIdc2V0Q29sbGFwc2VDb250ZW50RGVzY3JpcHRpb24qKAjXBhCwAhi8ASIdc2V0Q29sbGFwc2VDb250ZW50RGVzY3JpcHRpb24qGQjYBhCwAhgaIg9zZXRDb2xsYXBzZUljb24qGgjZBhCwAhi5ASIPc2V0Q29sbGFwc2VJY29uKhgI2gYQsAIYFyIOc2V0Q29sbGFwc2libGUqJwjbBhCwAhgaIh1zZXRDb250ZW50SW5zZXRFbmRXaXRoQWN0aW9ucyosCNwGELACGBoiInNldENvbnRlbnRJbnNldFN0YXJ0V2l0aE5hdmlnYXRpb24qEQjdBhCwAhgaIgdzZXRMb2dvKhII3gYQsAIYuQEiB3NldExvZ28qHAjfBhCwAhgaIhJzZXRMb2dvRGVzY3JpcHRpb24qHQjgBhCwAhi8ASISc2V0TG9nb0Rlc2NyaXB0aW9uKikI4QYQsAIYGiIfc2V0TmF2aWdhdGlvbkNvbnRlbnREZXNjcmlwdGlvbioqCOIGELACGLwBIh9zZXROYXZpZ2F0aW9uQ29udGVudERlc2NyaXB0aW9uKhsI4wYQsAIYGiIRc2V0TmF2aWdhdGlvbkljb24qHAjkBhCwAhi5ASIRc2V0TmF2aWdhdGlvbkljb24qJwjlBhCwAhjpASIcc2V0TmF2aWdhdGlvbk9uQ2xpY2tMaXN0ZW5lciolCOYGELACGPIBIhpzZXRPbk1lbnVJdGVtQ2xpY2tMaXN0ZW5lcioaCOcGELACGLkBIg9zZXRPdmVyZmxvd0ljb24qFwjoBhCwAhgaIg1zZXRQb3B1cFRoZW1lKhUI6QYQsAIYGiILc2V0U3VidGl0bGUqFgjqBhCwAhi8ASILc2V0U3VidGl0bGUqHgjrBhCwAhgaIhRzZXRTdWJ0aXRsZVRleHRDb2xvciofCOwGELACGMsBIhRzZXRTdWJ0aXRsZVRleHRDb2xvcioSCO0GELACGBoiCHNldFRpdGxlKhMI7gYQsAIYvAEiCHNldFRpdGxlKh4I7wYQsAIYGiIUc2V0VGl0bGVNYXJnaW5Cb3R0b20qGwjwBhCwAhgaIhFzZXRUaXRsZU1hcmdpbkVuZCodCPEGELACGBoiE3NldFRpdGxlTWFyZ2luU3RhcnQqGwjyBhCwAhgaIhFzZXRUaXRsZU1hcmdpblRvcCobCPMGELACGBoiEXNldFRpdGxlVGV4dENvbG9yKhwI9AYQsAIYywEiEXNldFRpdGxlVGV4dENvbG9yKhcI9QYQtQIYDCINZ2V0SW5mbGF0ZWRJZCocCPYGELUCGL0BIhFnZXRMYXlvdXRJbmZsYXRlciobCPcGELUCGAwiEWdldExheW91dFJlc291cmNlKhcI+AYQtQIYGiINc2V0SW5mbGF0ZWRJZCocCPkGELUCGPMBIhFzZXRMYXlvdXRJbmZsYXRlciobCPoGELUCGBoiEXNldExheW91dFJlc291cmNlKh8I+wYQtQIY9AEiFHNldE9uSW5mbGF0ZUxpc3RlbmVyKhcI/AYQtQIYGiINc2V0VmlzaWJpbGl0eSodCP0GELcCGBYiE2dldEFsbG93c0dvbmVXaWRnZXQqEwj+BhC3AhgMIglnZXRNYXJnaW4qEQj/BhC3AhgMIgdnZXRUeXBlKh0IgAcQtwIYFyITc2V0QWxsb3dzR29uZVdpZGdldCoVCIEHELcCGBoiC3NldERwTWFyZ2luKhMIggcQtwIYGiIJc2V0TWFyZ2luKhEIgwcQtwIYGiIHc2V0VHlwZSobCIQHELgCGM0BIhBnZXRSZWZlcmVuY2VkSWRzKhAIhQcQuAIYHSIGc2V0SWRzKhoIhgcQuAIYHSIQc2V0UmVmZXJlbmNlVGFncyobCIcHELgCGPUBIhBzZXRSZWZlcmVuY2VkSWRzKhYIiAcQuQIYDCIMZ2V0TWF4SGVpZ2h0KhUIiQcQuQIYDCILZ2V0TWF4V2lkdGgqFgiKBxC5AhgMIgxnZXRNaW5IZWlnaHQqFQiLBxC5AhgMIgtnZXRNaW5XaWR0aCoeCIwHELkCGAwiFGdldE9wdGltaXphdGlvbkxldmVsKhkIjQcQuQIYDCIPZ2V0UGFkZGluZ1dpZHRoKhgIjgcQuQIYDSIOZ2V0U2NlbmVTdHJpbmcqGgiPBxC5Ahj2ASIPZ2V0U2hhcmVkVmFsdWVzKhsIkAcQuQIY9wEiEHNldENvbnN0cmFpbnRTZXQqDwiRBxC5AhgaIgVzZXRJZCoWCJIHELkCGBoiDHNldE1heEhlaWdodCoVCJMHELkCGBoiC3NldE1heFdpZHRoKhYIlAcQuQIYGiIMc2V0TWluSGVpZ2h0KhUIlQcQuQIYGiILc2V0TWluV2lkdGgqIgiWBxC5Ahj4ASIXc2V0T25Db25zdHJhaW50c0NoYW5nZWQqHgiXBxC5AhgaIhRzZXRPcHRpbWl6YXRpb25MZXZlbCohCJgHEL0CGBciF3NldEZpbHRlclJlZHVuZGFudENhbGxzKhsImQcQvQIYGiIRc2V0R3VpZGVsaW5lQmVnaW4qGQiaBxC9AhgaIg9zZXRHdWlkZWxpbmVFbmQqHgibBxC9AhieASITc2V0R3VpZGVsaW5lUGVyY2VudCoXCJwHEL0CGBoiDXNldFZpc2liaWxpdHkqJgidBxC+AhidASIbZ2V0Qm90dG9tRmFkaW5nRWRnZVN0cmVuZ3RoKhwIngcQvgIYDCISZ2V0TWF4U2Nyb2xsQW1vdW50Kh0InwcQvgIYDCITZ2V0TmVzdGVkU2Nyb2xsQXhlcyoYCKAHEL4CGAwiDmdldFNjcm9sbFJhbmdlKiMIoQcQvgIYnQEiGGdldFRvcEZhZGluZ0VkZ2VTdHJlbmd0aCooCKIHEL4CGJ0BIh1nZXRWZXJ0aWNhbFNjcm9sbEZhY3RvckNvbXBhdCoZCKMHEL4CGBciD3NldEZpbGxWaWV3cG9ydCojCKQHEL4CGBciGXNldE5lc3RlZFNjcm9sbGluZ0VuYWJsZWQqJAilBxC+Ahj5ASIZc2V0T25TY3JvbGxDaGFuZ2VMaXN0ZW5lciojCKYHEL4CGBciGXNldFNtb290aFNjcm9sbGluZ0VuYWJsZWQqJginBxDAAhgXIhxzZXREcmF3RGlzYXBwZWFyaW5nVmlld3NMYXN0Kh4IqAcQwAIY+gEiE3NldExheW91dFRyYW5zaXRpb24qKQipBxDAAhj7ASIec2V0T25BcHBseVdpbmRvd0luc2V0c0xpc3RlbmVyKhAIqgcQwwIYBSIGPGluaXQ+KhAIqwcQxAIYBSIGPGluaXQ+KhAIrAcQxQIYBSIGPGluaXQ+KiUIrQcQxgIY/AEiGmdldE9uQmFja0ludm9rZWREaXNwYXRjaGVyKiYIrgcQyAIY/QEiG2NyZWF0ZU9uQmFja0ludm9rZWRDYWxsYmFjayooCK8HEMgCGP4BIh1yZWdpc3Rlck9uQmFja0ludm9rZWRDYWxsYmFjayoqCLAHEMgCGP8BIh91bnJlZ2lzdGVyT25CYWNrSW52b2tlZENhbGxiYWNrKiQIsQcQygIYgAIiGWxvY2FsZUxpc3RGb3JMYW5ndWFnZVRhZ3MqLQiyBxDMAhiBAiIibG9jYWxlTWFuYWdlckdldEFwcGxpY2F0aW9uTG9jYWxlcyotCLMHEMwCGIICIiJsb2NhbGVNYW5hZ2VyU2V0QXBwbGljYXRpb25Mb2NhbGVzKh0ItAcQzQIYgwIiEnNldExheW91dERpcmVjdGlvbioUCLUHEM0CGIMCIglzZXRMb2NhbGUqGAi2BxDQAhiEAiINdG9MYW5ndWFnZVRhZyolCLcHENECGIUCIhpnZW5lcmF0ZUNvbmZpZ0RlbHRhX2xvY2FsZSoVCLgHENECGIYCIgpnZXRMb2NhbGVzKhwIuQcQ0QIYhwIiEXNldERlZmF1bHRMb2NhbGVzKhUIugcQ0QIYiAIiCnNldExvY2FsZXMqJQi7BxDTAhj8ASIaZ2V0T25CYWNrSW52b2tlZERpc3BhdGNoZXIqKAi8BxDTAhiJAiIdcmVnaXN0ZXJPbkJhY2tQcmVzc2VkQ2FsbGJhY2sqKgi9BxDTAhj/ASIfdW5yZWdpc3Rlck9uQmFja0ludm9rZWRDYWxsYmFjayoiCL4HENUCGAwiGGdldERpc2FibGVkQ29tcG9uZW50RmxhZyoQCL8HENYCGIoCIgVjbGVhciodCMAHENgCGIsCIhJnZXRWYWx1ZUZvckRlbnNpdHkqGAjBBxDaAhiMAiINc2V0QXV0b0NhbmNlbCodCMIHENwCGI0CIhJjcmVhdGVGcm9tWG1sSW5uZXIqJAjDBxDcAhiOAiIZZ2V0Q2hhbmdpbmdDb25maWd1cmF0aW9ucyoSCMQHENwCGI8CIgdpbmZsYXRlKiUIxQcQ3wIYkAIiGmNyZWF0ZUNvbmZpZ3VyYXRpb25Db250ZXh0KhwIxgcQ4QIYkQIiEW9uU2VhcmNoUmVxdWVzdGVkKiUIxwcQ4QIYkgIiGm9uV2luZG93U3RhcnRpbmdBY3Rpb25Nb2RlKiUIyAcQ5AIYkwIiGm9uUHJvdmlkZUtleWJvYXJkU2hvcnRjdXRzKiIIyQcQ5QIYlAIiF29uUG9pbnRlckNhcHR1cmVDaGFuZ2VkKhYIygcQ5gIYlQIiC2dldFJlYWxTaXplKhwIywcQ6AIYlgIiEW9uRHJvcEZvclRleHRWaWV3KhgIzAcQ6AIYlwIiDW9uRHJvcEZvclZpZXcqJgjNBxDqAhhcIhxyZW1vdmVPbkdsb2JhbExheW91dExpc3RlbmVyKhoIzgcQ6wIYVSIQZ2V0VGV4dEFsaWdubWVudCoaCM8HEOsCGFUiEGdldFRleHREaXJlY3Rpb24qGgjQBxDrAhhfIhBzZXRUZXh0QWxpZ25tZW50KhoI0QcQ6wIYXyIQc2V0VGV4dERpcmVjdGlvbiofCNIHEOwCGJgCIhRzZXREcm9wRG93blZpZXdUaGVtZSocCNMHEO4CGJkCIhFnZXRUZXh0Q2xhc3NpZmllcionCNQHEO8CGJoCIhxnZXRDb21wb3VuZERyYXdhYmxlc1JlbGF0aXZlKjoI1QcQ7wIYmwIiL3NldENvbXBvdW5kRHJhd2FibGVzUmVsYXRpdmVXaXRoSW50cmluc2ljQm91bmRzKhgI1gcQ7wIYnAIiDXNldFRleHRMb2NhbGUqGQjXBxDxAhidAiIOZm9yTGFuZ3VhZ2VUYWcqGgjYBxDyAhiAAiIPZm9yTGFuZ3VhZ2VUYWdzKhkI2QcQ8gIYngIiDnNldFRleHRMb2NhbGVzKiUI2gcQ8wIYnwIiGmdldEF1dG9TaXplU3RlcEdyYW51bGFyaXR5KjYI2wcQ8wIYoAIiK3NldEF1dG9TaXplVGV4dFR5cGVVbmlmb3JtV2l0aENvbmZpZ3VyYXRpb24qNAjcBxDzAhihAiIpc2V0QXV0b1NpemVUZXh0VHlwZVVuaWZvcm1XaXRoUHJlc2V0U2l6ZXMqIwjdBxDzAhiiAiIYc2V0Rm9udFZhcmlhdGlvblNldHRpbmdzKhEI3gcQ9AIYowIiBmNyZWF0ZSopCN8HEPYCGKQCIh5jcmVhdGVTdGF0aWNMYXlvdXRGb3JNZWFzdXJpbmcqFgjgBxD2AhifAiILZ2V0TWF4TGluZXMqFAjhBxD6AhhTIgppc0luTGF5b3V0KikI4gcQ+wIYpQIiHmNyZWF0ZVN0YXRpY0xheW91dEZvck1lYXN1cmluZyohCOMHEPwCGKYCIhZkcmF3YWJsZUhvdHNwb3RDaGFuZ2VkKiUI5AcQ/QIYpwIiGmlzU2VsZWN0ZWRDaGlsZFZpZXdFbmFibGVkKiYI5QcQ/QIYqAIiG3NldFNlbGVjdGVkQ2hpbGRWaWV3RW5hYmxlZCogCOYHEP8CGKkCIhVnZXRNYXhBdmFpbGFibGVIZWlnaHQqHQjnBxCBAxiqAiISc2V0RXBpY2VudGVyQm91bmRzKh8I6AcQgQMYqwIiFHNldElzQ2xpcHBlZFRvU2NyZWVuKh0I6QcQggMYrAIiEnNldEVudGVyVHJhbnNpdGlvbiocCOoHEIIDGKwCIhFzZXRFeGl0VHJhbnNpdGlvbioYCOsHEIQDGKsCIg1zZXRUb3VjaE1vZGFsKh0I7AcQhQMYrQIiEmdldExheW91dERpcmVjdGlvbiolCO0HEIYDGK4CIhpyZWZyZXNoQXV0b0NvbXBsZXRlUmVzdWx0cyodCO4HEIYDGK8CIhJzZXRJbnB1dE1ldGhvZE1vZGUqJgjvBxCIAxiwAiIbZmluZE9uQmFja0ludm9rZWREaXNwYXRjaGVyKiMI8AcQiAMY/QEiGG5ld09uQmFja0ludm9rZWRDYWxsYmFjayorCPEHEIgDGP8BIiB0cnlSZWdpc3Rlck9uQmFja0ludm9rZWRDYWxsYmFjayotCPIHEIgDGP8BIiJ0cnlVbnJlZ2lzdGVyT25CYWNrSW52b2tlZENhbGxiYWNrKhgI8wcQiQMYeiIOc2V0VG9vbHRpcFRleHQqGQj0BxCKAxiaASIOZmluaXNoQWZmaW5pdHkqIQj1BxCKAxixAiIWc3RhcnRBY3Rpdml0eUZvclJlc3VsdColCPYHEIoDGLICIhpzdGFydEludGVudFNlbmRlckZvclJlc3VsdCobCPcHEIwDGLMCIhBnZXRTeXN0ZW1TZXJ2aWNlKhYI+AcQjAMYtAIiC25vdGVQcm94eU9wKh0I+QcQjAMYtAIiEm5vdGVQcm94eU9wTm9UaHJvdyoZCPoHEIwDGLUCIg5wZXJtaXNzaW9uVG9PcCoZCPsHEI8DGLYCIg5jaGVja09wTm9UaHJvdyobCPwHEI8DGLcCIhBnZXRPcFBhY2thZ2VOYW1lKhsI/QcQjwMYuAIiEGdldFN5c3RlbVNlcnZpY2UqIgj+BxCQAxi5AiIXZ2V0UGFyZW50QWN0aXZpdHlJbnRlbnQqFwj/BxCQAxi6AiIMbmF2aWdhdGVVcFRvKh8IgAgQkAMYugIiFHNob3VsZFVwUmVjcmVhdGVUYXNrKhoIgQgQkQMYuwIiD3N0YXJ0QWN0aXZpdGllcyoYCIIIEJEDGLwCIg1zdGFydEFjdGl2aXR5KhoIgwgQkwMYvQIiD2dldENvZGVDYWNoZURpcioWCIQIEJMDGL4CIgtnZXREcmF3YWJsZSoeCIUIEJMDGL0CIhNnZXROb0JhY2t1cEZpbGVzRGlyKhMIhggQlQMYvwIiCGdldENvbG9yKhsIhwgQlQMYswIiEGdldFN5c3RlbVNlcnZpY2UqHwiICBCVAxjAAiIUZ2V0U3lzdGVtU2VydmljZU5hbWUqEgiJCBCWAxjBAiIHZ2V0VHlwZSoWCIoIEJcDGMICIgtnZXREcmF3YWJsZSogCIsIEJcDGMMCIhVnZXREcmF3YWJsZUZvckRlbnNpdHkqEwiMCBCYAxjEAiIIZ2V0Q29sb3IqHAiNCBCYAxjFAiIRZ2V0Q29sb3JTdGF0ZUxpc3QqEQiOCBCZAxjGAiIGcmViYXNlKg0IjwgQmgMYxwIiAm9mKhMIkAgQnAMYyAIiCGhhc0dseXBoKh0IkQgQnQMYyQIiEm9wZW5GaWxlRGVzY3JpcHRvcioTCJIIEKADGMoCIghnZXRBbHBoYSoTCJMIEKADGMsCIghnZXRDaGlsZCoWCJQIEKADGMwCIgtnZXREcmF3YWJsZSoZCJUIEKADGM0CIg5pc0F1dG9NaXJyb3JlZCoaCJYIEKADGM4CIg9zZXRBdXRvTWlycm9yZWQqFQiXCBCjAxjPAiIKYXBwbHlUaGVtZSoYCJgIEKMDGM0CIg1jYW5BcHBseVRoZW1lKhkImQgQowMY0AIiDmdldENvbG9yRmlsdGVyKhIImggQowMYjwIiB2luZmxhdGUqFQibCBCjAxjRAiIKc2V0SG90c3BvdCobCJwIEKMDGNICIhBzZXRIb3RzcG90Qm91bmRzKhIInQgQowMY0wIiB3NldFRpbnQqFgieCBCjAxjUAiILc2V0VGludExpc3QqFgifCBCjAxjVAiILc2V0VGludE1vZGUqHQigCBClAxjKAiISZ2V0TGF5b3V0RGlyZWN0aW9uKh0IoQgQpQMY1gIiEnNldExheW91dERpcmVjdGlvbioRCKIIEKYDGNcCIgZnZXRVcmkqFwijCBCmAxjYAiIMbG9hZERyYXdhYmxlKhEIpAgQpgMY2QIiBnRvSWNvbiolCKUIEKgDGNoCIhpjcmVhdGVBZGFwdGl2ZUljb25EcmF3YWJsZSojCKYIEKgDGNsCIhhjcmVhdGVXaXRoQWRhcHRpdmVCaXRtYXAqEwinCBCpAxjcAiIIZ2V0UmVzSWQqGAioCBCpAxjdAiINZ2V0UmVzUGFja2FnZSoSCKkIEKkDGNwCIgdnZXRUeXBlKhEIqggQqQMY1wIiBmdldFVyaSotCKsIEKoDGN4CIiJjcmVhdGVXaXRoQWRhcHRpdmVCaXRtYXBDb250ZW50VXJpKhEIrAgQqwMY3wIiBmNhbmNlbCojCK0IEKsDGOACIhhjcmVhdGVDYW5jZWxsYXRpb25TaWduYWwqGQiuCBCsAxidAiIOZm9yTGFuZ3VhZ2VUYWcqIwivCBCsAxjhAiIYbWF0Y2hlc0xhbmd1YWdlQW5kU2NyaXB0KhsIsAgQrQMY4gIiEGNyZWF0ZUxvY2FsZUxpc3QqHQixCBCtAxjjAiISZ2V0QWRqdXN0ZWREZWZhdWx0KhUIsggQrQMY4wIiCmdldERlZmF1bHQqFgizCBCvAxgdIgxiZWdpblNlY3Rpb24qFAi0CBCvAxgFIgplbmRTZWN0aW9uKhAItQgQsAMY5AIiBXF1ZXJ5KhQItggQsQMYhAIiCWdldFNjcmlwdCobCLcIELIDGOUCIhBhZGRMaWtlbHlTdWJ0YWdzKhQIuAgQsgMY5gIiCWZvckxvY2FsZSoUCLkIELIDGN0CIglnZXRTY3JpcHQqEQi6CBC0AxjnAiIGZXF1YWxzKg8IuwgQtAMY6AIiBGhhc2gqJwi8CBC2AxjpAiIcZ2V0QWNjZXNzaWJpbGl0eU5vZGVQcm92aWRlciolCL0IELYDGOoCIhpwZXJmb3JtQWNjZXNzaWJpbGl0eUFjdGlvbioeCL4IELcDGOsCIhNjcmVhdGVEaXNwbGF5Q3V0b3V0KhsIvwgQtwMY7AIiEGdldEJvdW5kaW5nUmVjdHMqHQjACBC3AxjtAiISZ2V0U2FmZUluc2V0Qm90dG9tKhsIwQgQtwMY7QIiEGdldFNhZmVJbnNldExlZnQqHAjCCBC3AxjtAiIRZ2V0U2FmZUluc2V0UmlnaHQqGgjDCBC3AxjtAiIPZ2V0U2FmZUluc2V0VG9wKh0IxAgQuQMY7gIiEmdldExheW91dERpcmVjdGlvbioXCMUIELkDGO4CIgxnZXRNYXJnaW5FbmQqGQjGCBC5AxjuAiIOZ2V0TWFyZ2luU3RhcnQqGwjHCBC5AxjvAiIQaXNNYXJnaW5SZWxhdGl2ZSohCMgIELkDGPACIhZyZXNvbHZlTGF5b3V0RGlyZWN0aW9uKh0IyQgQuQMY8AIiEnNldExheW91dERpcmVjdGlvbioXCMoIELkDGPACIgxzZXRNYXJnaW5FbmQqGQjLCBC5AxjwAiIOc2V0TWFyZ2luU3RhcnQqIQjMCBC7AxjxAiIWZ2V0QWxwaGFiZXRpY01vZGlmaWVycyogCM0IELsDGPICIhVnZXRDb250ZW50RGVzY3JpcHRpb24qGgjOCBC7AxjzAiIPZ2V0SWNvblRpbnRMaXN0KhoIzwgQuwMY9AIiD2dldEljb25UaW50TW9kZSoeCNAIELsDGPECIhNnZXROdW1lcmljTW9kaWZpZXJzKhkI0QgQuwMY8gIiDmdldFRvb2x0aXBUZXh0KiAI0ggQuwMY9QIiFXNldEFscGhhYmV0aWNTaG9ydGN1dCogCNMIELsDGPYCIhVzZXRDb250ZW50RGVzY3JpcHRpb24qGgjUCBC7Axj3AiIPc2V0SWNvblRpbnRMaXN0KhoI1QgQuwMY+AIiD3NldEljb25UaW50TW9kZSodCNYIELsDGPUCIhJzZXROdW1lcmljU2hvcnRjdXQqFgjXCBC7Axj5AiILc2V0U2hvcnRjdXQqGQjYCBC7Axj2AiIOc2V0VG9vbHRpcFRleHQqHQjZCBC+Axj6AiISZ2V0U2NhbGVkSG92ZXJTbG9wKjUI2ggQvgMY+wIiKnNob3VsZFNob3dNZW51U2hvcnRjdXRzV2hlbktleWJvYXJkUHJlc2VudCoeCNsIEMADGPwCIhNnZXROZXN0ZWRTY3JvbGxBeGVzKhwI3AgQwAMY/QIiEWlzVHJhbnNpdGlvbkdyb3VwKh0I3QgQwAMY/gIiEnNldFRyYW5zaXRpb25Hcm91cCoYCN4IEMIDGP8CIg1vbk5lc3RlZEZsaW5nKhsI3wgQwgMYgAMiEG9uTmVzdGVkUHJlRmxpbmcqHAjgCBDCAxiBAyIRb25OZXN0ZWRQcmVTY3JvbGwqGQjhCBDCAxiCAyIOb25OZXN0ZWRTY3JvbGwqIAjiCBDCAxhiIhZvbk5lc3RlZFNjcm9sbEFjY2VwdGVkKh4I4wgQwgMYgwMiE29uU3RhcnROZXN0ZWRTY3JvbGwqHQjkCBDCAxiEAyISb25TdG9wTmVzdGVkU2Nyb2xsKhwI5QgQwwMYhQMiEXNldFVwZGF0ZUxpc3RlbmVyKhgI5ggQxgMYhgMiDWdldE1heFNjcm9sbFgqGAjnCBDGAxiGAyINZ2V0TWF4U2Nyb2xsWSoYCOgIEMYDGIcDIg1zZXRNYXhTY3JvbGxYKhgI6QgQxgMYhwMiDXNldE1heFNjcm9sbFkqHAjqCBDIAxiIAyIRZ2V0QnV0dG9uVGludExpc3QqHAjrCBDIAxiJAyIRZ2V0QnV0dG9uVGludE1vZGUqHAjsCBDIAxiKAyIRc2V0QnV0dG9uVGludExpc3QqHAjtCBDIAxiLAyIRc2V0QnV0dG9uVGludE1vZGUqHAjuCBDKAxiMAyIRZ2V0QnV0dG9uRHJhd2FibGUqGwjvCBDLAxiNAyIQZ2V0SW1hZ2VUaW50TGlzdCobCPAIEMsDGI4DIhBnZXRJbWFnZVRpbnRNb2RlKhsI8QgQywMYjwMiEHNldEltYWdlVGludExpc3QqGwjyCBDLAxiQAyIQc2V0SW1hZ2VUaW50TW9kZSoYCPMIEM0DGJEDIg1jYW5TY3JvbGxMaXN0KhcI9AgQzQMYkgMiDHNjcm9sbExpc3RCeSobCPUIEM8DGP0CIhBnZXRDbGlwVG9QYWRkaW5nKhkI9ggQ0AMYkwMiDnNob3dBc0Ryb3BEb3duKhsI9wgQ0QMYlAMiEGdldE92ZXJsYXBBbmNob3IqHgj4CBDRAxiVAyITZ2V0V2luZG93TGF5b3V0VHlwZSobCPkIENEDGKsCIhBzZXRPdmVybGFwQW5jaG9yKh4I+ggQ0QMYlgMiE3NldFdpbmRvd0xheW91dFR5cGUqHwj7CBDSAxhSIhVnZXRJbmNsdWRlRm9udFBhZGRpbmcqFgj8CBDSAxifAiILZ2V0TWF4TGluZXMqFgj9CBDSAxifAiILZ2V0TWluTGluZXMqJwj+CBDTAxiaAiIcZ2V0Q29tcG91bmREcmF3YWJsZXNSZWxhdGl2ZSocCP8IENMDGFUiEmdldExheW91dERpcmVjdGlvbioaCIAJENMDGFUiEGdldFRleHREaXJlY3Rpb24qGAiBCRDTAxiXAyINZ2V0VGV4dExvY2FsZSonCIIJENMDGJsCIhxzZXRDb21wb3VuZERyYXdhYmxlc1JlbGF0aXZlKjoIgwkQ0wMYoAIiL3NldENvbXBvdW5kRHJhd2FibGVzUmVsYXRpdmVXaXRoSW50cmluc2ljQm91bmRzKjoIhAkQ0wMYmwIiL3NldENvbXBvdW5kRHJhd2FibGVzUmVsYXRpdmVXaXRoSW50cmluc2ljQm91bmRzKhoIhQkQ0wMYXyIQc2V0VGV4dERpcmVjdGlvbiobCIYJENQDGJ8CIhBnZXRCcmVha1N0cmF0ZWd5KiYIhwkQ1AMYmAMiG2dldENvbXBvdW5kRHJhd2FibGVUaW50TGlzdComCIgJENQDGJkDIhtnZXRDb21wb3VuZERyYXdh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EjNMYW5kcm9pZHgvYWN0aXZpdHkvcmVzdWx0L0FjdGl2aXR5UmVzdWx0UmVnaXN0cnkkMTs6MwgGEi9MYW5kcm9pZHgvZW1vamkyL3RleHQvRW1vamlDb21wYXRJbml0aWFsaXplciQxOzomCAcSIkxhbmRyb2lkeC9mcmFnbWVudC9hcHAvRnJhZ21lbnQkNTs6LQgIEilMYW5kcm9pZHgvZnJhZ21lbnQvYXBwL0ZyYWdtZW50TWFuYWdlciQ2Ozo7CAkSN0xhbmRyb2lkeC9saWZlY3ljbGUvQ29tcG9zaXRlR2VuZXJhdGVkQWRhcHRlcnNPYnNlcnZlcjs6NQgKEjFMYW5kcm9pZHgvbGlmZWN5Y2xlL0Z1bGxMaWZlY3ljbGVPYnNlcnZlckFkYXB0ZXI7OjsICxI3TGFuZHJvaWR4L2xpZmVjeWNsZS9MZWdhY3lTYXZlZFN0YXRlSGFuZGxlQ29udHJvbGxlciQxOzomCAwSIkxhbmRyb2lkeC9saWZlY3ljbGUvTGlmZWN5Y2xpbmckMTs6OAgNEjRMYW5kcm9pZHgvbGlmZWN5Y2xlL0xpdmVEYXRhJExpZmVjeWNsZUJvdW5kT2JzZXJ2ZXI7OjsIDhI3TGFuZHJvaWR4L2xpZmVjeWNsZS9SZWZsZWN0aXZlR2VuZXJpY0xpZmVjeWNsZU9ic2VydmVyOzoxCA8SLUxhbmRyb2lkeC9saWZlY3ljbGUvU2F2ZWRTdGF0ZUhhbmRsZUF0dGFjaGVyOzozCBASL0xhbmRyb2lkeC9saWZlY3ljbGUvU2F2ZWRTdGF0ZUhhbmRsZUNvbnRyb2xsZXI7OjcIERIzTGFuZHJvaWR4L2xpZmVjeWNsZS9TaW5nbGVHZW5lcmF0ZWRBZGFwdGVyT2JzZXJ2ZXI7OiMIEhIfTGFuZHJvaWR4L3NhdmVkc3RhdGUvUmVjcmVhdG9yOzo7CBMSN0xrb3RsaW54L2Nvcm91dGluZXMvYW5kcm9pZC9BbmRyb2lkRXhjZXB0aW9uUHJlSGFuZGxlcjs6NwgUEjNMa290bGluL2Nvcm91dGluZXMvQWJzdHJhY3RDb3JvdXRpbmVDb250ZXh0RWxlbWVudDs6LAgVEihMa290bGluL2Nvcm91dGluZXMvQ29yb3V0aW5lQ29udGV4dCRLZXk7OhYIFhISTGphdmEvbGFuZy9PYmplY3Q7OiQIFxIgTGtvdGxpbi9qdm0vZnVuY3Rpb25zL0Z1bmN0aW9uMjs6MAgYEixMa290bGluL2Nvcm91dGluZXMvQ29yb3V0aW5lQ29udGV4dCRFbGVtZW50OzooCBkSJExrb3RsaW4vY29yb3V0aW5lcy9Db3JvdXRpbmVDb250ZXh0OzoFCBoSAVY6GQgbEhVMamF2YS9sYW5nL1Rocm93YWJsZTs6HggcEhpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOzoxCB0SLUxhbmRyb2lkeC9lbW9qaTIvdGV4dC9FbW9qaUNvbXBhdEluaXRpYWxpemVyOzo0CB4SMExhbmRyb2lkeC9saWZlY3ljbGUvUHJvY2Vzc0xpZmVjeWNsZUluaXRpYWxpemVyOzoqCB8SJkxhbmRyb2lkeC9jb3JlL2FwcC9SZW1vdGVBY3Rpb25Db21wYXQ7OjAIIBIsTGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvZHJhd2FibGUvSWNvbkNvbXBhdDs6PAghEjhMYW5kcm9pZHgvdmVyc2lvbmVkcGFyY2VsYWJsZS9DdXN0b21WZXJzaW9uZWRQYXJjZWxhYmxlOzoqCCISJkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L1NlYXJjaFZpZXc7Oh0IIxIZTGFuZHJvaWQvY29udGVudC9Db250ZXh0OzofCCQSG0xhbmRyb2lkL3V0aWwvQXR0cmlidXRlU2V0OzoFCCUSAUk6OQgmEjVMa290bGlueC9jb3JvdXRpbmVzL2FuZHJvaWQvQW5kcm9pZERpc3BhdGNoZXJGYWN0b3J5OzoUCCcSEExqYXZhL3V0aWwvTGlzdDs6MAgoEixMa290bGlueC9jb3JvdXRpbmVzL01haW5Db3JvdXRpbmVEaXNwYXRjaGVyOzoWCCkSEkxqYXZhL2xhbmcvU3RyaW5nOzo5CCoSNUxhbmRyb2lkL3N1cHBvcnQvdjQvYXBwL1JlbW90ZUFjdGlvbkNvbXBhdFBhcmNlbGl6ZXI7Oj8IKxI7TGFuZHJvaWQvc3VwcG9ydC92NC9ncmFwaGljcy9kcmF3YWJsZS9JY29uQ29tcGF0UGFyY2VsaXplcjs6MggsEi5MYW5kcm9pZHgvdmVyc2lvbmVkcGFyY2VsYWJsZS9WZXJzaW9uZWRQYXJjZWw7OjQILRIwTGFuZHJvaWR4L2NvcmUvYXBwL1JlbW90ZUFjdGlvbkNvbXBhdFBhcmNlbGl6ZXI7OjoILhI2TGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvZHJhd2FibGUvSWNvbkNvbXBhdFBhcmNlbGl6ZXI7Oi0ILxIpTGFuZHJvaWR4L3ZlcnNpb25lZHBhcmNlbGFibGUvUGFyY2VsSW1wbDs6UAgwEkxMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L0Ryb3BEYXRhQ29udGVudFByb3ZpZGVyQm91bmRhcnlJbnRlcmZhY2U7OkoIMRJGTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9GZWF0dXJlRmxhZ0hvbGRlckJvdW5kYXJ5SW50ZXJmYWNlOzpJCDISRUxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvSXNvbW9ycGhpY09iamVjdEJvdW5kYXJ5SW50ZXJmYWNlOzpFCDMSQUxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvSnNSZXBseVByb3h5Qm91bmRhcnlJbnRlcmZhY2U7Ol4INBJaTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9Qcm9jZXNzR2xvYmFsQ29uZmlnQ29uc3RhbnRzJFByb2Nlc3NHbG9iYWxDb25maWdNYXBLZXk7OkQINRJATG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9Qcm9jZXNzR2xvYmFsQ29uZmlnQ29uc3RhbnRzOzpACDYSPExvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvUHJvZmlsZUJvdW5kYXJ5SW50ZXJmYWNlOzpFCDcSQUxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvUHJvZmlsZVN0b3JlQm91bmRhcnlJbnRlcmZhY2U7OkgIOBJETG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9Qcm94eUNvbnRyb2xsZXJCb3VuZGFyeUludGVyZmFjZTs6TQg5EklMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1NhZmVCcm93c2luZ1Jlc3BvbnNlQm91bmRhcnlJbnRlcmZhY2U7OkYIOhJCTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9TY3JpcHRIYW5kbGVyQm91bmRhcnlJbnRlcmZhY2U7OkwIOxJITG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9TZXJ2aWNlV29ya2VyQ2xpZW50Qm91bmRhcnlJbnRlcmZhY2U7OlAIPBJMTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9TZXJ2aWNlV29ya2VyQ29udHJvbGxlckJvdW5kYXJ5SW50ZXJmYWNlOzpRCD0STUxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvU2VydmljZVdvcmtlcldlYlNldHRpbmdzQm91bmRhcnlJbnRlcmZhY2U7OkAIPhI8TG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9TdGF0aWNzQm91bmRhcnlJbnRlcmZhY2U7OkoIPxJGTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9UcmFjaW5nQ29udHJvbGxlckJvdW5kYXJ5SW50ZXJmYWNlOzpMCEASSExvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvVmlzdWFsU3RhdGVDYWxsYmFja0JvdW5kYXJ5SW50ZXJmYWNlOzpJCEESRUxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViQXV0aG5DYWxsYmFja0JvdW5kYXJ5SW50ZXJmYWNlOzpDCEISP0xvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViTWVzc2FnZUJvdW5kYXJ5SW50ZXJmYWNlOzpLCEMSR0xvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViTWVzc2FnZUNhbGxiYWNrQm91bmRhcnlJbnRlcmZhY2U7OksIRBJHTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJNZXNzYWdlTGlzdGVuZXJCb3VuZGFyeUludGVyZmFjZTs6YAhFElxMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYk1lc3NhZ2VQYXlsb2FkQm91bmRhcnlJbnRlcmZhY2UkV2ViTWVzc2FnZVBheWxvYWRUeXBlOzpKCEYSRkxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViTWVzc2FnZVBheWxvYWRCb3VuZGFyeUludGVyZmFjZTs6RwhHEkNMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYk1lc3NhZ2VQb3J0Qm91bmRhcnlJbnRlcmZhY2U7OkkISBJFTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJSZXNvdXJjZUVycm9yQm91bmRhcnlJbnRlcmZhY2U7OksISRJHTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJSZXNvdXJjZVJlcXVlc3RCb3VuZGFyeUludGVyZmFjZTs6WAhKElRMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlNldHRpbmdzQm91bmRhcnlJbnRlcmZhY2UkQXR0cmlidXRpb25CZWhhdmlvcjs6VghLElJMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlNldHRpbmdzQm91bmRhcnlJbnRlcmZhY2UkRm9yY2VEYXJrQmVoYXZpb3I7OmMITBJfTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJTZXR0aW5nc0JvdW5kYXJ5SW50ZXJmYWNlJFdlYlZpZXdNZWRpYUludGVncml0eUFwaVN0YXR1czs6VAhNElBMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlNldHRpbmdzQm91bmRhcnlJbnRlcmZhY2UkV2ViYXV0aG5TdXBwb3J0OzpECE4SQExvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViU2V0dGluZ3NCb3VuZGFyeUludGVyZmFjZTs6RghPEkJMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlZpZXdDbGllbnRCb3VuZGFyeUludGVyZmFjZTs6TQhQEklMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlZpZXdDb29raWVNYW5hZ2VyQm91bmRhcnlJbnRlcmZhY2U7OkgIURJETG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJWaWV3UHJvdmlkZXJCb3VuZGFyeUludGVyZmFjZTs6TwhSEktMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlZpZXdQcm92aWRlckZhY3RvcnlCb3VuZGFyeUludGVyZmFjZTs6SAhTEkRMb3JnL2Nocm9taXVtL3N1cHBvcnRfbGliX2JvdW5kYXJ5L1dlYlZpZXdSZW5kZXJlckJvdW5kYXJ5SW50ZXJmYWNlOzpOCFQSSkxvcmcvY2hyb21pdW0vc3VwcG9ydF9saWJfYm91bmRhcnkvV2ViVmlld1JlbmRlcmVyQ2xpZW50Qm91bmRhcnlJbnRlcmZhY2U7OlAIVRJMTG9yZy9jaHJvbWl1bS9zdXBwb3J0X2xpYl9ib3VuZGFyeS9XZWJraXRUb0NvbXBhdENvbnZlcnRlckJvdW5kYXJ5SW50ZXJmYWNlOzoGCFYSAltCOhUIVxIRTGFuZHJvaWQvbmV0L1VyaTs6FwhYEhNMYW5kcm9pZC9vcy9CdW5kbGU7OhcIWRITW0xqYXZhL2xhbmcvU3RyaW5nOzoFCFoSAVo6JQhbEiFMYW5kcm9pZC9jb250ZW50L0NvbnRlbnRQcm92aWRlcjs6JQhcEiFMYW5kcm9pZC9vcy9QYXJjZWxGaWxlRGVzY3JpcHRvcjs6HQhdEhlMYW5kcm9pZC9kYXRhYmFzZS9DdXJzb3I7OiMIXhIfTGphdmEvdXRpbC9jb25jdXJyZW50L0NhbGxhYmxlOzopCF8SJUxqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uSGFuZGxlcjs6IghgEh5MYW5kcm9pZC93ZWJraXQvQ29va2llTWFuYWdlcjs6KwhhEidMYW5kcm9pZC93ZWJraXQvR2VvbG9jYXRpb25QZXJtaXNzaW9uczs6LAhiEihMYW5kcm9pZC93ZWJraXQvU2VydmljZVdvcmtlckNvbnRyb2xsZXI7Oh8IYxIbTGFuZHJvaWQvd2Via2l0L1dlYlN0b3JhZ2U7OhgIZBIUTGphdmEvbGFuZy9SdW5uYWJsZTs6IwhlEh9MamF2YS91dGlsL2NvbmN1cnJlbnQvRXhlY3V0b3I7OhgIZhIUW1tMamF2YS9sYW5nL1N0cmluZzs6JwhnEiNMYW5kcm9pZC93ZWJraXQvV2ViUmVzb3VyY2VSZXF1ZXN0OzooCGgSJExhbmRyb2lkL3dlYmtpdC9XZWJSZXNvdXJjZVJlc3BvbnNlOzoTCGkSD0xqYXZhL3V0aWwvU2V0OzoiCGoSHkxhbmRyb2lkL3dlYmtpdC9WYWx1ZUNhbGxiYWNrOzoaCGsSFkxqYXZhL3V0aWwvQ29sbGVjdGlvbjs6GghsEhZMamF2YS9pby9PdXRwdXRTdHJlYW07OgUIbRIBSjocCG4SGExhbmRyb2lkL2NvbnRlbnQvSW50ZW50OzoqCG8SJltMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7OhwIcBIYTGFuZHJvaWQvd2Via2l0L1dlYlZpZXc7OhgIcRIUTGFuZHJvaWQvb3MvSGFuZGxlcjs6HAhyEhhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTs6EwhzEg9MamF2YS91dGlsL01hcDs6Hwh0EhtMYW5kcm9pZC9hcHAvUGVuZGluZ0ludGVudDs6JAh1EiBMYW5kcm9pZC93ZWJraXQvV2ViQ2hyb21lQ2xpZW50OzoiCHYSHkxhbmRyb2lkL3dlYmtpdC9XZWJWaWV3Q2xpZW50OzogCHcSHExhbmRyb2lkL3dlYmtpdC9XZWJTZXR0aW5nczs6Hgh4EhpMYW5kcm9pZHgvYW5ub3RhdGlvbi9LZWVwOzpGCHkSQkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRWaWV3QXV0b1NpemVIZWxwZXIkSW1wbDIzOzonCHoSI0xhbmRyb2lkL3RleHQvU3RhdGljTGF5b3V0JEJ1aWxkZXI7Oh0IexIZTGFuZHJvaWQvd2lkZ2V0L1RleHRWaWV3OzpGCHwSQkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRWaWV3QXV0b1NpemVIZWxwZXIkSW1wbDI5OzpECH0SQExhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRWaWV3QXV0b1NpemVIZWxwZXIkSW1wbDs6LQh+EilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMTVJbXBsOzoXCH8SE0xhbmRyb2lkL3ZpZXcvVmlldzs6LgiAARIpTGFuZHJvaWR4L2NvcmUvdmlldy9WaWV3Q29tcGF0JEFwaTE2SW1wbDs6OwiBARI2TGFuZHJvaWQvdmlldy9hY2Nlc3NpYmlsaXR5L0FjY2Vzc2liaWxpdHlOb2RlUHJvdmlkZXI7Oh4IggESGUxhbmRyb2lkL3ZpZXcvVmlld1BhcmVudDs6JAiDARIfTGFuZHJvaWQvdmlldy9WaWV3VHJlZU9ic2VydmVyOzo7CIQBEjZMYW5kcm9pZC92aWV3L1ZpZXdUcmVlT2JzZXJ2ZXIkT25HbG9iYWxMYXlvdXRMaXN0ZW5lcjs6KQiFARIkTGFuZHJvaWQvZ3JhcGhpY3MvZHJhd2FibGUvRHJhd2FibGU7Oi4IhgESKUxhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld0NvbXBhdCRBcGkxN0ltcGw7OhsIhwESFkxhbmRyb2lkL3ZpZXcvRGlzcGxheTs6HQiIARIYTGFuZHJvaWQvZ3JhcGhpY3MvUGFpbnQ7Oi4IiQESKUxhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld0NvbXBhdCRBcGkxOUltcGw7OjQIigESL0xhbmRyb2lkL3ZpZXcvYWNjZXNzaWJpbGl0eS9BY2Nlc3NpYmlsaXR5RXZlbnQ7Oi4IiwESKUxhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld0NvbXBhdCRBcGkyMEltcGw7OiAIjAESG0xhbmRyb2lkL3ZpZXcvV2luZG93SW5zZXRzOzowCI0BEitMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMjFJbXBsJDE7OjUIjgESMExhbmRyb2lkeC9jb3JlL3ZpZXcvT25BcHBseVdpbmRvd0luc2V0c0xpc3RlbmVyOzouCI8BEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMjFJbXBsOzosCJABEidMYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdDs6HAiRARIXTGFuZHJvaWQvZ3JhcGhpY3MvUmVjdDs6BgiSARIBRjoHCJMBEgJbSTopCJQBEiRMYW5kcm9pZC9jb250ZW50L3Jlcy9Db2xvclN0YXRlTGlzdDs6JwiVARIiTGFuZHJvaWQvZ3JhcGhpY3MvUG9ydGVyRHVmZiRNb2RlOzouCJYBEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMjNJbXBsOzouCJcBEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMjhJbXBsOzpGCJgBEkFMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkT25VbmhhbmRsZWRLZXlFdmVudExpc3RlbmVyQ29tcGF0OzouCJkBEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMjlJbXBsOzouCJoBEilMYW5kcm9pZC92aWV3L1ZpZXckQWNjZXNzaWJpbGl0eURlbGVnYXRlOzolCJsBEiBMYW5kcm9pZC9jb250ZW50L3Jlcy9UeXBlZEFycmF5OzouCJwBEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMzBJbXBsOzouCJ0BEilMYW5kcm9pZHgvY29yZS92aWV3L1ZpZXdDb21wYXQkQXBpMzFJbXBsOzorCJ4BEiZMYW5kcm9pZHgvY29yZS92aWV3L0NvbnRlbnRJbmZvQ29tcGF0OzoyCJ8BEi1MYW5kcm9pZHgvY29yZS92aWV3L09uUmVjZWl2ZUNvbnRlbnRMaXN0ZW5lcjs6OgigARI1TGFuZHJvaWR4L2NvcmUvdmlldy9XaW5kb3dJbnNldHNDb21wYXQkQnVpbGRlckltcGwyMDs6JAihARIfTGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvSW5zZXRzOzo6CKIBEjVMYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdCRCdWlsZGVySW1wbDI5Ozo6CKMBEjVMYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdCRCdWlsZGVySW1wbDMwOzo4CKQBEjNMYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdCRCdWlsZGVySW1wbDs6MwilARIuTGFuZHJvaWR4L2NvcmUvdmlldy9XaW5kb3dJbnNldHNDb21wYXQkSW1wbDIwOzolCKYBEiBbTGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvSW5zZXRzOzozCKcBEi5MYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdCRJbXBsMjE7OjMIqAESLkxhbmRyb2lkeC9jb3JlL3ZpZXcvV2luZG93SW5zZXRzQ29tcGF0JEltcGwyODs6LQipARIoTGFuZHJvaWR4L2NvcmUvdmlldy9EaXNwbGF5Q3V0b3V0Q29tcGF0OzozCKoBEi5MYW5kcm9pZHgvY29yZS92aWV3L1dpbmRvd0luc2V0c0NvbXBhdCRJbXBsMjk7OjMIqwESLkxhbmRyb2lkeC9jb3JlL3ZpZXcvV2luZG93SW5zZXRzQ29tcGF0JEltcGwzMDs6MQisARIsTGFuZHJvaWR4L2NvcmUvdmlldy9XaW5kb3dJbnNldHNDb21wYXQkSW1wbDs6NwitARIyTGFuZHJvaWR4L2NvcmUvdmlldy9XaW5kb3dJbnNldHNDb21wYXQkVHlwZUltcGwzMDs6NgiuARIxTGFuZHJvaWR4L2NvcmUvd2lkZ2V0L0VkZ2VFZmZlY3RDb21wYXQkQXBpMjFJbXBsOzogCK8BEhtMYW5kcm9pZC93aWRnZXQvRWRnZUVmZmVjdDs6NgiwARIxTGFuZHJvaWR4L2NvcmUvd2lkZ2V0L0VkZ2VFZmZlY3RDb21wYXQkQXBpMzFJbXBsOzoqCLEBEiVMYW5kcm9pZHgvYWN0aXZpdHkvQ29tcG9uZW50QWN0aXZpdHk7Oi8IsgESKkxhbmRyb2lkeC9hcHBjb21wYXQvYXBwL0FwcENvbXBhdEFjdGl2aXR5OzozCLMBEi5MYW5kcm9pZHgvbGlmZWN5Y2xlL1Byb2Nlc3NMaWZlY3ljbGVPd25lciQzJDE7OjEItAESLExhbmRyb2lkeC9saWZlY3ljbGUvUHJvY2Vzc0xpZmVjeWNsZU93bmVyJDM7Oi8ItQESKkxhbmRyb2lkeC9saWZlY3ljbGUvUHJvY2Vzc0xpZmVjeWNsZU93bmVyOzo5CLYBEjRMYW5kcm9pZHgvbGlmZWN5Y2xlL0VtcHR5QWN0aXZpdHlMaWZlY3ljbGVDYWxsYmFja3M7OhsItwESFkxhbmRyb2lkL2FwcC9BY3Rpdml0eTs6SAi4ARJDTGFuZHJvaWR4L2xpZmVjeWNsZS9MaWZlY3ljbGVEaXNwYXRjaGVyJERpc3BhdGNoZXJBY3Rpdml0eUNhbGxiYWNrOzouCLkBEilMYW5kcm9pZHgvYWN0aXZpdHkvcmVzdWx0L0FjdGl2aXR5UmVzdWx0OzokCLoBEh9MYW5kcm9pZC9vcy9QYXJjZWxhYmxlJENyZWF0b3I7OjwIuwESN0xhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFNwaW5uZXIkU2F2ZWRTdGF0ZTs6Ngi8ARIxTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvU2VhcmNoVmlldyRTYXZlZFN0YXRlOzozCL0BEi5MYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9Ub29sYmFyJFNhdmVkU3RhdGU7OjcIvgESMkxhbmRyb2lkeC9jb3JlL3dpZGdldC9OZXN0ZWRTY3JvbGxWaWV3JFNhdmVkU3RhdGU7Oi0IvwESKExhbmRyb2lkeC9jdXN0b212aWV3L3ZpZXcvQWJzU2F2ZWRTdGF0ZTs6KwjAARImTGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9CYWNrU3RhY2tTdGF0ZTs6QQjBARI8TGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9GcmFnbWVudE1hbmFnZXIkTGF1bmNoZWRGcmFnbWVudEluZm87OjEIwgESLExhbmRyb2lkeC9mcmFnbWVudC9hcHAvRnJhZ21lbnRNYW5hZ2VyU3RhdGU7OioIwwESJUxhbmRyb2lkeC9mcmFnbWVudC9hcHAvRnJhZ21lbnRTdGF0ZTs6OwjEARI2TGFuZHJvaWR4L2xpZmVjeWNsZS9SZXBvcnRGcmFnbWVudCRMaWZlY3ljbGVDYWxsYmFja3M7Ok8IxQESSkxhbmRyb2lkeC92ZWN0b3JkcmF3YWJsZS9ncmFwaGljcy9kcmF3YWJsZS9WZWN0b3JEcmF3YWJsZUNvbXBhdCRWRnVsbFBhdGg7OkwIxgESR0xhbmRyb2lkeC92ZWN0b3JkcmF3YWJsZS9ncmFwaGljcy9kcmF3YWJsZS9WZWN0b3JEcmF3YWJsZUNvbXBhdCRWR3JvdXA7Oh4IxwESGUxhbmRyb2lkL2dyYXBoaWNzL01hdHJpeDs6SwjIARJGTGFuZHJvaWR4L3ZlY3RvcmRyYXdhYmxlL2dyYXBoaWNzL2RyYXdhYmxlL1ZlY3RvckRyYXdhYmxlQ29tcGF0JFZQYXRoOzo2CMkBEjFbTGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvUGF0aFBhcnNlciRQYXRoRGF0YU5vZGU7OlMIygESTkxhbmRyb2lkeC92ZWN0b3JkcmF3YWJsZS9ncmFwaGljcy9kcmF3YWJsZS9WZWN0b3JEcmF3YWJsZUNvbXBhdCRWUGF0aFJlbmRlcmVyOzpfCMsBElpMYW5kcm9pZHgvdmVjdG9yZHJhd2FibGUvZ3JhcGhpY3MvZHJhd2FibGUvVmVjdG9yRHJhd2FibGVDb21wYXQkVmVjdG9yRHJhd2FibGVDb21wYXRTdGF0ZTs6YQjMARJcTGFuZHJvaWR4L3ZlY3RvcmRyYXdhYmxlL2dyYXBoaWNzL2RyYXdhYmxlL1ZlY3RvckRyYXdhYmxlQ29tcGF0JFZlY3RvckRyYXdhYmxlRGVsZWdhdGVTdGF0ZTs6KQjNARIkTGtvdGxpbngvY29yb3V0aW5lcy9EZWZhdWx0RXhlY3V0b3I7OhcIzgESEkxqYXZhL2xhbmcvVGhyZWFkOzorCM8BEiZMa290bGlueC9jb3JvdXRpbmVzL0V2ZW50TG9vcEltcGxCYXNlOzowCNABEitMa290bGlueC9jb3JvdXRpbmVzL2FuZHJvaWQvSGFuZGxlckNvbnRleHQ7OjUI0QESMExrb3RsaW54L2Nvcm91dGluZXMvYW5kcm9pZC9IYW5kbGVyRGlzcGF0Y2hlckt0OzohCNIBEhxMYW5kcm9pZC92aWV3L0Nob3Jlb2dyYXBoZXI7OjQI0wESL0xrb3RsaW54L2Nvcm91dGluZXMvaW50ZXJuYWwvTGltaXRlZERpc3BhdGNoZXI7OjQI1AESL0xrb3RsaW54L2Nvcm91dGluZXMvaW50ZXJuYWwvTG9ja0ZyZWVUYXNrUXVldWU7OjgI1QESM0xrb3RsaW54L2Nvcm91dGluZXMvaW50ZXJuYWwvTG9ja0ZyZWVUYXNrUXVldWVDb3JlOzo3CNYBEjJMa290bGlueC9jb3JvdXRpbmVzL2ludGVybmFsL1Jlc2l6YWJsZUF0b21pY0FycmF5Ozo3CNcBEjJMamF2YS91dGlsL2NvbmN1cnJlbnQvYXRvbWljL0F0b21pY1JlZmVyZW5jZUFycmF5Ozo+CNgBEjlMa290bGlueC9jb3JvdXRpbmVzL3NjaGVkdWxpbmcvQ29yb3V0aW5lU2NoZWR1bGVyJFdvcmtlcjs6NwjZARIyTGtvdGxpbngvY29yb3V0aW5lcy9zY2hlZHVsaW5nL0Nvcm91dGluZVNjaGVkdWxlcjs6LgjaARIpTGtvdGxpbngvY29yb3V0aW5lcy9zY2hlZHVsaW5nL1dvcmtRdWV1ZTs6OQjbARI0TGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvY29yZS9Tb2x2ZXJWYXJpYWJsZSRUeXBlOzo6CNwBEjVbTGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvY29yZS9Tb2x2ZXJWYXJpYWJsZSRUeXBlOzpDCN0BEj5MYW5kcm9pZHgvY29uc3RyYWludGxheW91dC9jb3JlL3dpZGdldHMvQ29uc3RyYWludEFuY2hvciRUeXBlOzpECN4BEj9bTGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvY29yZS93aWRnZXRzL0NvbnN0cmFpbnRBbmNob3IkVHlwZTs6UQjfARJMTGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvY29yZS93aWRnZXRzL0NvbnN0cmFpbnRXaWRnZXQkRGltZW5zaW9uQmVoYXZpb3VyOzpSCOABEk1bTGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvY29yZS93aWRnZXRzL0NvbnN0cmFpbnRXaWRnZXQkRGltZW5zaW9uQmVoYXZpb3VyOzpKCOEBEkVMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC9jb3JlL3dpZGdldHMvYW5hbHl6ZXIvRGVwZW5kZW5jeU5vZGUkVHlwZTs6SwjiARJGW0xhbmRyb2lkeC9jb25zdHJhaW50bGF5b3V0L2NvcmUvd2lkZ2V0cy9hbmFseXplci9EZXBlbmRlbmN5Tm9kZSRUeXBlOzpICOMBEkNMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC9jb3JlL3dpZGdldHMvYW5hbHl6ZXIvV2lkZ2V0UnVuJFJ1blR5cGU7OkkI5AESRFtMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC9jb3JlL3dpZGdldHMvYW5hbHl6ZXIvV2lkZ2V0UnVuJFJ1blR5cGU7OkkI5QESRExhbmRyb2lkeC9jb25zdHJhaW50bGF5b3V0L3dpZGdldC9Db25zdHJhaW50QXR0cmlidXRlJEF0dHJpYnV0ZVR5cGU7OkoI5gESRVtMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC93aWRnZXQvQ29uc3RyYWludEF0dHJpYnV0ZSRBdHRyaWJ1dGVUeXBlOzpPCOcBEkpMYW5kcm9pZHgvZnJhZ21lbnQvYXBwL1NwZWNpYWxFZmZlY3RzQ29udHJvbGxlciRPcGVyYXRpb24kTGlmZWN5Y2xlSW1wYWN0OzpQCOgBEktbTGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9TcGVjaWFsRWZmZWN0c0NvbnRyb2xsZXIkT3BlcmF0aW9uJExpZmVjeWNsZUltcGFjdDs6RQjpARJATGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9TcGVjaWFsRWZmZWN0c0NvbnRyb2xsZXIkT3BlcmF0aW9uJFN0YXRlOzpGCOoBEkFbTGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9TcGVjaWFsRWZmZWN0c0NvbnRyb2xsZXIkT3BlcmF0aW9uJFN0YXRlOzopCOsBEiRMYW5kcm9pZHgvbGlmZWN5Y2xlL0xpZmVjeWNsZSRFdmVudDs6KgjsARIlW0xhbmRyb2lkeC9saWZlY3ljbGUvTGlmZWN5Y2xlJEV2ZW50OzopCO0BEiRMYW5kcm9pZHgvbGlmZWN5Y2xlL0xpZmVjeWNsZSRTdGF0ZTs6KgjuARIlW0xhbmRyb2lkeC9saWZlY3ljbGUvTGlmZWN5Y2xlJFN0YXRlOzpDCO8BEj5Ma290bGlueC9jb3JvdXRpbmVzL3NjaGVkdWxpbmcvQ29yb3V0aW5lU2NoZWR1bGVyJFdvcmtlclN0YXRlOzpECPABEj9bTGtvdGxpbngvY29yb3V0aW5lcy9zY2hlZHVsaW5nL0Nvcm91dGluZVNjaGVkdWxlciRXb3JrZXJTdGF0ZTs6NgjxARIxTGFuZHJvaWR4L2FwcGNvbXBhdC92aWV3L21lbnUvQWN0aW9uTWVudUl0ZW1WaWV3OzowCPIBEitMYW5kcm9pZHgvYXBwY29tcGF0L3ZpZXcvbWVudS9NZW51SXRlbUltcGw7OjsI8wESNkxhbmRyb2lkeC9hcHBjb21wYXQvdmlldy9tZW51L01lbnVCdWlsZGVyJEl0ZW1JbnZva2VyOzpECPQBEj9MYW5kcm9pZHgvYXBwY29tcGF0L3ZpZXcvbWVudS9BY3Rpb25NZW51SXRlbVZpZXckUG9wdXBDYWxsYmFjazs6NAj1ARIvTGFuZHJvaWR4L2FwcGNvbXBhdC92aWV3L21lbnUvRXhwYW5kZWRNZW51Vmlldzs6NAj2ARIvTGFuZHJvaWR4L2FwcGNvbXBhdC92aWV3L21lbnUvTGlzdE1lbnVJdGVtVmlldzs6Igj3ARIdTGFuZHJvaWQvdmlldy9MYXlvdXRJbmZsYXRlcjs6Mwj4ARIuTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQWN0aW9uQmFyQ29udGFpbmVyOzo6CPkBEjVMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9TY3JvbGxpbmdUYWJDb250YWluZXJWaWV3Ozo1CPoBEjBMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BY3Rpb25CYXJDb250ZXh0Vmlldzs6Nwj7ARIyTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQWN0aW9uQmFyT3ZlcmxheUxheW91dDs6Uwj8ARJOTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQWN0aW9uQmFyT3ZlcmxheUxheW91dCRBY3Rpb25CYXJWaXNpYmlsaXR5Q2FsbGJhY2s7OiMI/QESHkxhbmRyb2lkL3ZpZXcvV2luZG93JENhbGxiYWNrOzovCP4BEipMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BY3Rpb25NZW51Vmlldzs6GAj/ARITTGFuZHJvaWQvdmlldy9NZW51OzpHCIACEkJMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BY3Rpb25NZW51VmlldyRPbk1lbnVJdGVtQ2xpY2tMaXN0ZW5lcjs6NAiBAhIvTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQWN0aW9uTWVudVByZXNlbnRlcjs6PgiCAhI5TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0QXV0b0NvbXBsZXRlVGV4dFZpZXc7OicIgwISIkxhbmRyb2lkL3ZpZXcvQWN0aW9uTW9kZSRDYWxsYmFjazs6JgiEAhIhTGFuZHJvaWQvdGV4dC9tZXRob2QvS2V5TGlzdGVuZXI7OjAIhQISK0xhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdEJ1dHRvbjs6OQiGAhI0TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0RW1vamlUZXh0SGVscGVyOzogCIcCEhtbTGFuZHJvaWQvdGV4dC9JbnB1dEZpbHRlcjs6MgiIAhItTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0Q2hlY2tCb3g7OjkIiQISNExhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdENoZWNrZWRUZXh0Vmlldzs6MgiKAhItTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0RWRpdFRleHQ7Oj4IiwISOUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdEVkaXRUZXh0JFN1cGVyQ2FsbGVyOzocCIwCEhdMYW5kcm9pZC90ZXh0L0VkaXRhYmxlOzoxCI0CEixMYW5kcm9pZC92aWV3L3RleHRjbGFzc2lmaWVyL1RleHRDbGFzc2lmaWVyOzo1CI4CEjBMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRJbWFnZUJ1dHRvbjs6HgiPAhIZTGFuZHJvaWQvZ3JhcGhpY3MvQml0bWFwOzozCJACEi5MYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRJbWFnZVZpZXc7OkMIkQISPkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdE11bHRpQXV0b0NvbXBsZXRlVGV4dFZpZXc7OjUIkgISMExhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFJhZGlvQnV0dG9uOzoxCJMCEixMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRTcGlubmVyOzo+CJQCEjlMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRTcGlubmVyJFNwaW5uZXJQb3B1cDs6HQiVAhIYTGFuZHJvaWQvd2lkZ2V0L0FkYXB0ZXI7OiQIlgISH0xhbmRyb2lkL3dpZGdldC9TcGlubmVyQWRhcHRlcjs6MgiXAhItTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0VGV4dFZpZXc7Oj4ImAISOUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRWaWV3JFN1cGVyQ2FsbGVyOzo2CJkCEjFMYW5kcm9pZHgvY29yZS90ZXh0L1ByZWNvbXB1dGVkVGV4dENvbXBhdCRQYXJhbXM7Oi8ImgISKkxhbmRyb2lkeC9jb3JlL3RleHQvUHJlY29tcHV0ZWRUZXh0Q29tcGF0OzoiCJsCEh1MamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlOzo2CJwCEjFMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRUb2dnbGVCdXR0b247OjAInQISK0xhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0J1dHRvbkJhckxheW91dDs6MwieAhIuTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQ29udGVudEZyYW1lTGF5b3V0OzoeCJ8CEhlMYW5kcm9pZC91dGlsL1R5cGVkVmFsdWU7OkQIoAISP0xhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0NvbnRlbnRGcmFtZUxheW91dCRPbkF0dGFjaExpc3RlbmVyOzoxCKECEixMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9Ecm9wRG93bkxpc3RWaWV3Ozo2CKICEjFMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9GaXRXaW5kb3dzRnJhbWVMYXlvdXQ7Ok8IowISSkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0ZpdFdpbmRvd3NWaWV3R3JvdXAkT25GaXRTeXN0ZW1XaW5kb3dzTGlzdGVuZXI7OjcIpAISMkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0ZpdFdpbmRvd3NMaW5lYXJMYXlvdXQ7OjMIpQISLkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0xpbmVhckxheW91dENvbXBhdDs6RQimAhJATGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvTWVudVBvcHVwV2luZG93JE1lbnVEcm9wRG93bkxpc3RWaWV3Ozo2CKcCEjFMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9NZW51SXRlbUhvdmVyTGlzdGVuZXI7Oj4IqAISOUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L1NlYXJjaFZpZXckU2VhcmNoQXV0b0NvbXBsZXRlOzoyCKkCEi1MYW5kcm9pZHgvY3Vyc29yYWRhcHRlci93aWRnZXQvQ3Vyc29yQWRhcHRlcjs6OwiqAhI2TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvU2VhcmNoVmlldyRPbkNsb3NlTGlzdGVuZXI7Oi4IqwISKUxhbmRyb2lkL3ZpZXcvVmlldyRPbkZvY3VzQ2hhbmdlTGlzdGVuZXI7Oj8IrAISOkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L1NlYXJjaFZpZXckT25RdWVyeVRleHRMaXN0ZW5lcjs6KAitAhIjTGFuZHJvaWQvdmlldy9WaWV3JE9uQ2xpY2tMaXN0ZW5lcjs6QAiuAhI7TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvU2VhcmNoVmlldyRPblN1Z2dlc3Rpb25MaXN0ZW5lcjs6IQivAhIcTGFuZHJvaWQvYXBwL1NlYXJjaGFibGVJbmZvOzooCLACEiNMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9Ub29sYmFyOzoaCLECEhVMamF2YS91dGlsL0FycmF5TGlzdDs6IAiyAhIbTGFuZHJvaWQvdmlldy9NZW51SW5mbGF0ZXI7Oi0IswISKExhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0RlY29yVG9vbGJhcjs6QAi0AhI7TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvVG9vbGJhciRPbk1lbnVJdGVtQ2xpY2tMaXN0ZW5lcjs6Lwi1AhIqTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvVmlld1N0dWJDb21wYXQ7OkEItgISPExhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L1ZpZXdTdHViQ29tcGF0JE9uSW5mbGF0ZUxpc3RlbmVyOzovCLcCEipMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC93aWRnZXQvQmFycmllcjs6OAi4AhIzTGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvd2lkZ2V0L0NvbnN0cmFpbnRIZWxwZXI7OjgIuQISM0xhbmRyb2lkeC9jb25zdHJhaW50bGF5b3V0L3dpZGdldC9Db25zdHJhaW50TGF5b3V0Ozo0CLoCEi9MYW5kcm9pZHgvY29uc3RyYWludGxheW91dC93aWRnZXQvU2hhcmVkVmFsdWVzOzo1CLsCEjBMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC93aWRnZXQvQ29uc3RyYWludFNldDs6Qgi8AhI9TGFuZHJvaWR4L2NvbnN0cmFpbnRsYXlvdXQvd2lkZ2V0L0NvbnN0cmFpbnRzQ2hhbmdlZExpc3RlbmVyOzoxCL0CEixMYW5kcm9pZHgvY29uc3RyYWludGxheW91dC93aWRnZXQvR3VpZGVsaW5lOzosCL4CEidMYW5kcm9pZHgvY29yZS93aWRnZXQvTmVzdGVkU2Nyb2xsVmlldzs6Qwi/AhI+TGFuZHJvaWR4L2NvcmUvd2lkZ2V0L05lc3RlZFNjcm9sbFZpZXckT25TY3JvbGxDaGFuZ2VMaXN0ZW5lcjs6MgjAAhItTGFuZHJvaWR4L2ZyYWdtZW50L2FwcC9GcmFnbWVudENvbnRhaW5lclZpZXc7OikIwQISJExhbmRyb2lkL2FuaW1hdGlvbi9MYXlvdXRUcmFuc2l0aW9uOzo0CMICEi9MYW5kcm9pZC92aWV3L1ZpZXckT25BcHBseVdpbmRvd0luc2V0c0xpc3RlbmVyOzotCMMCEihMYW5kcm9pZHgvbGlmZWN5Y2xlL1NhdmVkU3RhdGVIYW5kbGVzVk07OjwIxAISN0xhbmRyb2lkeC9sb2FkZXIvYXBwL0xvYWRlck1hbmFnZXJJbXBsJExvYWRlclZpZXdNb2RlbDs6RwjFAhJCTGFuZHJvaWR4L2xpZmVjeWNsZS9MZWdhY3lTYXZlZFN0YXRlSGFuZGxlQ29udHJvbGxlciRPblJlY3JlYXRpb247OjQIxgISL0xhbmRyb2lkeC9hY3Rpdml0eS9Db21wb25lbnRBY3Rpdml0eSRBcGkzM0ltcGw7Oi0IxwISKExhbmRyb2lkL3dpbmRvdy9PbkJhY2tJbnZva2VkRGlzcGF0Y2hlcjs6OgjIAhI1TGFuZHJvaWR4L2FjdGl2aXR5L09uQmFja1ByZXNzZWREaXNwYXRjaGVyJEFwaTMzSW1wbDs6KwjJAhImTGFuZHJvaWQvd2luZG93L09uQmFja0ludm9rZWRDYWxsYmFjazs6OQjKAhI0TGFuZHJvaWR4L2FwcGNvbXBhdC9hcHAvQXBwQ29tcGF0RGVsZWdhdGUkQXBpMjRJbXBsOzocCMsCEhdMYW5kcm9pZC9vcy9Mb2NhbGVMaXN0Ozo5CMwCEjRMYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9BcHBDb21wYXREZWxlZ2F0ZSRBcGkzM0ltcGw7Oj0IzQISOExhbmRyb2lkeC9hcHBjb21wYXQvYXBwL0FwcENvbXBhdERlbGVnYXRlSW1wbCRBcGkxN0ltcGw7OigIzgISI0xhbmRyb2lkL2NvbnRlbnQvcmVzL0NvbmZpZ3VyYXRpb247OhcIzwISEkxqYXZhL3V0aWwvTG9jYWxlOzo9CNACEjhMYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9BcHBDb21wYXREZWxlZ2F0ZUltcGwkQXBpMjFJbXBsOzo9CNECEjhMYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9BcHBDb21wYXREZWxlZ2F0ZUltcGwkQXBpMjRJbXBsOzooCNICEiNMYW5kcm9pZHgvY29yZS9vcy9Mb2NhbGVMaXN0Q29tcGF0Ozo9CNMCEjhMYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9BcHBDb21wYXREZWxlZ2F0ZUltcGwkQXBpMzNJbXBsOzozCNQCEi5MYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9BcHBDb21wYXREZWxlZ2F0ZUltcGw7OkcI1QISQkxhbmRyb2lkeC9hcHBjb21wYXQvYXBwL0FwcExvY2FsZXNNZXRhZGF0YUhvbGRlclNlcnZpY2UkQXBpMjRJbXBsOzo4CNYCEjNMYW5kcm9pZHgvYXBwY29tcGF0L2FwcC9SZXNvdXJjZXNGbHVzaGVyJEFwaTE2SW1wbDs6IwjXAhIeTGFuZHJvaWQvdXRpbC9Mb25nU3BhcnNlQXJyYXk7OjsI2AISNkxhbmRyb2lkeC9hcHBjb21wYXQvcmVzb3VyY2VzL0NvbXBhdGliaWxpdHkkQXBpMTVJbXBsOzokCNkCEh9MYW5kcm9pZC9jb250ZW50L3Jlcy9SZXNvdXJjZXM7OjsI2gISNkxhbmRyb2lkeC9hcHBjb21wYXQvcmVzb3VyY2VzL0NvbXBhdGliaWxpdHkkQXBpMThJbXBsOzonCNsCEiJMYW5kcm9pZC9hbmltYXRpb24vT2JqZWN0QW5pbWF0b3I7OjsI3AISNkxhbmRyb2lkeC9hcHBjb21wYXQvcmVzb3VyY2VzL0NvbXBhdGliaWxpdHkkQXBpMjFJbXBsOzojCN0CEh5Mb3JnL3htbHB1bGwvdjEvWG1sUHVsbFBhcnNlcjs6KgjeAhIlTGFuZHJvaWQvY29udGVudC9yZXMvUmVzb3VyY2VzJFRoZW1lOzo8CN8CEjdMYW5kcm9pZHgvYXBwY29tcGF0L3ZpZXcvQ29udGV4dFRoZW1lV3JhcHBlciRBcGkxN0ltcGw7OjII4AISLUxhbmRyb2lkeC9hcHBjb21wYXQvdmlldy9Db250ZXh0VGhlbWVXcmFwcGVyOzo+COECEjlMYW5kcm9pZHgvYXBwY29tcGF0L3ZpZXcvV2luZG93Q2FsbGJhY2tXcmFwcGVyJEFwaTIzSW1wbDs6HwjiAhIaTGFuZHJvaWQvdmlldy9TZWFyY2hFdmVudDs6HgjjAhIZTGFuZHJvaWQvdmlldy9BY3Rpb25Nb2RlOzo+COQCEjlMYW5kcm9pZHgvYXBwY29tcGF0L3ZpZXcvV2luZG93Q2FsbGJhY2tXcmFwcGVyJEFwaTI0SW1wbDs6PgjlAhI5TGFuZHJvaWR4L2FwcGNvbXBhdC92aWV3L1dpbmRvd0NhbGxiYWNrV3JhcHBlciRBcGkyNkltcGw7Oj0I5gISOExhbmRyb2lkeC9hcHBjb21wYXQvdmlldy9tZW51L01lbnVQb3B1cEhlbHBlciRBcGkxN0ltcGw7Oh0I5wISGExhbmRyb2lkL2dyYXBoaWNzL1BvaW50OzpOCOgCEklMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRSZWNlaXZlQ29udGVudEhlbHBlciRPbkRyb3BBcGkyNEltcGw7Oh0I6QISGExhbmRyb2lkL3ZpZXcvRHJhZ0V2ZW50Ozo7COoCEjZMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRTcGlubmVyJEFwaTE2SW1wbDs6OwjrAhI2TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0U3Bpbm5lciRBcGkxN0ltcGw7OjsI7AISNkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFNwaW5uZXIkQXBpMjNJbXBsOzoqCO0CEiVMYW5kcm9pZC93aWRnZXQvVGhlbWVkU3Bpbm5lckFkYXB0ZXI7OkgI7gISQ0xhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRDbGFzc2lmaWVySGVscGVyJEFwaTI2SW1wbDs6PgjvAhI5TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0VGV4dEhlbHBlciRBcGkxN0ltcGw7OioI8AISJVtMYW5kcm9pZC9ncmFwaGljcy9kcmF3YWJsZS9EcmF3YWJsZTs6PgjxAhI5TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0VGV4dEhlbHBlciRBcGkyMUltcGw7Oj4I8gISOUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRIZWxwZXIkQXBpMjRJbXBsOzo+CPMCEjlMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRUZXh0SGVscGVyJEFwaTI2SW1wbDs6Pgj0AhI5TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvQXBwQ29tcGF0VGV4dEhlbHBlciRBcGkyOEltcGw7OiAI9QISG0xhbmRyb2lkL2dyYXBoaWNzL1R5cGVmYWNlOzpKCPYCEkVMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRUZXh0Vmlld0F1dG9TaXplSGVscGVyJEFwaTE2SW1wbDs6JAj3AhIfTGFuZHJvaWQvdGV4dC9MYXlvdXQkQWxpZ25tZW50OzodCPgCEhhMYW5kcm9pZC90ZXh0L1RleHRQYWludDs6IAj5AhIbTGFuZHJvaWQvdGV4dC9TdGF0aWNMYXlvdXQ7OkoI+gISRUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0FwcENvbXBhdFRleHRWaWV3QXV0b1NpemVIZWxwZXIkQXBpMThJbXBsOzpKCPsCEkVMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9BcHBDb21wYXRUZXh0Vmlld0F1dG9TaXplSGVscGVyJEFwaTIzSW1wbDs6Owj8AhI2TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvRHJvcERvd25MaXN0VmlldyRBcGkyMUltcGw7OjsI/QISNkxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0Ryb3BEb3duTGlzdFZpZXckQXBpMzNJbXBsOzohCP4CEhxMYW5kcm9pZC93aWRnZXQvQWJzTGlzdFZpZXc7OjoI/wISNUxhbmRyb2lkeC9hcHBjb21wYXQvd2lkZ2V0L0xpc3RQb3B1cFdpbmRvdyRBcGkyNEltcGw7OiEIgAMSHExhbmRyb2lkL3dpZGdldC9Qb3B1cFdpbmRvdzs6OgiBAxI1TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvTGlzdFBvcHVwV2luZG93JEFwaTI5SW1wbDs6OgiCAxI1TGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvTWVudVBvcHVwV2luZG93JEFwaTIzSW1wbDs6JAiDAxIfTGFuZHJvaWQvdHJhbnNpdGlvbi9UcmFuc2l0aW9uOzo6CIQDEjVMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9NZW51UG9wdXBXaW5kb3ckQXBpMjlJbXBsOzpPCIUDEkpMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9NZW51UG9wdXBXaW5kb3ckTWVudURyb3BEb3duTGlzdFZpZXckQXBpMTdJbXBsOzo1CIYDEjBMYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9TZWFyY2hWaWV3JEFwaTI5SW1wbDs6KgiHAxIlTGFuZHJvaWQvd2lkZ2V0L0F1dG9Db21wbGV0ZVRleHRWaWV3OzoyCIgDEi1MYW5kcm9pZHgvYXBwY29tcGF0L3dpZGdldC9Ub29sYmFyJEFwaTMzSW1wbDs6OAiJAxIzTGFuZHJvaWR4L2FwcGNvbXBhdC93aWRnZXQvVG9vbHRpcENvbXBhdCRBcGkyNkltcGw7OjEIigMSLExhbmRyb2lkeC9jb3JlL2FwcC9BY3Rpdml0eUNvbXBhdCRBcGkxNkltcGw7OiMIiwMSHkxhbmRyb2lkL2NvbnRlbnQvSW50ZW50U2VuZGVyOzo2CIwDEjFMYW5kcm9pZHgvY29yZS9hcHAvQXBwT3BzTWFuYWdlckNvbXBhdCRBcGkyM0ltcGw7OhYIjQMSEUxqYXZhL2xhbmcvQ2xhc3M7OiAIjgMSG0xhbmRyb2lkL2FwcC9BcHBPcHNNYW5hZ2VyOzo2CI8DEjFMYW5kcm9pZHgvY29yZS9hcHAvQXBwT3BzTWFuYWdlckNvbXBhdCRBcGkyOUltcGw7OisIkAMSJkxhbmRyb2lkeC9jb3JlL2FwcC9OYXZVdGlscyRBcGkxNkltcGw7OjQIkQMSL0xhbmRyb2lkeC9jb3JlL2NvbnRlbnQvQ29udGV4dENvbXBhdCRBcGkxNkltcGw7Oh4IkgMSGVtMYW5kcm9pZC9jb250ZW50L0ludGVudDs6NAiTAxIvTGFuZHJvaWR4L2NvcmUvY29udGVudC9Db250ZXh0Q29tcGF0JEFwaTIxSW1wbDs6EwiUAxIOTGphdmEvaW8vRmlsZTs6NAiVAxIvTGFuZHJvaWR4L2NvcmUvY29udGVudC9Db250ZXh0Q29tcGF0JEFwaTIzSW1wbDs6RAiWAxI/TGFuZHJvaWR4L2NvcmUvY29udGVudC9yZXMvRm9udFJlc291cmNlc1BhcnNlckNvbXBhdCRBcGkyMUltcGw7OjoIlwMSNUxhbmRyb2lkeC9jb3JlL2NvbnRlbnQvcmVzL1Jlc291cmNlc0NvbXBhdCRBcGkyMUltcGw7OjoImAMSNUxhbmRyb2lkeC9jb3JlL2NvbnRlbnQvcmVzL1Jlc291cmNlc0NvbXBhdCRBcGkyM0ltcGw7OkYImQMSQUxhbmRyb2lkeC9jb3JlL2NvbnRlbnQvcmVzL1Jlc291cmNlc0NvbXBhdCRUaGVtZUNvbXBhdCRBcGkyOUltcGw7Oi4ImgMSKUxhbmRyb2lkeC9jb3JlL2dyYXBoaWNzL0luc2V0cyRBcGkyOUltcGw7Oh4ImwMSGUxhbmRyb2lkL2dyYXBoaWNzL0luc2V0czs6MwicAxIuTGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvUGFpbnRDb21wYXQkQXBpMjNJbXBsOzo6CJ0DEjVMYW5kcm9pZHgvY29yZS9ncmFwaGljcy9UeXBlZmFjZUNvbXBhdFV0aWwkQXBpMTlJbXBsOzomCJ4DEiFMYW5kcm9pZC9jb250ZW50L0NvbnRlbnRSZXNvbHZlcjs6JAifAxIfTGFuZHJvaWQvb3MvQ2FuY2VsbGF0aW9uU2lnbmFsOzo/CKADEjpMYW5kcm9pZHgvY29yZS9ncmFwaGljcy9kcmF3YWJsZS9EcmF3YWJsZUNvbXBhdCRBcGkxOUltcGw7OkkIoQMSRExhbmRyb2lkL2dyYXBoaWNzL2RyYXdhYmxlL0RyYXdhYmxlQ29udGFpbmVyJERyYXdhYmxlQ29udGFpbmVyU3RhdGU7Oi4IogMSKUxhbmRyb2lkL2dyYXBoaWNzL2RyYXdhYmxlL0luc2V0RHJhd2FibGU7Oj8IowMSOkxhbmRyb2lkeC9jb3JlL2dyYXBoaWNzL2RyYXdhYmxlL0RyYXdhYmxlQ29tcGF0JEFwaTIxSW1wbDs6IwikAxIeTGFuZHJvaWQvZ3JhcGhpY3MvQ29sb3JGaWx0ZXI7Oj8IpQMSOkxhbmRyb2lkeC9jb3JlL2dyYXBoaWNzL2RyYXdhYmxlL0RyYXdhYmxlQ29tcGF0JEFwaTIzSW1wbDs6OwimAxI2TGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvZHJhd2FibGUvSWNvbkNvbXBhdCRBcGkyM0ltcGw7OiUIpwMSIExhbmRyb2lkL2dyYXBoaWNzL2RyYXdhYmxlL0ljb247OjsIqAMSNkxhbmRyb2lkeC9jb3JlL2dyYXBoaWNzL2RyYXdhYmxlL0ljb25Db21wYXQkQXBpMjZJbXBsOzo7CKkDEjZMYW5kcm9pZHgvY29yZS9ncmFwaGljcy9kcmF3YWJsZS9JY29uQ29tcGF0JEFwaTI4SW1wbDs6OwiqAxI2TGFuZHJvaWR4L2NvcmUvZ3JhcGhpY3MvZHJhd2FibGUvSWNvbkNvbXBhdCRBcGkzMEltcGw7OjQIqwMSL0xhbmRyb2lkeC9jb3JlL29zL0NhbmNlbGxhdGlvblNpZ25hbCRBcGkxNkltcGw7OjIIrAMSLUxhbmRyb2lkeC9jb3JlL29zL0xvY2FsZUxpc3RDb21wYXQkQXBpMjFJbXBsOzoyCK0DEi1MYW5kcm9pZHgvY29yZS9vcy9Mb2NhbGVMaXN0Q29tcGF0JEFwaTI0SW1wbDs6GAiuAxITW0xqYXZhL3V0aWwvTG9jYWxlOzotCK8DEihMYW5kcm9pZHgvY29yZS9vcy9UcmFjZUNvbXBhdCRBcGkxOEltcGw7OjQIsAMSL0xhbmRyb2lkeC9jb3JlL3Byb3ZpZGVyL0ZvbnRQcm92aWRlciRBcGkxNkltcGw7Oi0IsQMSKExhbmRyb2lkeC9jb3JlL3RleHQvSUNVQ29tcGF0JEFwaTIxSW1wbDs6LQiyAxIoTGFuZHJvaWR4L2NvcmUvdGV4dC9JQ1VDb21wYXQkQXBpMjRJbXBsOzofCLMDEhpMYW5kcm9pZC9pY3UvdXRpbC9VTG9jYWxlOzoxCLQDEixMYW5kcm9pZHgvY29yZS91dGlsL09iamVjdHNDb21wYXQkQXBpMTlJbXBsOzoYCLUDEhNbTGphdmEvbGFuZy9PYmplY3Q7Oj8ItgMSOkxhbmRyb2lkeC9jb3JlL3ZpZXcvQWNjZXNzaWJpbGl0eURlbGVnYXRlQ29tcGF0JEFwaTE2SW1wbDs6Nwi3AxIyTGFuZHJvaWR4L2NvcmUvdmlldy9EaXNwbGF5Q3V0b3V0Q29tcGF0JEFwaTI4SW1wbDs6IQi4AxIcTGFuZHJvaWQvdmlldy9EaXNwbGF5Q3V0b3V0Ozo8CLkDEjdMYW5kcm9pZHgvY29yZS92aWV3L01hcmdpbkxheW91dFBhcmFtc0NvbXBhdCRBcGkxN0ltcGw7OjAIugMSK0xhbmRyb2lkL3ZpZXcvVmlld0dyb3VwJE1hcmdpbkxheW91dFBhcmFtczs6Mgi7AxItTGFuZHJvaWR4L2NvcmUvdmlldy9NZW51SXRlbUNvbXBhdCRBcGkyNkltcGw7OhwIvAMSF0xhbmRyb2lkL3ZpZXcvTWVudUl0ZW07OgYIvQMSAUM6Owi+AxI2TGFuZHJvaWR4L2NvcmUvdmlldy9WaWV3Q29uZmlndXJhdGlvbkNvbXBhdCRBcGkyOEltcGw7OiUIvwMSIExhbmRyb2lkL3ZpZXcvVmlld0NvbmZpZ3VyYXRpb247OjMIwAMSLkxhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld0dyb3VwQ29tcGF0JEFwaTIxSW1wbDs6HQjBAxIYTGFuZHJvaWQvdmlldy9WaWV3R3JvdXA7OjQIwgMSL0xhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld1BhcmVudENvbXBhdCRBcGkyMUltcGw7Oj4IwwMSOUxhbmRyb2lkeC9jb3JlL3ZpZXcvVmlld1Byb3BlcnR5QW5pbWF0b3JDb21wYXQkQXBpMTlJbXBsOzooCMQDEiNMYW5kcm9pZC92aWV3L1ZpZXdQcm9wZXJ0eUFuaW1hdG9yOzo9CMUDEjhMYW5kcm9pZC9hbmltYXRpb24vVmFsdWVBbmltYXRvciRBbmltYXRvclVwZGF0ZUxpc3RlbmVyOzpLCMYDEkZMYW5kcm9pZHgvY29yZS92aWV3L2FjY2Vzc2liaWxpdHkvQWNjZXNzaWJpbGl0eVJlY29yZENvbXBhdCRBcGkxNUltcGw7OjUIxwMSMExhbmRyb2lkL3ZpZXcvYWNjZXNzaWJpbGl0eS9BY2Nlc3NpYmlsaXR5UmVjb3JkOzo6CMgDEjVMYW5kcm9pZHgvY29yZS93aWRnZXQvQ29tcG91bmRCdXR0b25Db21wYXQkQXBpMjFJbXBsOzokCMkDEh9MYW5kcm9pZC93aWRnZXQvQ29tcG91bmRCdXR0b247OjoIygMSNUxhbmRyb2lkeC9jb3JlL3dpZGdldC9Db21wb3VuZEJ1dHRvbkNvbXBhdCRBcGkyM0ltcGw7OjUIywMSMExhbmRyb2lkeC9jb3JlL3dpZGdldC9JbWFnZVZpZXdDb21wYXQkQXBpMjFJbXBsOzofCMwDEhpMYW5kcm9pZC93aWRnZXQvSW1hZ2VWaWV3Ozo0CM0DEi9MYW5kcm9pZHgvY29yZS93aWRnZXQvTGlzdFZpZXdDb21wYXQkQXBpMTlJbXBsOzoeCM4DEhlMYW5kcm9pZC93aWRnZXQvTGlzdFZpZXc7OjYIzwMSMUxhbmRyb2lkeC9jb3JlL3dpZGdldC9OZXN0ZWRTY3JvbGxWaWV3JEFwaTIxSW1wbDs6NwjQAxIyTGFuZHJvaWR4L2NvcmUvd2lkZ2V0L1BvcHVwV2luZG93Q29tcGF0JEFwaTE5SW1wbDs6NwjRAxIyTGFuZHJvaWR4L2NvcmUvd2lkZ2V0L1BvcHVwV2luZG93Q29tcGF0JEFwaTIzSW1wbDs6NAjSAxIvTGFuZHJvaWR4L2NvcmUvd2lkZ2V0L1RleHRWaWV3Q29tcGF0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nwJaQggIoQESA9sCWkIMCKIBEgfZAt0CJN4CQgcIowESApsBQg4IpAESCYUB2QLdAiTeAkIJCKUBEgTgAs4CQgkIpgESBP0B4gJCCginARIF/QGDAiVCCwioARIG/QEn/wElQggIqQESA/0BWkIJCKoBEgSHAecCQgoIqwESBekCe7cBQgoIrAESBekCf7cBQgkIrQESBO0C3gJCDgiuARIJe4UBhQGFAYUBQggIrwESA3vPAkIICLABEgN7ywJCCgixARIFeyUlJSVCCQiyARIEe5MBJUIHCLMBEgJ7KUIJCLQBEgT1AiVaQgwItQESB3L3AiV7+AJCDgi2ARIJcvcCJSV7+AJ9QgcItwESAv4CQggIuAESA/4CWkIKCLkBEgWAA38lWkIJCLoBEgSAA5EBQggIuwESA4ADWkIJCLwBEgSAA4MDQgcIvQESAocDQggIvgESA6gCJUIKCL8BEgW3AW4lWEIPCMABEgq3AYsDJW4lJSVYQggIwQESAyONA0IJCMIBEgSOAykpQgoIwwESBY4DKSUpQggIxAESA7cBbkIJCMUBEgQjkgNYQggIxgESAyNuWEIHCMcBEgIjJUIICMgBEgObASVCCgjJARIF2QIl3gJCCwjKARIG2QIlJd4CQgcIywESAt4CQggIzAESA4gBKUILCM0BEgaeA1cpnwNCCAjOARIDoQMlQgcIzwESAqIDQggI0AESA4UBWkIJCNEBEgSFAd4CQgsI0gESBoUBkgGSAUILCNMBEgaFASUlJSVCCAjUARIDhQElQgkI1QESBIUBlAFCCQjWARIEhQGVAUIICNcBEgOnAyNCBwjYARICICNCCQjZARIEhQGFAUIJCNoBEgTPAs8CQgcI2wESAq4DQg0I3AESCJ4DV1kpWSkWQgcI3QESArUDQggI3gESA5oBf0IKCN8BEgWaAX8lWEIICOABEgORASdCBwjhARICuANCBwjiARICugNCCAjjARIDugMlQgcI5AESArwDQgoI5QESBbwDvQMlQggI5gESA7wDckIJCOcBEgS8A5QBQgkI6AESBLwDlQFCDQjpARIIvAO9A70DJSVCBwjqARICvwNCBwjrARICwQNCCAjsARIDwQNaQg0I7QESCIIBf5IBkgFaQgwI7gESB4IBf5IBkgFCDAjvARIHggF/JSWTAUIMCPABEgeCAX8lJSUl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EloJCF8QXxgWIgESWgkIYBBgGBYiARJaCQhhEGEYFiIBEloJCGIQYhgWIgESWgkIYxBjGBYiARJaCQhkEGQYFiIBEloJCGUQZRgWIgESWgkIZhBmGBYiARJaCQhnEGcYFiIBEloJCGgQaBgWIgESWgkIaRBpGBYiARJaCQhqEGoYFiIBEloJCGsQaxgWIgESWgkIbBBsGBYiARJaCQhtEG0YFiIBEloJCG4QbhgWIgESWgkIbxBvGBYiARJaCQhwEHAYFiIBEloJCHEQcRgWIgESWgkIchByGBYiARJaCQhzEHMYFiIBEloJCHQQdBgWIgESWgkIdRB1GBYiARJaCQh2EHYYFiIBEloJCHcQdxgWIgESWgkIeBB4GBYiARJaCQh5EHkYFiIBEloJCHoQehgWIgESWgkIexB7GBYiARJaCQh8EHwYFiIBEloJCH0QfRgWIgESWgkIfhB+GBYiARJaCQh/EH8YFiIBEloLCIABEIABGBYiARJaCwiBARCBARgWIgESWgsIggEQggEYFiIBEloLCIMBEIMBGBYiARJaCwiEARCEARgWIgESWgsIhQEQhQEYFiIBEloLCIYBEIYBGBYiARJaCwiHARCHARgWIgESWgsIiAEQiAEYFiIBEloLCIkBEIkBGBYiARJaCwiKARCKARgWIgESWgsIiwEQiwEYFiIBEloLCIwBEIwBGBYiARJaCwiNARCNARgWIgESWgsIjgEQjgEYFiIBEloLCI8BEI8BGBYiARJaCwiQARCQARgWIgESWgsIkQEQkQEYFiIBEloLCJIBEJIBGBYiARJaCwiTARCTARgWIgESWgsIlAEQlAEYFiIBEloLCJUBEJUBGBYiARJaCwiWARCWARgWIgESWgsIlwEQlwEYFiIBEloLCJgBEJgBGBYiARJaCwiZARCZARgWIgESWgsImgEQmgEYFiIBEloLCJsBEJsBGBYiARJaCwicARCcARgWIgESWgsInQEQnQEYFiIBEloLCJ4BEJ4BGBYiARJaCwifARCfARgWIgESWgsIoAEQoAEYFiIBEloLCKEBEKEBGBYiARJaCwiiARCiARgWIgESWgsIowEQowEYFiIBEloLCKQBEKQBGBYiARJaCwilARClARgWIgESWgsIpgEQpgEYFiIBEloLCKcBEKcBGBYiARJaCwioARCoARgWIgESWgsI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YW5ub3RhdGlvbi5LZWVwIGNsYXNzICogeyo7fRgBIgYIDBACGAEqERIBExoCAAEiCAABAgMEBQYHarkBCAQSoAEta2VlcCBjbGFzcyAqIGV4dGVuZHMgYW5kcm9pZHguc3RhcnR1cC5Jbml0aWFsaXplciB7CiAgICAjIEtlZXAgdGhlIHB1YmxpYyBuby1hcmd1bWVudCBjb25zdHJ1Y3RvciB3aGlsZSBhbGxvd2luZyBvdGhlciBtZXRob2RzIHRvIGJlIG9wdGltaXplZC4KICAgIDxpbml0PigpOwp9GAEiBggOEAcYASoIEgIUFSICCAlqYQgFElEta2VlcCBjbGFzcyAqIGltcGxlbWVudHMgYW5kcm9pZHgubGlmZWN5Y2xlLkdlbmVyYXRlZEFkYXB0ZXIgewogICAgPGluaXQ+KC4uLik7Cn0YASIGCAgQDRgBKgBqXggGEkkta2VlcCBjbGFzcyAqIGltcGxlbWVudHMgYW5kcm9pZHgudmVyc2lvbmVkcGFyY2VsYWJsZS5WZXJzaW9uZWRQYXJjZWxhYmxlGAEiBggREAEYASoFEgMWFxhqOwgHEista2VlcCBjbGFzcyBhbmRyb2lkLnN1cHBvcnQuYW5ub3RhdGlvbi5LZWVwGAEiBggJEEQYASoAalkICBJBLWtlZXAgY2xhc3MgYW5kcm9pZHguYXBwY29tcGF0LndpZGdldC5TZWFyY2hWaWV3IHsgPGluaXQ+KC4uLik7IH0YASIGCBMQERgBKggSARkiAwoLDGo5CAkSJi1rZWVwIGNsYXNzIGFuZHJvaWR4LmJyb3dzZXIuKiogeyAqOyB9GAEiBggUEAIYASoAMgEAakgIChI1LWtlZXAgY2xhc3MgY29tLmdvb2dsZS5hbmRyb2lkYnJvd3NlcmhlbHBlci4qKiB7ICo7IH0YASIGCBQQARgBKgAyAQBqXQgLEkQta2VlcCBjbGFzcyBrb3RsaW54LmNvcm91dGluZXMuYW5kcm9pZC5BbmRyb2lkRGlzcGF0Y2hlckZhY3Rvcnkgeyo7fRgBIgYIFRAMGAEqCRIBGiIEDQ4PEGpSCAwSNi1rZWVwIHB1YmxpYyBjbGFzcyBhbmRyb2lkLnN1cHBvcnQuKipQYXJjZWxpemVyIHsgKjsgfRgBIgYIERACGAEqDBICGxwiBhESExQVFmpLCA0SLy1rZWVwIHB1YmxpYyBjbGFzcyBhbmRyb2lkeC4qKlBhcmNlbGl6ZXIgeyAqOyB9GAEiBggREAMYASoMEgIdHiIGFxgZGhscak0IDhI6LWtlZXAgcHVibGljIGNsYXNzIGFuZHJvaWR4LnZlcnNpb25lZHBhcmNlbGFibGUuUGFyY2VsSW1wbBgBIgYIERAEGAEqAxIBH2pSCA8SQi1rZWVwIHB1YmxpYyBjbGFzcyBjb20uYW5kcm9pZC52ZW5kaW5nLmxpY2Vuc2luZy5JTGljZW5zaW5nU2VydmljZRgBIgYICRAWGAEqAGpZCBASSS1rZWVwIHB1YmxpYyBjbGFzcyBjb20uZ29vZ2xlLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2UYASIGCAkQFxgBKgBqUQgREkEta2VlcCBwdWJsaWMgY2xhc3MgY29tLmdvb2dsZS52ZW5kaW5nLmxpY2Vuc2luZy5JTGljZW5zaW5nU2VydmljZRgBIgYICRAVGAEqAGrMAggSEkQta2VlcCBwdWJsaWMgY2xhc3Mgb3JnLmNocm9taXVtLnN1cHBvcnRfbGliX2JvdW5kYXJ5LiogeyBwdWJsaWMgKjsgfRgBIgYIFxAJGAEq9AESJiAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFGhICAwQFBgcICQoLDA0ODxAREhMitQEdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPAZABkQGSAZMBlAGVAZYBlwGYAZkBmgGbAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBMgEAaksIExI6LWtlZXAsYWxsb3dvYmZ1c2NhdGlvbiBAaW50ZXJmYWNlIGFuZHJvaWR4LmFubm90YXRpb24uS2VlcCIGCAwQARgBKgMSAUZqZggUElYta2VlcGNsYXNzZXN3aXRobWVtYmVybmFtZXMsaW5jbHVkZWRlc2NyaXB0b3JjbGFzc2VzIGNsYXNzICogewogICAgbmF0aXZlIDxtZXRob2RzPjsKfRgCIgYICRAdGAEqAGpiCBUSUi1rZWVwY2xhc3Nlc3dpdGhtZW1iZXJzIGNsYXNzICogewogICAgQGFuZHJvaWQuc3VwcG9ydC5hbm5vdGF0aW9uLktlZXAgPGZpZWxkcz47Cn0YASIGCAkQTBgBKgBqZQgWElUta2VlcGNsYXNzZXN3aXRobWVtYmVycyBjbGFzcyAqIHsKICAgIEBhbmRyb2lkLnN1cHBvcnQuYW5ub3RhdGlvbi5LZWVwIDxpbml0PiguLi4pOwp9GAEiBggJEFAYASoAamMIFxJTLWtlZXBjbGFzc2Vzd2l0aG1lbWJlcnMgY2xhc3MgKiB7CiAgICBAYW5kcm9pZC5zdXBwb3J0LmFubm90YXRpb24uS2VlcCA8bWV0aG9kcz47Cn0YASIGCAkQSBgBKgBqXQgYEk0ta2VlcGNsYXNzZXN3aXRobWVtYmVycyBjbGFzcyAqIHsNCiAgICBAYW5kcm9pZHguYW5ub3RhdGlvbi5LZWVwIDxmaWVsZHM+Ow0KfRgBIgYIDBAIGAEqAGpgCBkSUC1rZWVwY2xhc3Nlc3dpdGhtZW1iZXJzIGNsYXNzICogew0KICAgIEBhbmRyb2lkeC5hbm5vdGF0aW9uLktlZXAgPGluaXQ+KC4uLik7DQp9GAEiBggMEAwYASoAal4IGhJOLWtlZXBjbGFzc2Vzd2l0aG1lbWJlcnMgY2xhc3MgKiB7DQogICAgQGFuZHJvaWR4LmFubm90YXRpb24uS2VlcCA8bWV0aG9kcz47DQp9GAEiBggMEAQYASoAaq8BCBsSjAEta2VlcGNsYXNzbWVtYmVybmFtZXMsYWxsb3dvYmZ1c2NhdGlvbixhbGxvd3Nocmlua2luZyBjbGFzcyBhbmRyb2lkeC5hcHBjb21wYXQud2lkZ2V0LkFwcENvbXBhdFRleHRWaWV3QXV0b1NpemVIZWxwZXIkSW1wbCogewogIDxtZXRob2RzPjsKfRgDIgYIExAUGAEqEiIQqQGqAasBrAGtAa4BrwGwAWqMAQgcEnwta2VlcGNsYXNzbWVtYmVybmFtZXMsYWxsb3dvYmZ1c2NhdGlvbixhbGxvd3Nocmlua2luZyBjbGFzcyBhbmRyb2lkeC5jb3JlLmFwcC5Ob3RpZmljYXRpb25Db21wYXQkKiRBcGkqSW1wbCB7CiAgPG1ldGhvZHM+Owp9GAMiBggZEAgYASoAaocBCB0Sdy1rZWVwY2xhc3NtZW1iZXJuYW1lcyxhbGxvd29iZnVzY2F0aW9uLGFsbG93c2hyaW5raW5nIGNsYXNzIGFuZHJvaWR4LmNvcmUub3MuVXNlckhhbmRsZUNvbXBhdCRBcGkqSW1wbCB7CiAgPG1ldGhvZHM+Owp9GAMiBggZEAsYASoAarkCCB4Sby1rZWVwY2xhc3NtZW1iZXJuYW1lcyxhbGxvd29iZnVzY2F0aW9uLGFsbG93c2hyaW5raW5nIGNsYXNzIGFuZHJvaWR4LmNvcmUudmlldy5WaWV3Q29tcGF0JEFwaSogewogIDxtZXRob2RzPjsKfRgDIgYIGRACGAEquQEitgGxAbIBswG0AbUBtgG3AbgBuQG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAccByAHJAcoBywHMAc0BzgHPAdAB0QHSAdMB1AHVAdYB1wHYAdkB2gHbAdwB3QHeAd8B4AHhAeIB4wHkAeUB5gHnAegB6QHqAesB7AHtAe4B7wHwAfEB8gHzAfQB9QH2AfcB+AH5AfoB+wH8Af0B/gH/AYACgQKCAoMChAKFAoYChwKIAokCigKLAmq/AggfEnkta2VlcGNsYXNzbWVtYmVybmFtZXMsYWxsb3dvYmZ1c2NhdGlvbixhbGxvd3Nocmlua2luZyBjbGFzcyBhbmRyb2lkeC5jb3JlLnZpZXcuV2luZG93SW5zZXRzQ29tcGF0JCpJbXBsKiB7CiAgPG1ldGhvZHM+Owp9GAMiBggZEAUYASq1ASKyAYwCjQKOAo8CkAKRApICkwKUApUClgKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+Ar8CwALBAsICwwLEAsUCxgLHAsgCyQLKAssCzALNAs4CzwLQAtEC0gLTAtQC1QLWAtcC2ALZAtoC2wLcAt0C3gLfAuAC4QLiAuMC5AJqlQEIIBJ7LWtlZXBjbGFzc21lbWJlcm5hbWVzLGFsbG93b2JmdXNjYXRpb24sYWxsb3dzaHJpbmtpbmcgY2xhc3MgYW5kcm9pZHguY29yZS53aWRnZXQuRWRnZUVmZmVjdENvbXBhdCRBcGkqSW1wbCB7CiAgPG1ldGhvZHM+Owp9GAMiBggZEA4YASoKIgjlAuYC5wLoAmp2CCESYC1rZWVwY2xhc3NtZW1iZXJzIGNsYXNzICogZXh0ZW5kcyBhbmRyb2lkLmFwcC5BY3Rpdml0eSB7CiAgICBwdWJsaWMgdm9pZCAqKGFuZHJvaWQudmlldy5WaWV3KTsKfRgBIgYICRAoGAEqBiIE6QLqAmqXAQgiElsta2VlcGNsYXNzbWVtYmVycyBjbGFzcyAqIGV4dGVuZHMgYW5kcm9pZHgubGlmZWN5Y2xlLkVtcHR5QWN0aXZpdHlMaWZlY3ljbGVDYWxsYmFja3MgeyAqOyB9GAEiBggaEAIYASosGgIUFSIm6wLsAu0C7gLvAvAC8QLyAvMC9AL1AvYC9wL4AvkC+gL7AvwC/QJqfwgjEmIta2VlcGNsYXNzbWVtYmVycyBjbGFzcyAqIGltcGxlbWVudHMgYW5kcm9pZC5vcy5QYXJjZWxhYmxlIHsKICAgIHB1YmxpYyBzdGF0aWMgZmluYWwgKiogQ1JFQVRPUjsKfRgBIgYICRAyGAEqDRoLFhcYGRobHB0eHyBqYAgkElAta2VlcGNsYXNzbWVtYmVycyBjbGFzcyAqIHsKICAgIEBhbmRyb2lkLndlYmtpdC5KYXZhc2NyaXB0SW50ZXJmYWNlIDxtZXRob2RzPjsKfRgBIgYICRA3GAEqAGpaCCUSSi1rZWVwY2xhc3NtZW1iZXJzIGNsYXNzICoqIHsKICAgIEBhbmRyb2lkeC5saWZlY3ljbGUuT25MaWZlY3ljbGVFdmVudCAqOwp9GAEiBggIEBEYASoAaoMBCCYSUy1rZWVwY2xhc3NtZW1iZXJzIGNsYXNzIGFuZHJvaWR4LmxpZmVjeWNsZS5SZXBvcnRGcmFnbWVudCRMaWZlY3ljbGVDYWxsYmFja3MgeyAqOyB9GAEiBggIEBcYASogIh7+Av8CgAOBA4IDgwOEA4UDhgOHA4gDiQOKA4sDjANq4gEIJxJ+LWtlZXBjbGFzc21lbWJlcnMgY2xhc3MgYW5kcm9pZHgudmVjdG9yZHJhd2FibGUuZ3JhcGhpY3MuZHJhd2FibGUuVmVjdG9yRHJhd2FibGVDb21wYXQkKiB7CiAgIHZvaWQgc2V0KigqKiopOwogICAqKiogZ2V0KigpOwp9GAEiBggeEBAYASpUIlKNA44DjwOQA5EDkgOTA5QDlQOWA5cDmAOZA5oDmwOcA50DngOfA6ADoQOiA6MDpAOlA6YDpwOoA6kDqgOrA6wDrQOuA68DsAOxA7IDswO0A7UDamcIKBJXLWtlZXBjbGFzc21lbWJlcnMgY2xhc3Mga290bGluLmNvcm91dGluZXMuU2FmZUNvbnRpbnVhdGlvbiB7DQogICAgdm9sYXRpbGUgPGZpZWxkcz47DQp9GAEiBggfEAsYASoAanYIKRJKLWtlZXBjbGFzc21lbWJlcnMgY2xhc3Mga290bGlueC5jb3JvdXRpbmVzLioqIHsNCiAgICB2b2xhdGlsZSA8ZmllbGRzPjsNCn0YASIGCB8QBhgBKhkaFyEiIyQlASYnKCkqKywtLi8wMTIzNDU2MgEAaqsBCCoSbS1rZWVwY2xhc3NtZW1iZXJzIGVudW0gKiB7CiAgICBwdWJsaWMgc3RhdGljICoqW10gdmFsdWVzKCk7CiAgICBwdWJsaWMgc3RhdGljICoqIHZhbHVlT2YoamF2YS5sYW5nLlN0cmluZyk7Cn0YASIGCAkQLRgBKi4iLLYDtwO4A7kDugO7A7wDvQO+A78DwAPBA8IDwwPEA8UDxgPHA8gDyQPKA8sDamUIKxJLLWtlZXBjbGFzc21lbWJlcnMgZW51bSBhbmRyb2lkeC5saWZlY3ljbGUuTGlmZWN5Y2xlJEV2ZW50IHsKICAgIDxmaWVsZHM+Owp9GAEiBggIEAYYASoKGgg3ODk6Ozw9PmqyCAgsEmIta2VlcGNsYXNzbWVtYmVycyBwdWJsaWMgY2xhc3MgKiBleHRlbmRzIGFuZHJvaWQudmlldy5WaWV3IHsKICAgIHZvaWQgc2V0KigqKiopOwogICAgKioqIGdldCooKTsKfRgBIgYICRAiGAEqvwcivAfMA80DzgPPA9AD0QPSA9MD1APVA9YD1wPYA9kD2gPbA9wD3QPeA98D4APhA+ID4wPkA+UD5gPnA+gD6QPqA+sD7APtA+4D7wPwA/ED8gPzA/QD9QP2A/cD+AP5A/oD+wP8A/0D/gP/A4AEgQSCBIMEhASFBIYEhwSIBIkEigSLBIwEjQSOBI8EkASRBJIEkwSUBJUElgSXBJgEmQSaBJsEnASdBJ4EnwSgBKEEogSjBKQEpQSmBKcEqASpBKoEqwSsBK0ErgSvBLAEsQSyBLMEtAS1BLYEtwS4BLkEugS7BLwEvQS+BL8EwATBBMIEwwTEBMUExgTHBMgEyQTKBMsEzATNBM4EzwTQBNEE0gTTBNQE1QTWBNcE2ATZBNoE2wTcBN0E3gTfBOAE4QTiBOME5ATlBOYE5wToBOkE6gTrBOwE7QTuBO8E8ATxBPIE8wT0BPUE9gT3BPgE+QT6BPsE/AT9BP4E/wSABYEFggWDBYQFhQWGBYcFiAWJBYoFiwWMBY0FjgWPBZAFkQWSBZMFlAWVBZYFlwWYBZkFmgWbBZwFnQWeBZ8FoAWhBaIFowWkBaUFpgWnBagFqQWqBasFrAWtBa4FrwWwBbEFsgWzBbQFtQW2BbcFuAW5BboFuwW8Bb0FvgW/BcAFwQXCBcMFxAXFBcYFxwXIBckFygXLBcwFzQXOBc8F0AXRBdIF0wXUBdUF1gXXBdgF2QXaBdsF3AXdBd4F3wXgBeEF4gXjBeQF5QXmBecF6AXpBeoF6wXsBe0F7gXvBfAF8QXyBfMF9AX1BfYF9wX4BfkF+gX7BfwF/QX+Bf8FgAaBBoIGgwaEBoUGhgaHBogGiQaKBosGjAaNBo4GjwaQBpEGkgaTBpQGlQaWBpcGmAaZBpoGmwacBp0GngafBqAGoQaiBqMGpAalBqYGpwaoBqkGqgarBqwGrQauBq8GsAaxBrIGswa0BrUGtga3BrgGuQa6BrsGvAa9Br4GvwbABsEGwgbDBsQGxQbGBscGyAbJBsoGywbMBs0GzgbPBtAG0QbSBtMG1AbVBtYG1wbYBtkG2gbbBtwG3QbeBt8G4AbhBuIG4wbkBuUG5gbnBugG6QbqBusG7AbtBu4G7wbwBvEG8gbzBvQG9Qb2BvcG+Ab5BvoG+wb8Bv0G/gb/BoAHgQeCB4MHhAeFB4YHhweIB4kHigeLB4wHjQeOB48HkAeRB5IHkweUB5UHlgeXB5gHmQeaB5sHnAedB54HnwegB6EHogejB6QHpQemB6cHqAepB2qNAQgtEn8ta2VlcGNsYXNzbWVtYmVycyxhbGxvd29iZnVzY2F0aW9uIGNsYXNzICogZXh0ZW5kcyBhbmRyb2lkeC5saWZlY3ljbGUuQW5kcm9pZFZpZXdNb2RlbCB7CiAgICA8aW5pdD4oYW5kcm9pZC5hcHAuQXBwbGljYXRpb24pOwp9IgYIIxAFGAEqAGqyAQguEqMBLWtlZXBjbGFzc21lbWJlcnMsYWxsb3dvYmZ1c2NhdGlvbiBjbGFzcyAqIGV4dGVuZHMgYW5kcm9pZHgubGlmZWN5Y2xlLkFuZHJvaWRWaWV3TW9kZWwgewogICAgPGluaXQ+KGFuZHJvaWQuYXBwLkFwcGxpY2F0aW9uLGFuZHJvaWR4LmxpZmVjeWNsZS5TYXZlZFN0YXRlSGFuZGxlKTsKfSIGCCQQBRgBKgBqdQgvEmEta2VlcGNsYXNzbWVtYmVycyxhbGxvd29iZnVzY2F0aW9uIGNsYXNzICogZXh0ZW5kcyBhbmRyb2lkeC5saWZlY3ljbGUuVmlld01vZGVsIHsKICAgIDxpbml0PigpOwp9IgYIIxABGAEqBiIEqgerB2qTAQgwEoQBLWtlZXBjbGFzc21lbWJlcnMsYWxsb3dvYmZ1c2NhdGlvbiBjbGFzcyAqIGV4dGVuZHMgYW5kcm9pZHgubGlmZWN5Y2xlLlZpZXdNb2RlbCB7CiAgICA8aW5pdD4oYW5kcm9pZHgubGlmZWN5Y2xlLlNhdmVkU3RhdGVIYW5kbGUpOwp9IgYIJBABGAEqAGqOAQgxEnwta2VlcGNsYXNzbWVtYmVycyxhbGxvd29iZnVzY2F0aW9uIGNsYXNzICogaW1wbGVtZW50cyBhbmRyb2lkeC5zYXZlZHN0YXRlLlNhdmVkU3RhdGVSZWdpc3RyeSRBdXRvUmVjcmVhdGVkIHsKICAgIDxpbml0PigpOwp9IgYIJhAPGAEqBCICrAdq9AUIMhJeLWtlZXBjbGFzc21lbWJlcnMsYWxsb3dvYmZ1c2NhdGlvbiBjbGFzcyAqIHsNCiAgQGFuZHJvaWR4LmFubm90YXRpb24uRG9Ob3RJbmxpbmUgPG1ldGhvZHM+Ow0KfSIGCAwQEBgBKocFIoQFrQeuB68HsAexB7IHswe0B7UHtge3B7gHuQe6B7sHvAe9B74HvwfAB8EHwgfDB8QHxQfGB8cHyAfJB8oHywfMB80HzgfPB9AH0QfSB9MH1AfVB9YH1wfYB9kH2gfbB9wH3QfeB98H4AfhB+IH4wfkB+UH5gfnB+gH6QfqB+sH7AftB+4H7wfwB/EH8gfzB/QH9Qf2B/cH+Af5B/oH+wf8B/0H/gf/B4AIgQiCCIMIhAiFCIYIhwiICIkIigiLCIwIjQiOCI8IkAiRCJIIkwiUCJUIlgiXCJgImQiaCJsInAidCJ4InwigCKEIogijCKQIpQimCKcIqAipCKoIqwisCK0IrgivCLAIsQiyCLMItAi1CLYItwi4CLkIugi7CLwIvQi+CL8IwAjBCMIIwwjECMUIxgjHCMgIyQjKCMsIzAjNCM4IzwjQCNEI0gjTCNQI1QjWCNcI2AixAbIBswG0AbUBtgG3AbgBuQG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAccByAHJAcoBywHMAc0BzgHPAdAB0QHSAdMB1AHVAdYB1wHYAdkB3AHdAd4B3wHgAeEB4gHjAeQB5QHmAecB6AHpAeoB6wHsAe0B7gHvAfAB8QHyAfMB9AH1AfcB+AH5AfoB+wH8Af0B/gH/AYACgQKCAoMChAKFAoYChwKIAokCigKLAtkI2gjbCNwI3QjeCN8I4AjhCOII4wjkCOUI5gjnCOgI6QjqCOsI7AjtCO4I5QLmAucC6ALvCPAI8QjyCPMI9Aj1CPYI9wj4CPkI+gj7CPwI/Qj+CP8IgAmBCYIJgwmECYUJhgmHCYgJiQmKCYsJjAmNCY4JjwmQCZEJkglqSwgzEjcta2VlcG5hbWVzIGNsYXNzICogZXh0ZW5kcyBhbmRyb2lkeC5zdGFydHVwLkluaXRpYWxpemVyGAIiBggOEAIYASoEEgIUFWqCAQg0EnIta2VlcG5hbWVzIHB1YmxpYyBjbGFzcyBhbmRyb2lkeC53ZWJraXQuUHJvY2Vzc0dsb2JhbENvbmZpZyB7CiAgICBwcml2YXRlIHN0YXRpYyBmaW5hbCAqKiogc1Byb2Nlc3NHbG9iYWxDb25maWc7Cn0YAiIGCBcQHhgBKgBqSwg1Ejsta2VlcG5hbWVzIHB1YmxpYyBjbGFzcyBhbmRyb2lkeC53ZWJraXQuV2ViVmlld0NsaWVudENvbXBhdBgCIgYIFxAaGAEqAHoUCLYlEI2VARj8kgIgnwwo2yAwiEY=</script>
2716 <script>// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
2717 // for details. All rights reserved. Use of this source code is governed by a
2718 // BSD-style license that can be found in the LICENSE file.
2719 /* ==========================================================================
2720 APPLICATION LOGIC
2721 ==========================================================================
2722 This script handles the interactive features of the Report.
2723 It is divided into:
2724 1. Constants & Configuration
2725 2. UI Utilities (DOM helpers)
2726 3. Main App Controller (Router)
2727 4. ReportApp (The main table/grid view)
2728 ========================================================================== */
2729 /**
2730 * Global Constants used throughout the application.
2731 */
2732 const CONSTANTS = {
2733 VIEWS: {
2734 MODULES: "modules",
2735 PACKAGES: "packages",
2736 DETAILS: "details",
2737 FILE_DETAILS: "file-details",
2738 },
2739 DEFAULTS: {
2740 AGGREGATED: "Aggregated",
2741 },
2742 };
2743 /**
2744 * UI Utilities
2745 * Collection of helper functions for DOM manipulation and common UI patterns.
2746 */
2747 const UIUtils = {
2748 /**
2749 * Returns the CSS class for score percentage coloring.
2750 * @param {number|string} percentage - The score percentage (0-100 or "--").
2751 * @returns {string} CSS class name.
2752 */
2753 getScoreClass(percentage) {
2754 if (percentage === "--") return "text-gray-500";
2755 if (percentage >= 80) return "text-green-600";
2756 if (percentage >= 60) return "text-yellow-600";
2757 return "text-red-600";
2758 },
2759 getMatchClass(percentage) {
2760 if (percentage === "--") return "text-gray-500";
2761 if (percentage < 10) return "text-green-600";
2762 if (percentage < 20) return "text-orange-600";
2763 return "text-red-600";
2764 },
2765 /**
2766 * Toggles the visibility of a DOM element.
2767 * @param {HTMLElement} element - The element to toggle.
2768 * @param {boolean} show - Whether to show or hide.
2769 */
2770 toggleVisibility(element, show) {
2771 if (!element) return;
2772 if (show) {
2773 element.classList.remove("hidden");
2774 // Restore appropriate display type
2775 if (element.id === "ts-selected-state") {
2776 element.style.display = "flex";
2777 } else {
2778 element.style.display = "";
2779 }
2780 } else {
2781 element.classList.add("hidden");
2782 element.style.display = "none";
2783 }
2784 },
2785 /**
2786 * Builds a multi-select dropdown with "Select All" / "Clear" actions (Zone A)
2787 * and a scrollable list of options (Zone B).
2788 *
2789 * @param {string} containerId - ID of the dropdown container.
2790 * @param {Array} options - List of options {name, value}.
2791 * @param {Array} selectedStateArr - Array storing currently selected values.
2792 * @param {Function} onSelectionChange - Callback when selection changes.
2793 * @param {boolean} searchable - Whether to include a search box.
2794 * @param {boolean} multiSelect - Whether to allow multiple selections.
2795 */
2796 buildActionDropdown(
2797 containerId,
2798 options,
2799 selectedStateArr,
2800 onSelectionChange,
2801 searchable = true,
2802 multiSelect = true,
2803 totalCount = null,
2804 itemName = "items",
2805 searchCallback = null,
2806 ) {
2807 const container = document.getElementById(containerId);
2808 if (!container) return;
2809 // Container config
2810 container.innerHTML = "";
2811 container.style.padding = "0";
2812 container.style.overflow = "hidden"; // Clip corners
2813 // --- ZONE 0: Search (Top Level) ---
2814 let searchInput = null;
2815 if (searchable) {
2816 const searchContainer = document.createElement("div");
2817 searchContainer.className = "dropdown-search-zone";
2818 searchContainer.style.padding = "0.5rem";
2819 searchContainer.style.borderBottom = "1px solid var(--border-color)";
2820 searchContainer.style.background = "var(--bg-surface)";
2821 searchInput = document.createElement("input");
2822 searchInput.type = "text";
2823 searchInput.className = "popover-search";
2824 searchInput.style.width = "100%";
2825 searchInput.placeholder = "Search...";
2826 searchContainer.appendChild(searchInput);
2827 container.appendChild(searchContainer);
2828 }
2829 // --- ZONE B: Option List ---
2830 const listZone = document.createElement("div");
2831 listZone.className = "dropdown-scroll-zone";
2832 container.appendChild(listZone);
2833 // Render List Logic
2834 const renderList = (optionsToRender) => {
2835 listZone.innerHTML = "";
2836 if (optionsToRender.length === 0) {
2837 listZone.innerHTML = `<div class="p-4 text-xs text-gray-400 text-center">No options available</div>`;
2838 return;
2839 }
2840 optionsToRender.forEach((opt) => {
2841 // SKIP "All" options if they exist in the passed options list.
2842 if (opt.value === "all") return;
2843 const isChecked = selectedStateArr.includes(opt.value);
2844 const item = document.createElement(multiSelect ? "label" : "div");
2845 item.className =
2846 "popover-item" + (isChecked && !multiSelect ? " active-item" : "");
2847 let checkbox = null;
2848 if (multiSelect) {
2849 checkbox = document.createElement("input");
2850 checkbox.type = "checkbox";
2851 checkbox.className = "popover-checkbox";
2852 checkbox.checked = isChecked;
2853 item.appendChild(checkbox);
2854 }
2855 const label = document.createElement("span");
2856 label.innerHTML = opt.name;
2857 item.appendChild(label);
2858 // Interaction: Toggle Individual
2859 const handleSelect = (e) => {
2860 if (multiSelect) {
2861 if (checkbox.checked) {
2862 if (!selectedStateArr.includes(opt.value))
2863 selectedStateArr.push(opt.value);
2864 } else {
2865 const idx = selectedStateArr.indexOf(opt.value);
2866 if (idx > -1) selectedStateArr.splice(idx, 1);
2867 }
2868 } else {
2869 selectedStateArr.length = 0;
2870 selectedStateArr.push(opt.value);
2871 }
2872 if (!multiSelect) {
2873 renderList(optionsToRender);
2874 }
2875 onSelectionChange();
2876 };
2877 if (multiSelect) {
2878 item.addEventListener("change", (e) => {
2879 e.stopPropagation();
2880 handleSelect(e);
2881 });
2882 } else {
2883 item.addEventListener("click", (e) => {
2884 e.stopPropagation();
2885 handleSelect(e);
2886 });
2887 }
2888 listZone.appendChild(item);
2889 });
2890 };
2891 renderList(options);
2892 // --- ZONE C: Footer ---
2893 let footer = null;
2894 if (totalCount !== null) {
2895 footer = document.createElement("div");
2896 footer.className = "dropdown-footer";
2897 footer.style.padding = "0.5rem 1rem";
2898 footer.style.fontSize = "0.75rem";
2899 footer.style.color = "var(--text-gray-400)";
2900 footer.style.borderTop = "1px solid var(--border-color)";
2901 footer.style.background = "var(--bg-subtle)";
2902 footer.textContent = `Showing ${options.length} out of ${totalCount} ${itemName}`;
2903 container.appendChild(footer);
2904 }
2905 // --- Search Logic ---
2906 if (searchInput) {
2907 searchInput.addEventListener("input", (e) => {
2908 const term = e.target.value.toLowerCase();
2909 if (searchCallback) {
2910 const { options: filteredOptions, total: mCount } =
2911 searchCallback(term);
2912 renderList(filteredOptions);
2913 if (footer) {
2914 footer.textContent = `Showing ${filteredOptions.length} out of ${mCount} ${itemName}`;
2915 }
2916 } else {
2917 const items = listZone.querySelectorAll(".popover-item");
2918 let visibleCount = 0;
2919 items.forEach((item) => {
2920 const label = item.querySelector("span").textContent.toLowerCase();
2921 const isVisible = label.includes(term);
2922 item.style.display = isVisible ? "flex" : "none";
2923 if (isVisible) visibleCount++;
2924 });
2925 if (footer && totalCount !== null) {
2926 footer.textContent = `Showing ${visibleCount} out of ${totalCount} ${itemName}`;
2927 }
2928 }
2929 });
2930 }
2931 },
2932 /**
2933 * Renders the text on the filter chip (e.g., "Module: All" or "Module: :core:network (+2)").
2934 */
2935 renderChipText(element, label, type, isMulti = false) {
2936 if (!element) return;
2937 // Logic for displaying close button: ALWAYS show if 'type' is present (meaning removable)
2938 // The user specifically requested option to remove filter when all items are selected.
2939 const showClose = !!type;
2940 if (!showClose) {
2941 element.innerHTML = `<span class="filter-text">${label}</span>`;
2942 } else {
2943 element.innerHTML = `
2944 <span class="filter-text">${label}</span>
2945 <span class="chip-close" data-clear="${type}">
2946 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="14" height="14">
2947 <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
2948 </svg>
2949 </span>`;
2950 }
2951 },
2952 getFilterLabel(prefix, selectedArr, totalCount, options = []) {
2953 if (
2954 selectedArr.length === 0 ||
2955 (totalCount > 0 && selectedArr.length === totalCount)
2956 )
2957 return `${prefix}: All`;
2958 if (selectedArr.length === 1) {
2959 const val = selectedArr[0];
2960 const opt = options.find((o) => String(o.value) === String(val));
2961 const name = opt ? opt.name : val;
2962 // Strip HTML tags if any (like in keepRuleOptions)
2963 const cleanName = name.replace(/<[^>]*>/g, "");
2964 return `${prefix}: ${cleanName}`;
2965 }
2966 return `${prefix}: ${selectedArr.length} Selected`;
2967 },
2968 /**
2969 * Finds the common prefix among an array of strings.
2970 * @param {string[]} strings - Array of strings to analyze.
2971 * @returns {string} The common prefix.
2972 */
2973 findCommonPrefix(strings) {
2974 if (!strings || strings.length === 0) return "";
2975 let prefix = strings[0];
2976 for (let i = 1; i < strings.length; i++) {
2977 if (strings[i].startsWith("Android Gradle plugin")) {
2978 continue;
2979 }
2980 while (strings[i].indexOf(prefix) !== 0) {
2981 prefix = prefix.substring(0, prefix.length - 1);
2982 if (prefix === "") return "";
2983 }
2984 }
2985 // Ensure we only strip up to the last slash to keep the filename.
2986 const lastSlash = prefix.lastIndexOf("/");
2987 if (lastSlash !== -1) {
2988 return prefix.substring(0, lastSlash + 1);
2989 }
2990 return "";
2991 },
2992 };
2993 /* ==========================================================================
2994 MAIN APP CONTROLLER
2995 ========================================================================== */
2996 const App = {
2997 keepRadiusData: null,
2998 lookups: {},
2999 detailsState: {
3000 ruleId: null,
3001 classQuery: "",
3002 fieldQuery: "",
3003 methodQuery: "",
3004 },
3005 init() {
3006 // Initialize Report (Main Grid)
3007 ReportApp.init();
3008 // Load Protobuf Data in the Background
3009 this.loadProtoData();
3010 const headerLink = document.getElementById("header-link");
3011 if (headerLink) {
3012 headerLink.addEventListener("click", (e) => {
3013 e.preventDefault();
3014 this.showReportView();
3015 });
3016 }
3017 // Setup Kept Lists Search listeners
3018 const setupListSearch = (type, stateKey) => {
3019 const toggleBtn = document.querySelector(
3020 `.search-toggle-btn[data-target="${type}"]`,
3021 );
3022 const container = document.getElementById(`${type}-search-container`);
3023 const input = document.getElementById(`${type}-search-input`);
3024 if (toggleBtn && container && input) {
3025 toggleBtn.addEventListener("click", () => {
3026 const isHidden = container.style.display === "none";
3027 container.style.display = isHidden ? "block" : "none";
3028 toggleBtn.classList.toggle("active", isHidden);
3029 if (isHidden) {
3030 input.focus();
3031 } else {
3032 // Clear query when closing search
3033 input.value = "";
3034 App.detailsState[stateKey] = "";
3035 const rule = App.keepRadiusData?.keepRuleKeepRadiusTable.find(
3036 (r) => r.id === parseInt(App.detailsState.ruleId),
3037 );
3038 if (rule) App.renderKeptLists(rule, type);
3039 }
3040 });
3041 input.addEventListener("input", (e) => {
3042 App.detailsState[stateKey] = e.target.value;
3043 const rule = App.keepRadiusData?.keepRuleKeepRadiusTable.find(
3044 (r) => r.id === parseInt(App.detailsState.ruleId),
3045 );
3046 if (rule) App.renderKeptLists(rule, type);
3047 });
3048 }
3049 };
3050 setupListSearch("classes", "classQuery");
3051 setupListSearch("fields", "fieldQuery");
3052 setupListSearch("methods", "methodQuery");
3053 },
3054 showDetailsView(ruleId) {
3055 this.detailsState.ruleId = ruleId;
3056 this.detailsState.classQuery = "";
3057 this.detailsState.fieldQuery = "";
3058 this.detailsState.methodQuery = "";
3059 ReportApp.state.currentView = CONSTANTS.VIEWS.DETAILS;
3060 document.getElementById("report-view").style.display = "none";
3061 document.getElementById("file-details-view").style.display = "none";
3062 document.getElementById("report-view-controls").style.display = "none";
3063 document.getElementById("details-view").style.display = "flex";
3064 const ruleContainer = document.getElementById("details-rule-container");
3065 const impactContainer = document.getElementById("details-impact-container");
3066 const identicalRulesBody = document.getElementById(
3067 "details-identical-rules-body",
3068 );
3069 const identicalRulesHeader = document.getElementById(
3070 "details-identical-rules-header",
3071 );
3072 const identicalRulesTitle = document.getElementById(
3073 "details-identical-rules-title",
3074 );
3075 const subsumedByBody = document.getElementById("details-subsumed-by-body");
3076 const subsumedByHeader = document.getElementById(
3077 "details-subsumed-by-header",
3078 );
3079 const subsumedByTitle = document.getElementById(
3080 "details-subsumed-by-title",
3081 );
3082 const impactHeader = document.getElementById("details-rule-impact-header");
3083 const classesContent = document.getElementById("details-classes-content");
3084 const methodsContent = document.getElementById("details-methods-content");
3085 const fieldsContent = document.getElementById("details-fields-content");
3086 let rule = this.keepRadiusData?.keepRuleKeepRadiusTable.find(
3087 (r) => r.id === parseInt(ruleId),
3088 );
3089 let isGlobal = false;
3090 if (!rule) {
3091 rule = this.keepRadiusData?.globalKeepRuleKeepRadiusTable.find(
3092 (r) => r.id === parseInt(ruleId),
3093 );
3094 isGlobal = true;
3095 }
3096 if (rule) {
3097 const fileOriginId = rule?.origin?.fileOriginId;
3098 const fileOrigin = this.keepRadiusData?.fileOriginTable.find(
3099 (f) => f.id === fileOriginId,
3100 );
3101 let originStr = "";
3102 if (fileOrigin) {
3103 const mavenName = formatMavenCoordinate(fileOrigin.mavenCoordinate);
3104 originStr = `${mavenName || fileOrigin.filename}:${rule.origin?.lineNumber || 1}`;
3105 if (mavenName) {
3106 originStr += ` (${fileOrigin.filename})`;
3107 }
3108 }
3109
3110 if (ruleContainer) {
3111 const highlightedSource = highlightRule(rule.source);
3112 ruleContainer.innerHTML = `
3113 ${originStr ? `<div style="font-size: 0.75rem; color: var(--text-gray-500); margin-bottom: 0.5rem; font-family: var(--font-family-mono);">${escapeHTML(originStr)}</div>` : ""}
3114 <div style="background-color: var(--bg-body); border: 1px solid var(--border-color); border-radius: 4px; padding: 0.75rem;">
3115 <pre style="white-space: pre-wrap; font-family: var(--font-family-mono); font-size: 0.8125rem; margin: 0; color: var(--text-main);">${highlightedSource}</pre>
3116 </div>
3117 `;
3118 }
3119 const impactContainer = document.getElementById(
3120 "details-impact-container",
3121 );
3122 const relatedRulesContainer = document.getElementById(
3123 "related-rules-container",
3124 );
3125 const gridContainer = document.getElementById("details-classes-content")
3126 ?.parentElement?.parentElement;
3127 if (isGlobal) {
3128 if (impactContainer)
3129 impactContainer.parentElement.style.display = "none";
3130 if (relatedRulesContainer) relatedRulesContainer.style.display = "none";
3131 if (gridContainer) gridContainer.style.display = "none";
3132 } else {
3133 if (impactContainer) impactContainer.parentElement.style.display = "";
3134 if (relatedRulesContainer) relatedRulesContainer.style.display = "";
3135 if (gridContainer) gridContainer.style.display = "grid";
3136 const constraintsMap = getConstraintsMap(this.keepRadiusData);
3137 const constraints = constraintsMap.get(rule.constraintsId) || [];
3138 const getTag = (c, label) => {
3139 const isRestricted = constraints.includes(c);
3140 if (!isRestricted) return "";
3141 const color = "oklch(0.446 0.043 257.281)";
3142 const bgColor = "oklch(0.984 0.003 247.858)";
3143 const borderColor = "oklch(0.929 0.013 255.508)";
3144 return `<span class="impact-tag" style="display: inline-block; color: ${color}; background-color: ${bgColor}; border: 1px solid ${borderColor}; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 400; height: 21px; line-height: 15px; text-transform: uppercase; letter-spacing: 0.25px; box-sizing: border-box; text-align: center;">${label}</span>`;
3145 };
3146 const impactTagsHtml = `
3147 <div class="impact-container">
3148 ${getTag("DONT_OBFUSCATE", "OBFUSCATE")}
3149 ${getTag("DONT_OPTIMIZE", "OPTIMIZE")}
3150 ${getTag("DONT_SHRINK", "SHRINK")}
3151 </div>
3152 `;
3153 const br = rule.keepRadius || {};
3154 const classIds = br.classKeepRadius || [];
3155 const fieldIds = br.fieldKeepRadius || [];
3156 const methodIds = br.methodKeepRadius || [];
3157 const matchedTotal =
3158 classIds.length + fieldIds.length + methodIds.length;
3159 const totalLive = getLiveItemCount(this.keepRadiusData);
3160 const liveClasses = this.keepRadiusData?.buildInfo?.liveClassCount || 0;
3161 const liveFields = this.keepRadiusData?.buildInfo?.liveFieldCount || 0;
3162 const liveMethods =
3163 this.keepRadiusData?.buildInfo?.liveMethodCount || 0;
3164 const renderMatchCell = (count, total, borderLeft = true) => {
3165 const perc = total > 0 ? (count / total) * 100 : 0;
3166 const colorClass = UIUtils.getMatchClass(perc);
3167 const bl = borderLeft ? "border-l border-gray-200" : "";
3168 return `
3169 <td class="text-center ${bl}" style="padding: 1rem; width: 100px; min-width: 100px;">
3170 <div style="display: flex; flex-direction: column; align-items: center; justify-content: center;">
3171 <span class="font-medium ${colorClass}">${perc.toFixed(1)}%</span>
3172 <span class="text-xs text-gray-500 mt-1">${count}</span>
3173 </div>
3174 </td>
3175 `;
3176 };
3177 if (impactContainer) {
3178 const getPerc = (count, total) =>
3179 total > 0 ? (count / total) * 100 : 0;
3180 const classPerc = getPerc(classIds.length, liveClasses);
3181 const fieldPerc = getPerc(fieldIds.length, liveFields);
3182 const methodPerc = getPerc(methodIds.length, liveMethods);
3183 impactContainer.innerHTML = `
3184 <div style="display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 2rem; padding: 0; align-items: start;">
3185 <!-- Kept Classes -->
3186 <div>
3187 <div style="color: var(--text-gray-500); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem;">Kept Classes</div>
3188 <div style="display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem;">
3189 <span style="color: oklch(0.505 0.213 27.518); font-size: 1.25rem; font-weight: 500;">${classPerc.toFixed(1)}%</span>
3190 <span style="color: var(--text-gray-500); font-size: 0.75rem;">${classIds.length} / ${liveClasses}</span>
3191 </div>
3192 <div style="height: 4px; background-color: var(--bg-hover); border-radius: 2px; width: 100%;">
3193 <div style="height: 100%; background-color: oklch(0.505 0.213 27.518); border-radius: 2px; width: ${classPerc}%;"></div>
3194 </div>
3195 </div>
3196 <!-- Kept Fields -->
3197 <div>
3198 <div style="color: var(--text-gray-500); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem;">Kept Fields</div>
3199 <div style="display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem;">
3200 <span style="color: oklch(0.505 0.213 27.518); font-size: 1.25rem; font-weight: 500;">${fieldPerc.toFixed(1)}%</span>
3201 <span style="color: var(--text-gray-500); font-size: 0.75rem;">${fieldIds.length} / ${liveFields}</span>
3202 </div>
3203 <div style="height: 4px; background-color: var(--bg-hover); border-radius: 2px; width: 100%;">
3204 <div style="height: 100%; background-color: oklch(0.505 0.213 27.518); border-radius: 2px; width: ${fieldPerc}%;"></div>
3205 </div>
3206 </div>
3207 <!-- Kept Methods -->
3208 <div>
3209 <div style="color: var(--text-gray-500); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem;">Kept Methods</div>
3210 <div style="display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem;">
3211 <span style="color: oklch(0.505 0.213 27.518); font-size: 1.25rem; font-weight: 500;">${methodPerc.toFixed(1)}%</span>
3212 <span style="color: var(--text-gray-500); font-size: 0.75rem;">${methodIds.length} / ${liveMethods}</span>
3213 </div>
3214 <div style="height: 4px; background-color: var(--bg-hover); border-radius: 2px; width: 100%;">
3215 <div style="height: 100%; background-color: oklch(0.505 0.213 27.518); border-radius: 2px; width: ${methodPerc}%;"></div>
3216 </div>
3217 </div>
3218 <!-- Blocked by Rule -->
3219 <div>
3220 <div style="color: var(--text-gray-500); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: .80rem;">Blocked by Rule</div>
3221 <div style="display: flex; align-items: center; gap: 0.5rem;">
3222 ${impactTagsHtml}
3223 </div>
3224 </div>
3225 </div>
3226 `;
3227 }
3228
3229 const allRules = this.keepRadiusData?.keepRuleKeepRadiusTable || [];
3230 const subsumingIds = br.subsumedBy || [];
3231 const identicalRules = [];
3232 const subsumedByRules = [];
3233 subsumingIds.forEach((id) => {
3234 const otherRule = allRules.find((r) => r.id === id);
3235 if (otherRule) {
3236 const otherSubsumedBy = otherRule.keepRadius?.subsumedBy || [];
3237 if (otherSubsumedBy.includes(rule.id)) {
3238 identicalRules.push(otherRule);
3239 } else {
3240 subsumedByRules.push(otherRule);
3241 }
3242 }
3243 });
3244 if (relatedRulesContainer) {
3245 const hasIdentical = identicalRules.length > 0;
3246 const hasSubsumed = subsumedByRules.length > 0;
3247 const renderDetailRuleRow = (r) => {
3248 const rBr = r.keepRadius || {};
3249 const rClassIds = rBr.classKeepRadius || [];
3250 const rFieldIds = rBr.fieldKeepRadius || [];
3251 const rMethodIds = rBr.methodKeepRadius || [];
3252 const totalLive = getLiveItemCount(this.keepRadiusData);
3253 const liveClasses =
3254 this.keepRadiusData?.buildInfo?.liveClassCount || 0;
3255 const liveFields =
3256 this.keepRadiusData?.buildInfo?.liveFieldCount || 0;
3257 const liveMethods =
3258 this.keepRadiusData?.buildInfo?.liveMethodCount || 0;
3259 const renderMatchCell = (count, total) => {
3260 const perc = total > 0 ? (count / total) * 100 : 0;
3261 return `
3262 <td class="text-center" style="padding: 0.5rem; border-left: 1px solid var(--border-color);">
3263 <div style="display: flex; flex-direction: column; align-items: center;">
3264 <span style="color: var(--text-red-600); font-weight: 500;">${perc.toFixed(1)}%</span>
3265 <span class="text-xs text-gray-500">${count}</span>
3266 </div>
3267 </td>
3268 `;
3269 };
3270 const constraintsMap = getConstraintsMap(this.keepRadiusData);
3271 const rConstraints = constraintsMap.get(r.constraintsId) || [];
3272 const getTag = (c, label) => {
3273 const isRestricted = rConstraints.includes(c);
3274 const color = isRestricted
3275 ? "oklch(0.446 0.043 257.281)"
3276 : "#cbd5e1";
3277 const bgColor = isRestricted
3278 ? "oklch(0.984 0.003 247.858)"
3279 : "#f8fafc";
3280 const borderColor = isRestricted
3281 ? "oklch(0.929 0.013 255.508)"
3282 : "#e2e8f0";
3283 return `<span class="impact-tag" style="display: inline-block; color: ${color}; background-color: ${bgColor}; border: 1px solid ${borderColor}; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 400; height: 21px; line-height: 15px; text-transform: uppercase; letter-spacing: 0.25px; box-sizing: border-box; text-align: center;">${label}</span>`;
3284 };
3285 const impactCell = `
3286 <td style="padding: 1rem; border-left: 1px solid var(--border-color);">
3287 <div class="flex justify-start" style="gap: 0.5rem;">
3288 ${getTag("DONT_OBFUSCATE", "OBFUSCATE")}
3289 ${getTag("DONT_OPTIMIZE", "OPTIMIZE")}
3290 ${getTag("DONT_SHRINK", "SHRINK")}
3291 </div>
3292 </td>
3293 `;
3294 return `
3295 <tr class="table-row border-t border-gray-200 hover:bg-gray-50 cursor-pointer" onclick="App.showDetailsView('${r.id}')">
3296 <td style="padding: 0.5rem; width: 40%;">
3297 <pre style="white-space: pre-wrap; font-family: var(--font-family-mono); font-size: 0.8125rem; margin: 0;">${highlightRule(r.source)}</pre>
3298 </td>
3299 ${renderMatchCell(rClassIds.length + rFieldIds.length + rMethodIds.length, totalLive)}
3300 ${renderMatchCell(rClassIds.length, liveClasses)}
3301 ${renderMatchCell(rFieldIds.length, liveFields)}
3302 ${renderMatchCell(rMethodIds.length, liveMethods)}
3303 ${impactCell}
3304 </tr>
3305 `;
3306 };
3307 const renderSection = (title, rules, explainer) => {
3308 const hasRules = rules.length > 0;
3309 let sectionHtml = `
3310 <div class="table-container" style="display: flex; flex-direction: column;">
3311 <div style="padding: 0.75rem 1rem; border-bottom: 1px solid var(--border-color); background-color: var(--bg-subtle); display: flex; justify-content: space-between; align-items: center;">
3312 <div>
3313 <span style="font-size: 0.875rem; font-weight: 600;">${title}</span>
3314 <span style="color: var(--text-gray-500); font-size: 0.75rem; margin-left: 0.25rem;">· ${hasRules ? rules.length : "None"}</span>
3315 </div>
3316 ${hasRules ? `<span style="color: var(--text-gray-500); font-size: 0.75rem;">${explainer}</span>` : ""}
3317 </div>
3318 `;
3319 if (hasRules) {
3320 sectionHtml += `
3321 <table style="width: 100%; border-collapse: collapse;">
3322 <thead>
3323 <tr>
3324 <th rowspan="2" class="text-left bg-gray-50" style="padding: 0 1rem; width: 40%;">RULE</th>
3325 <th colspan="4" class="text-center bg-gray-50" style="padding: .25rem 1rem; width: 40%; border-left: 1px solid var(--border-color);">KEPT ITEMS <span style="color: var(--text-muted);text-transform: none;font-weight: 500;">Higher is worse</span> <button type="button" class="tooltip-icon" data-tooltip="Items retained in the app due to this rule" aria-label="Items retained in the app due to this rule">?</button></th>
3326 <th rowspan="2" class="text-left bg-gray-50" style="padding: 0 1rem; width: 20%; border-left: 1px solid var(--border-color);">BLOCKED BY RULE <button type="button" class="tooltip-icon" data-tooltip="Specific actions blocked by this rule" aria-label="Specific actions blocked by this rule">?</button></th>
3327 </tr>
3328 <tr>
3329 <th class="text-center text-xs font-medium text-gray-500" style="padding: .15rem 1rem; width: 10%; border-left: 1px solid var(--border-color);">Total</th>
3330 <th class="text-center text-xs font-medium text-gray-500" style="padding: .15rem 1rem; width: 10%; border-left: 1px solid var(--border-color);">Classes</th>
3331 <th class="text-center text-xs font-medium text-gray-500" style="padding: .15rem 1rem; width: 10%; border-left: 1px solid var(--border-color);">Fields</th>
3332 <th class="text-center text-xs font-medium text-gray-500" style="padding: .15rem 1rem; width: 10%; border-left: 1px solid var(--border-color);">Methods</th>
3333 </tr>
3334 </thead>
3335 <tbody>
3336 ${rules.map((r) => renderDetailRuleRow(r)).join("")}
3337 </tbody>
3338 </table>
3339 `;
3340 }
3341 sectionHtml += `</div>`;
3342 return sectionHtml;
3343 };
3344 if (!hasIdentical && !hasSubsumed) {
3345 relatedRulesContainer.innerHTML = `
3346 <div class="table-container" style="padding: 0.75rem 1rem; font-size: 0.875rem; display: flex; gap: 2rem; align-items: center;">
3347 <span style="font-weight: 600; color: var(--text-gray-900);">Related rules:</span>
3348 <span style="color: var(--text-gray-500);">Identical · None</span>
3349 <span style="color: var(--text-gray-500);">Subsumed by · None</span>
3350 </div>
3351 `;
3352 } else {
3353 let html =
3354 '<div style="display: flex; flex-direction: column; gap: 1.5rem;">';
3355 html += renderSection(
3356 "Identical Rules",
3357 identicalRules,
3358 "Same matchers, can be deduplicated",
3359 );
3360 html += renderSection(
3361 "Subsumed By",
3362 subsumedByRules,
3363 "Already covered by a broader rule",
3364 );
3365 html += "</div>";
3366 relatedRulesContainer.innerHTML = html;
3367 }
3368 }
3369
3370 // Reset search inputs UI
3371 const resetSearchUI = (type) => {
3372 const container = document.getElementById(`${type}-search-container`);
3373 const input = document.getElementById(`${type}-search-input`);
3374 const toggleBtn = document.querySelector(
3375 `.search-toggle-btn[data-target="${type}"]`,
3376 );
3377 if (container && input && toggleBtn) {
3378 container.style.display = "none";
3379 input.value = "";
3380 toggleBtn.classList.remove("active");
3381 }
3382 };
3383 resetSearchUI("classes");
3384 resetSearchUI("fields");
3385 resetSearchUI("methods");
3386 // Initial render of kept lists
3387 this.renderKeptLists(rule);
3388 }
3389 } else {
3390 ruleBody.innerHTML =
3391 '<tr><td colspan="2" style="padding: 1rem;">Rule not found.</td></tr>';
3392 classesContent.innerHTML = "";
3393 methodsContent.innerHTML = "";
3394 fieldsContent.innerHTML = "";
3395 }
3396 const fileOrigin = this.keepRadiusData?.fileOriginTable.find(
3397 (f) => f.id === rule?.origin?.fileOriginId,
3398 );
3399 const fileOriginName =
3400 formatMavenCoordinate(fileOrigin?.mavenCoordinate) ||
3401 fileOrigin?.filename;
3402 this.updateDetailsBreadcrumbs(fileOriginName, fileOrigin?.id);
3403 },
3404 renderKeptLists(rule, targetType = null) {
3405 const br = rule.keepRadius || {};
3406
3407 const typeRefMap = App.lookups.typeReference;
3408 const renderList = (ids, getLabel) => {
3409 if (ids.length === 0)
3410 return '<div style="padding: 0.5rem; color: var(--text-gray-500); font-size: 0.8125rem;">None</div>';
3411 const formatSleekItem = (fullName) => {
3412 const firstParen = fullName.indexOf("(");
3413 const searchString =
3414 firstParen === -1 ? fullName : fullName.substring(0, firstParen);
3415 const lastDot = searchString.lastIndexOf(".");
3416 if (lastDot === -1) {
3417 return `<span style="color: var(--text-gray-900); font-weight: 500;">${escapeHTML(fullName)}</span>`;
3418 }
3419 const pkg = fullName.substring(0, lastDot + 1);
3420 const name = fullName.substring(lastDot + 1);
3421 return `<span style="color: var(--text-gray-500);">${escapeHTML(pkg)}</span><span style="color: var(--text-gray-900); font-weight: 500;">${escapeHTML(name)}</span>`;
3422 };
3423 const limit = 1000;
3424 const toRender = ids.slice(0, limit);
3425 const listHtml = toRender
3426 .map(
3427 (id) => `
3428 <div style="padding: 0.375rem 0.5rem; border-bottom: 1px solid #f1f5f9; font-family: var(--font-family-mono); font-size: 0.8125rem;" class="hover-bg-gray-100">
3429 ${formatSleekItem(getLabel(id))}
3430 </div>
3431 `,
3432 )
3433 .join("");
3434 if (ids.length > limit) {
3435 return (
3436 listHtml +
3437 `
3438 <div style="padding: 0.5rem; color: var(--text-gray-500); font-size: 0.8125rem; font-style: italic;">
3439 ... and ${ids.length - limit} more items
3440 </div>`
3441 );
3442 }
3443 return listHtml;
3444 };
3445 // 1. CLASSES COLUMN
3446 if (!targetType || targetType === "classes") {
3447 const classIds = br.classKeepRadius || [];
3448 const classesContent = document.getElementById("details-classes-content");
3449 const filteredClassIds = classIds.filter((id) => {
3450 const info = App.lookups.keptClassInfo.get(id);
3451 const name = formatDescriptor(typeRefMap.get(info?.classReferenceId));
3452 return name
3453 .toLowerCase()
3454 .includes(this.detailsState.classQuery.toLowerCase());
3455 });
3456 document.getElementById("details-classes-count").textContent =
3457 `· ${filteredClassIds.length}`;
3458 classesContent.innerHTML = renderList(filteredClassIds, (id) => {
3459 const info = App.lookups.keptClassInfo.get(id);
3460 return formatDescriptor(typeRefMap.get(info?.classReferenceId));
3461 });
3462 }
3463 // 2. FIELDS COLUMN
3464 if (!targetType || targetType === "fields") {
3465 const fieldIds = br.fieldKeepRadius || [];
3466 const fieldsContent = document.getElementById("details-fields-content");
3467 const filteredFieldIds = fieldIds.filter((id) => {
3468 const info = App.lookups.keptFieldInfo.get(id);
3469 const ref = App.lookups.fieldReference.get(info?.fieldReferenceId);
3470 const name = formatFieldName(ref, App.lookups);
3471 return name
3472 .toLowerCase()
3473 .includes(this.detailsState.fieldQuery.toLowerCase());
3474 });
3475 document.getElementById("details-fields-count").textContent =
3476 `· ${filteredFieldIds.length}`;
3477 fieldsContent.innerHTML = renderList(filteredFieldIds, (id) => {
3478 const info = App.lookups.keptFieldInfo.get(id);
3479 const ref = App.lookups.fieldReference.get(info?.fieldReferenceId);
3480 return formatFieldName(ref, App.lookups);
3481 });
3482 }
3483 // 3. METHODS COLUMN
3484 if (!targetType || targetType === "methods") {
3485 const methodIds = br.methodKeepRadius || [];
3486 const methodsContent = document.getElementById("details-methods-content");
3487 const filteredMethodIds = methodIds.filter((id) => {
3488 const info = App.lookups.keptMethodInfo.get(id);
3489 const ref = App.lookups.methodReference.get(info?.methodReferenceId);
3490 const name = formatMethodName(ref, App.lookups);
3491 return name
3492 .toLowerCase()
3493 .includes(this.detailsState.methodQuery.toLowerCase());
3494 });
3495 document.getElementById("details-methods-count").textContent =
3496 `· ${filteredMethodIds.length}`;
3497 methodsContent.innerHTML = renderList(filteredMethodIds, (id) => {
3498 const info = App.lookups.keptMethodInfo.get(id);
3499 const ref = App.lookups.methodReference.get(info?.methodReferenceId);
3500 return formatMethodName(ref, App.lookups);
3501 });
3502 }
3503 },
3504 showReportView() {
3505 ReportApp.state.currentView = CONSTANTS.VIEWS.MODULES;
3506 document.getElementById("details-view").style.display = "none";
3507 document.getElementById("file-details-view").style.display = "none";
3508 document.getElementById("report-view-controls").style.display = "flex";
3509 document.getElementById("report-view").style.display = "flex";
3510 ReportApp.render();
3511 },
3512 showFileDetailsView(fileOriginId) {
3513 ReportApp.state.currentView = CONSTANTS.VIEWS.FILE_DETAILS;
3514 ReportApp.state.drillContext.fileOriginId = fileOriginId;
3515 this.renderFileDetailsView(fileOriginId);
3516 },
3517 renderFileDetailsView(fileOriginId) {
3518 document.getElementById("report-view").style.display = "none";
3519 document.getElementById("details-view").style.display = "none";
3520 document.getElementById("report-view-controls").style.display = "none";
3521 document.getElementById("file-details-view").style.display = "flex";
3522 const impactBody = document.getElementById("file-details-impact-body");
3523 const rulesBody = document.getElementById("file-details-rules-body");
3524 const rulesHeader = document.getElementById("file-details-rules-header");
3525 const fileOrigin = this.keepRadiusData?.fileOriginTable.find(
3526 (f) => f.id === parseInt(fileOriginId),
3527 );
3528 if (!fileOrigin) return;
3529 const fileRules = this.keepRadiusData.keepRuleKeepRadiusTable.filter(
3530 (r) => r.origin?.fileOriginId === fileOrigin.id,
3531 );
3532
3533 const globalRules = [];
3534 if (this.keepRadiusData.globalKeepRuleKeepRadiusTable) {
3535 const totalClasses = this.keepRadiusData?.buildInfo?.liveClassCount || 0;
3536 const totalFields = this.keepRadiusData?.buildInfo?.liveFieldCount || 0;
3537 const totalMethods = this.keepRadiusData?.buildInfo?.liveMethodCount || 0;
3538 this.keepRadiusData.globalKeepRuleKeepRadiusTable.forEach((rule) => {
3539 if (
3540 rule.source === "-dontoptimize" ||
3541 rule.source === "-dontshrink" ||
3542 rule.source === "-dontobfuscate"
3543 ) {
3544 if (rule.origin?.fileOriginId === fileOrigin.id) {
3545 globalRules.push({
3546 id: rule.id,
3547 source: rule.source,
3548 constraintsId: rule.constraintsId,
3549 isGlobal: true,
3550 keepRadius: {
3551 classKeepRadius: new Array(totalClasses),
3552 fieldKeepRadius: new Array(totalFields),
3553 methodKeepRadius: new Array(totalMethods),
3554 },
3555 });
3556 }
3557 }
3558 });
3559 }
3560
3561 const allRulesForFile = [...globalRules, ...fileRules];
3562 let rules = allRulesForFile;
3563 const lens = ReportApp.state.filters.keepRules[0];
3564 if (lens) {
3565 rules = ReportApp.applyKeepRuleLens(rules, lens);
3566 }
3567 const isIdenticalLens = lens === "Identical";
3568 const isSubsumedLens = lens === "Subsumed";
3569 if (isIdenticalLens || isSubsumedLens) {
3570 rulesHeader.innerHTML = `
3571 <tr>
3572 <th class="text-left bg-gray-50 z-30" style="padding: 1rem; width: 600px; min-width: 600px; border-bottom: 1px solid var(--border-color);">Rule</th>
3573 <th class="text-left bg-gray-50 border-l border-gray-200" style="padding: 1rem; width: 100%; border-bottom: 1px solid var(--border-color);">${isIdenticalLens ? "Identical Rules" : "Subsumed By"}</th>
3574 </tr>
3575 `;
3576 } else {
3577 rulesHeader.innerHTML = `
3578 <tr>
3579 <th rowspan="2" class="text-left bg-gray-50" style="padding: 1rem; width: 40%; border-bottom: 1px solid var(--border-color);">Rule</th>
3580 <th colspan="4" class="text-center bg-gray-50" style="padding: 0.5rem; width: 40%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Kept Items <span style="color: var(--text-muted);text-transform: none;font-weight: 500;">Higher is worse</span></th>
3581 <th rowspan="2" class="text-left bg-gray-50" style="padding: 0.5rem; width: 20%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Blocked by Rule</th>
3582 </tr>
3583 <tr>
3584 <th class="text-center text-xs font-medium text-gray-500" style="padding: 0.5rem; width: 10%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Total</th>
3585 <th class="text-center text-xs font-medium text-gray-500" style="padding: 0.5rem; width: 10%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Classes</th>
3586 <th class="text-center text-xs font-medium text-gray-500" style="padding: 0.5rem; width: 10%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Fields</th>
3587 <th class="text-center text-xs font-medium text-gray-500" style="padding: 0.5rem; width: 10%; border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);">Methods</th>
3588 </tr>
3589 `;
3590 }
3591 const liveClasses = this.keepRadiusData?.buildInfo?.liveClassCount || 0;
3592 const liveFields = this.keepRadiusData?.buildInfo?.liveFieldCount || 0;
3593 const liveMethods = this.keepRadiusData?.buildInfo?.liveMethodCount || 0;
3594 const matchedClasses = new Set();
3595 const matchedFields = new Set();
3596 const matchedMethods = new Set();
3597 allRulesForFile.forEach((rule) => {
3598 const br = rule.keepRadius || {};
3599 (br.classKeepRadius || []).forEach((id) => matchedClasses.add(id));
3600 (br.fieldKeepRadius || []).forEach((id) => matchedFields.add(id));
3601 (br.methodKeepRadius || []).forEach((id) => matchedMethods.add(id));
3602 });
3603 const renderMatchCell = (count, total, borderLeft = true) => {
3604 const perc = total > 0 ? (count / total) * 100 : 0;
3605 const colorClass = UIUtils.getMatchClass(perc);
3606 const bl = borderLeft ? "border-l border-gray-200" : "";
3607 return `
3608 <td class="text-center ${bl}" style="padding: 1rem;">
3609 <div style="display: flex; flex-direction: column; align-items: center; justify-content: center;">
3610 <span class="font-medium ${colorClass}">${perc.toFixed(1)}%</span>
3611 <span class="text-xs text-gray-500 mt-1">${count}</span>
3612 </div>
3613 </td>
3614 `;
3615 };
3616 impactBody.innerHTML = `
3617 <tr class="border-t border-gray-200">
3618 ${renderMatchCell(matchedClasses.size, liveClasses, false)}
3619 ${renderMatchCell(matchedFields.size, liveFields)}
3620 ${renderMatchCell(matchedMethods.size, liveMethods)}
3621 </tr>
3622 `;
3623 rulesBody.innerHTML = rules
3624 .map((rule) => {
3625 if (isIdenticalLens || isSubsumedLens) {
3626 const list = isIdenticalLens
3627 ? rule.identicalRules
3628 : rule.subsumedByRules;
3629 const otherRulesHtml = (list || [])
3630 .map(
3631 (other) => `
3632 <div class="text-xs text-blue-600 hover:underline cursor-pointer mb-1" onclick="event.stopPropagation(); App.showDetailsView('${other.id}')">
3633 <pre style="white-space: pre-wrap; margin: 0; font-family: var(--font-family-mono);">${escapeHTML(other.source)}</pre>
3634 </div>
3635 `,
3636 )
3637 .join("");
3638 return `
3639 <tr class="border-t border-gray-200 hover:bg-gray-50 cursor-pointer" onclick="App.showDetailsView('${rule.id}')">
3640 <td class="sticky-name font-medium text-blue-600 hover:underline" title="${escapeHTML(rule.source)}" style="padding: 1rem; width: 600px; min-width: 600px;">
3641 <pre style="white-space: pre-wrap; font-family: var(--font-family-mono); font-size: 0.8125rem; margin: 0; pointer-events: none;">${escapeHTML(rule.source)}</pre>
3642 </td>
3643 <td class="border-l border-gray-200" style="padding: 1rem; width: 100%;">
3644 ${otherRulesHtml || '<span class="text-gray-400 italic">None</span>'}
3645 </td>
3646 </tr>
3647 `;
3648 }
3649 const br = rule.keepRadius || {};
3650 const c = (br.classKeepRadius || []).length;
3651 const f = (br.fieldKeepRadius || []).length;
3652 const m = (br.methodKeepRadius || []).length;
3653 const constraintsMap = getConstraintsMap(this.keepRadiusData);
3654 let constraints = [...(constraintsMap.get(rule.constraintsId) || [])];
3655 if (rule.isGlobal) {
3656 if (
3657 rule.source === "-dontobfuscate" &&
3658 !constraints.includes("DONT_OBFUSCATE")
3659 ) {
3660 constraints.push("DONT_OBFUSCATE");
3661 }
3662 if (
3663 rule.source === "-dontoptimize" &&
3664 !constraints.includes("DONT_OPTIMIZE")
3665 ) {
3666 constraints.push("DONT_OPTIMIZE");
3667 }
3668 if (
3669 rule.source === "-dontshrink" &&
3670 !constraints.includes("DONT_SHRINK")
3671 ) {
3672 constraints.push("DONT_SHRINK");
3673 }
3674 }
3675 const getTag = (c, label) => {
3676 const isRestricted = constraints.includes(c);
3677 const color = isRestricted ? "oklch(0.446 0.043 257.281)" : "#cbd5e1";
3678 const bgColor = isRestricted
3679 ? "oklch(0.984 0.003 247.858)"
3680 : "#f8fafc";
3681 const borderColor = isRestricted
3682 ? "oklch(0.929 0.013 255.508)"
3683 : "#e2e8f0";
3684 return `<span class="impact-tag" style="display: inline-block; color: ${color}; background-color: ${bgColor}; border: 1px solid ${borderColor}; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 400; height: 21px; line-height: 15px; text-transform: uppercase; letter-spacing: 0.25px; box-sizing: border-box; text-align: center;">${label}</span>`;
3685 };
3686 const impactCell = `
3687 <td style="padding: 0.5rem; border-left: 1px solid var(--border-color);">
3688 <div class="flex justify-start" style="gap: 0.5rem;">
3689 ${getTag("DONT_OBFUSCATE", "OBFUSCATE")}
3690 ${getTag("DONT_OPTIMIZE", "OPTIMIZE")}
3691 ${getTag("DONT_SHRINK", "SHRINK")}
3692 </div>
3693 </td>
3694 `;
3695 const renderMatchCell = (count, total, borderLeft = true) => {
3696 const perc = total > 0 ? (count / total) * 100 : 0;
3697 const colorClass = UIUtils.getMatchClass(perc);
3698 const bl = borderLeft
3699 ? "border-left: 1px solid var(--border-color);"
3700 : "";
3701 return `
3702 <td class="text-center" style="padding: 1rem; ${bl}">
3703 <div style="display: flex; flex-direction: column; align-items: center; justify-content: center;">
3704 <span class="font-medium ${colorClass}">${perc.toFixed(1)}%</span>
3705 <span class="text-xs text-gray-500 mt-1">${count}</span>
3706 </div>
3707 </td>
3708 `;
3709 };
3710 const totalLive = getLiveItemCount(this.keepRadiusData);
3711 const liveClasses = this.keepRadiusData?.buildInfo?.liveClassCount || 0;
3712 const liveFields = this.keepRadiusData?.buildInfo?.liveFieldCount || 0;
3713 const liveMethods =
3714 this.keepRadiusData?.buildInfo?.liveMethodCount || 0;
3715 const highlightedSource = highlightRule(rule.source);
3716 return `
3717 <tr class="border-t border-gray-200 hover:bg-gray-50 cursor-pointer" onclick="App.showDetailsView('${rule.id}')">
3718 <td style="padding: 0.5rem; width: 40%;">
3719 <pre style="white-space: pre-wrap; font-family: var(--font-family-mono); font-size: 0.8125rem; margin: 0;">${highlightedSource}</pre>
3720 </td>
3721 ${renderMatchCell(c + f + m, totalLive, true)}
3722 ${renderMatchCell(c, liveClasses)}
3723 ${renderMatchCell(f, liveFields)}
3724 ${renderMatchCell(m, liveMethods)}
3725 ${impactCell}
3726 </tr>
3727 `;
3728 })
3729 .join("");
3730 const fileOriginName =
3731 formatMavenCoordinate(fileOrigin.mavenCoordinate) || fileOrigin.filename;
3732 this.updateFileDetailsBreadcrumbs(fileOriginName);
3733 },
3734 updateFileDetailsBreadcrumbs(filename) {
3735 const bc = document.getElementById("file-details-breadcrumbs");
3736 if (!bc) return;
3737 const linkClass = "breadcrumb-pill";
3738 const textClass = "breadcrumb-text";
3739 const sep = '<span class="text-gray-300 mx-1">/</span>';
3740 bc.innerHTML = `
3741 <span class="${linkClass}" id="file-details-back-to-summary">Project</span>
3742 ${sep}
3743 <span class="${textClass}">${escapeHTML(filename)}</span>
3744 `;
3745 document
3746 .getElementById("file-details-back-to-summary")
3747 .addEventListener("click", () => {
3748 this.showReportView();
3749 });
3750 },
3751 updateDetailsBreadcrumbs(filename, fileOriginId) {
3752 const bc = document.getElementById("details-breadcrumbs");
3753 if (!bc) return;
3754 const linkClass = "breadcrumb-pill";
3755 const textClass = "breadcrumb-text";
3756 const sep = '<span class="text-gray-300 mx-1">/</span>';
3757 let html = `
3758 <span class="${linkClass}" id="details-back-to-summary">Project</span>
3759 ${sep}`;
3760 if (filename && fileOriginId !== undefined) {
3761 html += `
3762 <span class="${linkClass}" id="details-back-to-file">${escapeHTML(filename)}</span>
3763 ${sep}`;
3764 }
3765 html += `<span class="${textClass}">Keep Rule Details</span>`;
3766 bc.innerHTML = html;
3767 document
3768 .getElementById("details-back-to-summary")
3769 .addEventListener("click", () => {
3770 this.showReportView();
3771 });
3772 if (filename && fileOriginId !== undefined) {
3773 document
3774 .getElementById("details-back-to-file")
3775 .addEventListener("click", () => {
3776 this.showFileDetailsView(fileOriginId);
3777 });
3778 }
3779 },
3780 async loadProtoData() {
3781 const embeddedProtoSchemaSource =
3782 document.getElementById("keepradius-proto");
3783 const embeddedProtoDataSource = document.getElementById("keepradius-data");
3784 try {
3785 const root = protobuf.parse(embeddedProtoSchemaSource.textContent).root;
3786 const data = embeddedProtoDataSource.textContent.trim();
3787 const bytes = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
3788 const KeepRadiusContainer = root.lookupType(
3789 "com.android.tools.r8.keepradius.proto.KeepRadiusContainer",
3790 );
3791 const message = KeepRadiusContainer.decode(bytes);
3792 this.keepRadiusData = KeepRadiusContainer.toObject(message, {
3793 longs: String,
3794 enums: String,
3795 bytes: String,
3796 defaults: true,
3797 arrays: true,
3798 objects: true,
3799 oneofs: true,
3800 });
3801 // Extract and strip common prefix from file names
3802 if (this.keepRadiusData.fileOriginTable) {
3803 const filenames = this.keepRadiusData.fileOriginTable
3804 .map((f) => f.filename)
3805 .filter(Boolean);
3806 const commonPrefix = UIUtils.findCommonPrefix(filenames);
3807 if (commonPrefix) {
3808 this.keepRadiusData.fileOriginTable.forEach((f) => {
3809 if (f.filename && f.filename.startsWith(commonPrefix)) {
3810 f.filename = f.filename.substring(commonPrefix.length);
3811 }
3812 });
3813 }
3814 }
3815 // Build highly optimized O(1) lookup Maps for details and filtering views
3816 App.lookups = {
3817 keptClassInfo: new Map(
3818 (this.keepRadiusData.keptClassInfoTable || []).map((c) => [c.id, c]),
3819 ),
3820 keptFieldInfo: new Map(
3821 (this.keepRadiusData.keptFieldInfoTable || []).map((f) => [f.id, f]),
3822 ),
3823 keptMethodInfo: new Map(
3824 (this.keepRadiusData.keptMethodInfoTable || []).map((m) => [m.id, m]),
3825 ),
3826 fieldReference: new Map(
3827 (this.keepRadiusData.fieldReferenceTable || []).map((r) => [r.id, r]),
3828 ),
3829 methodReference: new Map(
3830 (this.keepRadiusData.methodReferenceTable || []).map((r) => [
3831 r.id,
3832 r,
3833 ]),
3834 ),
3835 protoReference: new Map(
3836 (this.keepRadiusData.protoReferenceTable || []).map((p) => [p.id, p]),
3837 ),
3838 typeReferenceList: new Map(
3839 (this.keepRadiusData.typeReferenceListTable || []).map((l) => [
3840 l.id,
3841 l,
3842 ]),
3843 ),
3844 typeReference: new Map(
3845 (this.keepRadiusData.typeReferenceTable || []).map((t) => [
3846 t.id,
3847 t.javaDescriptor,
3848 ]),
3849 ),
3850 };
3851 console.log("Protobuf data loaded successfully:", this.keepRadiusData);
3852 // Trigger re-render now that data is available
3853 ReportApp.render();
3854 } catch (err) {
3855 console.error("Failed to load protobuf data:", err);
3856 }
3857 },
3858 };
3859 /* ==========================================================================
3860 REPORT APP (Grid/List View)
3861 ========================================================================== */
3862 const ReportApp = {
3863 state: {
3864 currentView: CONSTANTS.VIEWS.MODULES,
3865 filters: {
3866 keepRules: [],
3867 classes: [],
3868 fields: [],
3869 methods: [],
3870 },
3871 activeFilterChips: [],
3872 sort: {
3873 by: "matches.total",
3874 order: "desc",
3875 },
3876 drillContext: {
3877 module: null,
3878 pkg: null,
3879 fileOriginId: null,
3880 }, // Navigation path
3881 statsVisible: true,
3882 searchTerm: "",
3883 showBlockedByRule: true,
3884 },
3885 elements: {},
3886 init() {
3887 this.cacheDOMElements();
3888 this.populateHeaderInfo();
3889 this.populateFilters();
3890 this.bindEvents();
3891 this.render();
3892 },
3893 cacheDOMElements() {
3894 const getById = (id) => document.getElementById(id);
3895 this.elements = {
3896 clsChip: getById("cls-chip-container"),
3897 clsBtn: getById("cls-filter-btn"),
3898 clsDropdown: getById("cls-dropdown"),
3899 clsText: getById("cls-filter-text"),
3900 fieldChip: getById("field-chip-container"),
3901 fieldBtn: getById("field-filter-btn"),
3902 fieldDropdown: getById("field-dropdown"),
3903 fieldText: getById("field-filter-text"),
3904 methodChip: getById("method-chip-container"),
3905 methodBtn: getById("method-filter-btn"),
3906 methodDropdown: getById("method-dropdown"),
3907 methodText: getById("method-filter-text"),
3908 addFilterContainer: getById("add-filter-container"),
3909 addFilterBtn: getById("add-filter-btn"),
3910 addFilterDropdown: getById("add-filter-dropdown"),
3911 addFilterList: getById("add-filter-list"),
3912 grpBtn: getById("group-by-btn"),
3913 grpDropdown: getById("group-by-dropdown"),
3914 grpText: getById("group-by-text"),
3915 grpContainer: getById("group-by-container"),
3916 grpList: getById("group-by-list"),
3917 tableHeaders: getById("table-headers"),
3918 tableData: getById("table-data"),
3919 statsTableHeaders: getById("stats-table-headers"),
3920 statsTableData: getById("stats-table-data"),
3921 statsContainer: getById("stats-container"),
3922 mainTable: getById("main-table"),
3923 totalObfuscation: getById("total-obfuscation"),
3924 totalOptimization: getById("total-optimization"),
3925 totalShrinking: getById("total-shrinking"),
3926 searchInput: getById("search-input"),
3927 clearSearchBtn: getById("clear-search-btn"),
3928 searchContainer: getById("search-container"),
3929 searchIconBtn: getById("search-icon-btn"),
3930 toggleColumnsBtn: getById("toggle-columns-btn"),
3931 columnsDropdown: getById("columns-dropdown"),
3932 toggleBlockedByRuleCb: getById("toggle-blocked-by-rule-cb"),
3933 };
3934 },
3935 populateHeaderInfo() {},
3936 /**
3937 * Initializes all filters (Variants, Modules, etc.)
3938 */
3939 populateFilters() {
3940 // --- 1. Dynamic Filters (Modules/Packages/Classes) ---
3941 this.updateDynamicFilters();
3942 },
3943 /**
3944 * Updates the available options in Module/Package/Class dropdowns based on dependencies.
3945 * e.g., Selecting "Module A" filters the Package options to only those in Module A.
3946 */
3947 updateDynamicFilters() {
3948 const { keepRules, classes, fields, methods } = this.state.filters;
3949 const chips = this.state.activeFilterChips;
3950 // --- 1. Keep Rules Lens ---
3951 const keepRuleOptions = [
3952 {
3953 name: "<b>Identical:</b> Show rules that match the same items as other rules",
3954 value: "Identical",
3955 },
3956 {
3957 name: "<b>Subsumed:</b> Show rules that match a subset of the items matched by another rule",
3958 value: "Subsumed",
3959 },
3960 {
3961 name: "<b>Unused:</b> Show rules that don't match anything",
3962 value: "Unused",
3963 },
3964 ];
3965 const lens = this.state.filters.keepRules[0] || "All";
3966 const tabsContainer = document.getElementById("lens-tabs");
3967 if (tabsContainer) {
3968 tabsContainer.querySelectorAll(".segment-btn").forEach((btn) => {
3969 if (btn.dataset.lens === lens) {
3970 btn.classList.add("active");
3971 } else {
3972 btn.classList.remove("active");
3973 }
3974 });
3975 }
3976 // --- 2. Classes Filter ---
3977 const { options: classOptions, total: totalClasses } =
3978 this.getKeptClasses();
3979 UIUtils.buildActionDropdown(
3980 "cls-dropdown",
3981 classOptions,
3982 classes,
3983 () => {
3984 this.updateDynamicFilters();
3985 this.render();
3986 },
3987 true,
3988 true,
3989 totalClasses,
3990 "classes",
3991 (term) => {
3992 const brData = App.keepRadiusData;
3993 if (!brData || !brData.keptClassInfoTable)
3994 return {
3995 options: [],
3996 total: 0,
3997 };
3998 const typeRefMap = new Map();
3999 brData.typeReferenceTable.forEach((t) =>
4000 typeRefMap.set(t.id, t.javaDescriptor),
4001 );
4002 const filtered = brData.keptClassInfoTable.filter((c) => {
4003 const name = formatDescriptor(typeRefMap.get(c.classReferenceId));
4004 return name.toLowerCase().includes(term);
4005 });
4006 const results = filtered.slice(0, 1000).map((c) => {
4007 const name = escapeHTML(
4008 formatDescriptor(typeRefMap.get(c.classReferenceId)),
4009 );
4010 return {
4011 name,
4012 value: c.id,
4013 };
4014 });
4015 return {
4016 options: results,
4017 total: filtered.length,
4018 };
4019 },
4020 );
4021 UIUtils.renderChipText(
4022 this.elements.clsText,
4023 UIUtils.getFilterLabel("Classes", classes, totalClasses, classOptions),
4024 "class",
4025 true,
4026 );
4027 UIUtils.toggleVisibility(this.elements.clsChip, chips.includes("class"));
4028 // --- 3. Fields Filter ---
4029 const { options: fieldOptions, total: totalFields } = this.getKeptFields();
4030 UIUtils.buildActionDropdown(
4031 "field-dropdown",
4032 fieldOptions,
4033 fields,
4034 () => {
4035 this.updateDynamicFilters();
4036 this.render();
4037 },
4038 true,
4039 true,
4040 totalFields,
4041 "fields",
4042 (term) => {
4043 const brData = App.keepRadiusData;
4044 if (!brData || !brData.keptFieldInfoTable)
4045 return {
4046 options: [],
4047 total: 0,
4048 };
4049 const typeRefMap = new Map();
4050 brData.typeReferenceTable.forEach((t) =>
4051 typeRefMap.set(t.id, t.javaDescriptor),
4052 );
4053 const fieldRefMap = new Map();
4054 brData.fieldReferenceTable.forEach((f) => fieldRefMap.set(f.id, f));
4055 const filtered = brData.keptFieldInfoTable.filter((f) => {
4056 const fieldRef = fieldRefMap.get(f.fieldReferenceId);
4057 const name = formatFieldName(fieldRef, brData, typeRefMap);
4058 return name.toLowerCase().includes(term);
4059 });
4060 const results = filtered.slice(0, 1000).map((f) => {
4061 const fieldRef = fieldRefMap.get(f.fieldReferenceId);
4062 const name = escapeHTML(
4063 formatFieldName(fieldRef, brData, typeRefMap),
4064 );
4065 return {
4066 name,
4067 value: f.id,
4068 };
4069 });
4070 return {
4071 options: results,
4072 total: filtered.length,
4073 };
4074 },
4075 );
4076 UIUtils.renderChipText(
4077 this.elements.fieldText,
4078 UIUtils.getFilterLabel("Fields", fields, totalFields, fieldOptions),
4079 "field",
4080 true,
4081 );
4082 UIUtils.toggleVisibility(this.elements.fieldChip, chips.includes("field"));
4083 // --- 4. Methods Filter ---
4084 const { options: methodOptions, total: totalMethods } =
4085 this.getKeptMethods();
4086 UIUtils.buildActionDropdown(
4087 "method-dropdown",
4088 methodOptions,
4089 methods,
4090 () => {
4091 this.updateDynamicFilters();
4092 this.render();
4093 },
4094 true,
4095 true,
4096 totalMethods,
4097 "methods",
4098 (term) => {
4099 const brData = App.keepRadiusData;
4100 if (!brData || !brData.keptMethodInfoTable)
4101 return {
4102 options: [],
4103 total: 0,
4104 };
4105 const typeRefMap = new Map();
4106 brData.typeReferenceTable.forEach((t) =>
4107 typeRefMap.set(t.id, t.javaDescriptor),
4108 );
4109 const methodRefMap = new Map();
4110 brData.methodReferenceTable.forEach((m) => methodRefMap.set(m.id, m));
4111 const filtered = brData.keptMethodInfoTable.filter((m) => {
4112 const methodRef = methodRefMap.get(m.methodReferenceId);
4113 const name = formatMethodName(methodRef, brData, typeRefMap);
4114 return name.toLowerCase().includes(term);
4115 });
4116 const results = filtered.slice(0, 1000).map((m) => {
4117 const methodRef = methodRefMap.get(m.methodReferenceId);
4118 const name = escapeHTML(
4119 formatMethodName(methodRef, brData, typeRefMap),
4120 );
4121 return {
4122 name,
4123 value: m.id,
4124 };
4125 });
4126 return {
4127 options: results,
4128 total: filtered.length,
4129 };
4130 },
4131 );
4132 UIUtils.renderChipText(
4133 this.elements.methodText,
4134 UIUtils.getFilterLabel("Methods", methods, totalMethods, methodOptions),
4135 "method",
4136 true,
4137 );
4138 UIUtils.toggleVisibility(
4139 this.elements.methodChip,
4140 chips.includes("method"),
4141 );
4142 // --- Add Filter Button Logic ---
4143 const availableFilters = [];
4144 if (!chips.includes("class"))
4145 availableFilters.push({
4146 name: "Classes",
4147 value: "class",
4148 options: classOptions,
4149 total: totalClasses,
4150 state: classes,
4151 });
4152 if (!chips.includes("field"))
4153 availableFilters.push({
4154 name: "Fields",
4155 value: "field",
4156 options: fieldOptions,
4157 total: totalFields,
4158 state: fields,
4159 });
4160 if (!chips.includes("method"))
4161 availableFilters.push({
4162 name: "Methods",
4163 value: "method",
4164 options: methodOptions,
4165 total: totalMethods,
4166 state: methods,
4167 });
4168 if (availableFilters.length > 0) {
4169 this.elements.addFilterContainer.classList.remove("hidden");
4170 this.elements.addFilterList.innerHTML = availableFilters
4171 .map(
4172 (f) =>
4173 `<a href="#" class="dropdown-item add-filter-option" data-value="${f.value}">${f.name}</a>`,
4174 )
4175 .join("");
4176 this.elements.addFilterList
4177 .querySelectorAll(".add-filter-option")
4178 .forEach((item) => {
4179 item.addEventListener("click", (e) => {
4180 e.preventDefault();
4181 e.stopPropagation();
4182 const val = item.dataset.value;
4183 if (!this.state.activeFilterChips.includes(val)) {
4184 this.state.activeFilterChips.push(val);
4185 }
4186 // Close the "Add filter" dropdown
4187 this.elements.addFilterDropdown.classList.add("hidden");
4188 this.elements.addFilterBtn.classList.remove("bg-gray-200");
4189 this.updateDynamicFilters();
4190 this.render();
4191 // Auto-open newly added dropdown
4192 setTimeout(() => {
4193 if (val === "class")
4194 this.toggleDropdown(
4195 this.elements.clsDropdown,
4196 this.elements.clsBtn,
4197 );
4198 if (val === "field")
4199 this.toggleDropdown(
4200 this.elements.fieldDropdown,
4201 this.elements.fieldBtn,
4202 );
4203 if (val === "method")
4204 this.toggleDropdown(
4205 this.elements.methodDropdown,
4206 this.elements.methodBtn,
4207 );
4208 }, 0);
4209 });
4210 });
4211 } else {
4212 this.elements.addFilterContainer.classList.add("hidden");
4213 }
4214 },
4215 getKeptClasses() {
4216 const brData = App.keepRadiusData;
4217 if (!brData || !brData.keptClassInfoTable)
4218 return {
4219 options: [],
4220 total: 0,
4221 };
4222 const typeRefMap = new Map();
4223 brData.typeReferenceTable.forEach((t) =>
4224 typeRefMap.set(t.id, t.javaDescriptor),
4225 );
4226 const options = brData.keptClassInfoTable.slice(0, 1000).map((c) => {
4227 const name = escapeHTML(
4228 formatDescriptor(typeRefMap.get(c.classReferenceId)),
4229 );
4230 return {
4231 name,
4232 value: c.id,
4233 };
4234 });
4235 return {
4236 options,
4237 total: brData.keptClassInfoTable.length,
4238 };
4239 },
4240 getKeptFields() {
4241 const brData = App.keepRadiusData;
4242 if (!brData || !brData.keptFieldInfoTable)
4243 return {
4244 options: [],
4245 total: 0,
4246 };
4247 const typeRefMap = new Map();
4248 brData.typeReferenceTable.forEach((t) =>
4249 typeRefMap.set(t.id, t.javaDescriptor),
4250 );
4251 const fieldRefMap = new Map();
4252 brData.fieldReferenceTable.forEach((f) => fieldRefMap.set(f.id, f));
4253 const options = brData.keptFieldInfoTable.slice(0, 1000).map((f) => {
4254 const fieldRef = fieldRefMap.get(f.fieldReferenceId);
4255 const name = escapeHTML(formatFieldName(fieldRef, brData, typeRefMap));
4256 return {
4257 name,
4258 value: f.id,
4259 };
4260 });
4261 return {
4262 options,
4263 total: brData.keptFieldInfoTable.length,
4264 };
4265 },
4266 getKeptMethods() {
4267 const brData = App.keepRadiusData;
4268 if (!brData || !brData.keptMethodInfoTable)
4269 return {
4270 options: [],
4271 total: 0,
4272 };
4273 const typeRefMap = new Map();
4274 brData.typeReferenceTable.forEach((t) =>
4275 typeRefMap.set(t.id, t.javaDescriptor),
4276 );
4277 const methodRefMap = new Map();
4278 brData.methodReferenceTable.forEach((m) => methodRefMap.set(m.id, m));
4279 const options = brData.keptMethodInfoTable.slice(0, 1000).map((m) => {
4280 const methodRef = methodRefMap.get(m.methodReferenceId);
4281 const name = escapeHTML(formatMethodName(methodRef, brData, typeRefMap));
4282 return {
4283 name,
4284 value: m.id,
4285 };
4286 });
4287 return {
4288 options,
4289 total: brData.keptMethodInfoTable.length,
4290 };
4291 },
4292 toggleDropdown(dropdown, triggerBtn = null) {
4293 const allHelpers = [
4294 {
4295 dd: this.elements.grpDropdown,
4296 btn: this.elements.grpBtn,
4297 },
4298 {
4299 dd: this.elements.clsDropdown,
4300 btn: this.elements.clsBtn,
4301 },
4302 {
4303 dd: this.elements.fieldDropdown,
4304 btn: this.elements.fieldBtn,
4305 },
4306 {
4307 dd: this.elements.methodDropdown,
4308 btn: this.elements.methodBtn,
4309 },
4310 {
4311 dd: this.elements.addFilterDropdown,
4312 btn: this.elements.addFilterBtn,
4313 },
4314 {
4315 dd: this.elements.columnsDropdown,
4316 btn: this.elements.toggleColumnsBtn,
4317 },
4318 ];
4319 const isOpening = dropdown.classList.contains("hidden");
4320 // Close all others first
4321 allHelpers.forEach(({ dd, btn }) => {
4322 if (dd !== dropdown) {
4323 if (dd) dd.classList.add("hidden");
4324 if (btn) btn.classList.remove("bg-gray-200");
4325 }
4326 });
4327 if (isOpening) {
4328 dropdown.classList.remove("hidden");
4329 if (triggerBtn) triggerBtn.classList.add("bg-gray-200");
4330 } else {
4331 dropdown.classList.add("hidden");
4332 if (triggerBtn) triggerBtn.classList.remove("bg-gray-200");
4333 }
4334 },
4335 bindEvents() {
4336 // --- Dropdown Management ---
4337 const map = [
4338 {
4339 btn: this.elements.grpBtn,
4340 dd: this.elements.grpDropdown,
4341 },
4342 {
4343 btn: this.elements.clsBtn,
4344 dd: this.elements.clsDropdown,
4345 },
4346 {
4347 btn: this.elements.fieldBtn,
4348 dd: this.elements.fieldDropdown,
4349 },
4350 {
4351 btn: this.elements.methodBtn,
4352 dd: this.elements.methodDropdown,
4353 },
4354 {
4355 btn: this.elements.addFilterBtn,
4356 dd: this.elements.addFilterDropdown,
4357 },
4358 {
4359 btn: this.elements.toggleColumnsBtn,
4360 dd: this.elements.columnsDropdown,
4361 },
4362 ];
4363 map.forEach(({ btn, dd }) => {
4364 if (btn && dd) {
4365 btn.addEventListener("click", (e) => {
4366 if (e.target.closest(".chip-close")) return;
4367 e.stopPropagation();
4368 this.toggleDropdown(dd, btn);
4369 this.updateDynamicFilters();
4370 });
4371 }
4372 });
4373 // --- Column Toggle ---
4374 if (this.elements.toggleBlockedByRuleCb) {
4375 this.elements.toggleBlockedByRuleCb.addEventListener("change", (e) => {
4376 this.state.showBlockedByRule = e.target.checked;
4377 this.render();
4378 });
4379 }
4380 const tabsContainer = document.getElementById("lens-tabs");
4381 if (tabsContainer) {
4382 tabsContainer.addEventListener("click", (e) => {
4383 const btn = e.target.closest(".segment-btn");
4384 if (!btn) return;
4385 const lens = btn.dataset.lens;
4386 this.state.filters.keepRules = lens === "All" ? [] : [lens];
4387 this.updateDynamicFilters();
4388 this.render();
4389 });
4390 }
4391 // Global click listener to close dropdowns
4392 document.addEventListener("click", (e) => {
4393 let anyClosed = false;
4394 map.forEach(({ dd, btn }) => {
4395 if (dd && !dd.contains(e.target) && (!btn || !btn.contains(e.target))) {
4396 if (!dd.classList.contains("hidden")) {
4397 dd.classList.add("hidden");
4398 if (btn) btn.classList.remove("bg-gray-200");
4399 anyClosed = true;
4400 }
4401 }
4402 });
4403 if (anyClosed) {
4404 this.updateDynamicFilters();
4405 }
4406 // Close search bar if clicked outside
4407 if (
4408 this.elements.searchContainer &&
4409 !this.elements.searchContainer.contains(e.target)
4410 ) {
4411 this.elements.searchContainer.classList.remove("active");
4412 }
4413 });
4414 // Chip removals
4415 document
4416 .getElementById("filter-chips-container")
4417 .addEventListener("click", (e) => {
4418 const closeBtn = e.target.closest(".chip-close");
4419 if (closeBtn) {
4420 e.stopPropagation();
4421 const type = closeBtn.dataset.clear;
4422 if (type === "module") {
4423 this.state.filters.keepRules = [];
4424 this.state.activeFilterChips = this.state.activeFilterChips.filter(
4425 (c) => c !== "module",
4426 );
4427 } else if (type === "class") {
4428 this.state.filters.classes = [];
4429 this.state.activeFilterChips = this.state.activeFilterChips.filter(
4430 (c) => c !== "class",
4431 );
4432 } else if (type === "field") {
4433 this.state.filters.fields = [];
4434 this.state.activeFilterChips = this.state.activeFilterChips.filter(
4435 (c) => c !== "field",
4436 );
4437 } else if (type === "method") {
4438 this.state.filters.methods = [];
4439 this.state.activeFilterChips = this.state.activeFilterChips.filter(
4440 (c) => c !== "method",
4441 );
4442 }
4443 this.updateDynamicFilters();
4444 this.render();
4445 }
4446 });
4447 // --- Group By Dropdown ---
4448 this.elements.grpDropdown.addEventListener("click", (e) => {
4449 const item = e.target.closest(".dropdown-item");
4450 if (!item) return;
4451 e.preventDefault();
4452 this.state.currentView = item.dataset.value;
4453 this.state.drillContext = {
4454 module: null,
4455 pkg: null,
4456 }; // Reset Drill-Down
4457 // Reset default sort based on view
4458 if (
4459 this.state.currentView === CONSTANTS.VIEWS.CLASSES ||
4460 this.state.currentView === CONSTANTS.VIEWS.FIELDS ||
4461 this.state.currentView === CONSTANTS.VIEWS.METHODS
4462 ) {
4463 this.state.sort = {
4464 by: "name",
4465 order: "asc",
4466 };
4467 } else {
4468 this.state.sort = {
4469 by: "matches.total",
4470 order: "desc",
4471 };
4472 }
4473 this.toggleDropdown(this.elements.grpDropdown, this.elements.grpBtn);
4474 this.render();
4475 });
4476 // --- Table Sorting ---
4477 this.elements.tableHeaders.addEventListener("click", (e) => {
4478 const th = e.target.closest("[data-sort-by]");
4479 if (!th) return;
4480 const newSortBy = th.dataset.sortBy;
4481 if (this.state.sort.by === newSortBy)
4482 this.state.sort.order =
4483 this.state.sort.order === "asc" ? "desc" : "asc";
4484 else {
4485 this.state.sort.by = newSortBy;
4486 this.state.sort.order = "desc";
4487 }
4488 this.render();
4489 });
4490 // --- Table Row Interaction (Manual Drill-Down) ---
4491 this.elements.tableData.addEventListener("click", (e) => {
4492 const ruleTd = e.target.closest("td[data-rule-id]");
4493 if (ruleTd) {
4494 e.preventDefault();
4495 App.showDetailsView(ruleTd.dataset.ruleId);
4496 return;
4497 }
4498 const fileTd = e.target.closest("td[data-file-origin-id]");
4499 if (fileTd) {
4500 e.preventDefault();
4501 App.showFileDetailsView(fileTd.dataset.fileOriginId);
4502 return;
4503 }
4504 const td = e.target.closest("td[data-name]");
4505 if (!td) return;
4506 const { name, type, moduleName } = td.dataset;
4507 // Update Drill-Context (Silent)
4508 if (type === "module") {
4509 this.state.drillContext.module = name;
4510 this.state.drillContext.pkg = null;
4511 } else if (type === "package") {
4512 this.state.drillContext.module =
4513 moduleName || this.state.drillContext.module;
4514 this.state.drillContext.pkg = name;
4515 }
4516 this.updateBreadcrumbs();
4517 this.render();
4518 });
4519 // --- Search Icon Toggle ---
4520 if (this.elements.searchIconBtn) {
4521 this.elements.searchIconBtn.addEventListener("click", (e) => {
4522 e.stopPropagation();
4523 this.elements.searchContainer.classList.toggle("active");
4524 if (this.elements.searchContainer.classList.contains("active")) {
4525 this.elements.searchInput.focus();
4526 }
4527 });
4528 }
4529 // --- Search Input ---
4530 if (this.elements.searchInput) {
4531 this.elements.searchInput.addEventListener("input", (e) => {
4532 this.state.searchTerm = e.target.value;
4533 if (this.state.searchTerm) {
4534 this.elements.clearSearchBtn.classList.remove("hidden");
4535 } else {
4536 this.elements.clearSearchBtn.classList.add("hidden");
4537 }
4538 this.render();
4539 });
4540 }
4541 if (this.elements.clearSearchBtn) {
4542 this.elements.clearSearchBtn.addEventListener("click", () => {
4543 this.elements.searchInput.value = "";
4544 this.state.searchTerm = "";
4545 this.elements.clearSearchBtn.classList.add("hidden");
4546 this.render();
4547 });
4548 }
4549 // --- Help Hub Events ---
4550 const helpHubFab = document.getElementById("help-hub-fab");
4551 const helpHubPanel = document.getElementById("help-hub-panel");
4552 const closeHelpHubBtn = document.getElementById("close-help-hub");
4553 const helpHubSearchInput = document.querySelector(
4554 "#help-hub-panel .search-input",
4555 );
4556 if (helpHubFab && helpHubPanel) {
4557 helpHubFab.addEventListener("click", (e) => {
4558 e.stopPropagation();
4559 helpHubPanel.classList.toggle("open");
4560 });
4561 }
4562 if (closeHelpHubBtn && helpHubPanel) {
4563 closeHelpHubBtn.addEventListener("click", (e) => {
4564 e.stopPropagation();
4565 helpHubPanel.classList.remove("open");
4566 });
4567 }
4568 // Close help hub when clicking outside
4569 document.addEventListener("click", (e) => {
4570 if (
4571 helpHubPanel &&
4572 !helpHubPanel.contains(e.target) &&
4573 !helpHubFab.contains(e.target)
4574 ) {
4575 helpHubPanel.classList.remove("open");
4576 }
4577 });
4578 // Help Hub Accordion
4579 if (helpHubPanel) {
4580 const legendItems = helpHubPanel.querySelectorAll(".legend-item");
4581 legendItems.forEach((item) => {
4582 const header = item.querySelector(".legend-item-header");
4583 if (header) {
4584 header.addEventListener("click", () => {
4585 // Close all other items
4586 legendItems.forEach((other) => {
4587 if (other !== item) {
4588 other.classList.remove("open");
4589 }
4590 });
4591 // Toggle current item
4592 item.classList.toggle("open");
4593 });
4594 }
4595 });
4596 }
4597 },
4598 /**
4599 * Core Data Pipeline: Filters and Flattens data based on current View Mode and Filters.
4600 */
4601 applyKeepRuleLens(rules, lens) {
4602 const brData = App.keepRadiusData;
4603 if (!lens || !brData) return rules;
4604 const ruleMap = new Map();
4605 brData.keepRuleKeepRadiusTable.forEach((r) => {
4606 ruleMap.set(r.id, r);
4607 });
4608 return rules.filter((r) => {
4609 const getSubsumedBy = (rule) =>
4610 rule.subsumedBy ||
4611 (rule.keepRadius && rule.keepRadius.subsumedBy) ||
4612 [];
4613 const getMatchesTotal = (rule) => {
4614 if (rule.matches && rule.matches.total !== undefined)
4615 return rule.matches.total;
4616 const b = rule.keepRadius || {};
4617 return (
4618 (b.classKeepRadius || []).length +
4619 (b.fieldKeepRadius || []).length +
4620 (b.methodKeepRadius || []).length
4621 );
4622 };
4623 const subsumedBy = getSubsumedBy(r);
4624 if (lens === "Identical") {
4625 const identical = subsumedBy
4626 .filter((otherId) => {
4627 const other = ruleMap.get(otherId);
4628 return other && (other.keepRadius?.subsumedBy || []).includes(r.id);
4629 })
4630 .map((otherId) => ruleMap.get(otherId));
4631 if (identical.length > 0) {
4632 r.identicalRules = identical;
4633 return true;
4634 }
4635 return false;
4636 } else if (lens === "Subsumed") {
4637 if (subsumedBy.length > 0) {
4638 r.subsumedByRules = subsumedBy.map((id) => ruleMap.get(id));
4639 return true;
4640 }
4641 return false;
4642 } else if (lens === "Unused") {
4643 return getMatchesTotal(r) === 0;
4644 }
4645 return true;
4646 });
4647 },
4648 getFilteredData() {
4649 const { currentView, filters } = this.state;
4650 const brData = App.keepRadiusData;
4651 if (!brData) return [];
4652 let data = [];
4653 if (currentView === CONSTANTS.VIEWS.PACKAGES) {
4654 data = getRuleFiles(brData).filter((f) => f.matches.total > 0);
4655 } else {
4656 data = getRules(brData);
4657 // If lens is 'Residual', 'All', or default, show only rules with matches > 0.
4658 // If lens is 'Unused', show only rules with matches === 0.
4659 const lens = filters.keepRules[0];
4660 if (lens === "Residual" || lens === "All" || !lens) {
4661 data = data.filter((r) => r.matches.total > 0);
4662 } else if (lens === "Unused") {
4663 data = data.filter((r) => r.matches.total === 0);
4664 }
4665 }
4666 // Apply Keep Rules Lens (Filtering)
4667 if (
4668 currentView === CONSTANTS.VIEWS.MODULES &&
4669 filters.keepRules.length > 0
4670 ) {
4671 data = this.applyKeepRuleLens(data, filters.keepRules[0]);
4672 }
4673 // Apply Class/Field/Method filters
4674 if (
4675 filters.classes.length > 0 ||
4676 filters.fields.length > 0 ||
4677 filters.methods.length > 0
4678 ) {
4679 const matchedRuleIds = new Set();
4680 if (filters.classes.length > 0) {
4681 brData.keptClassInfoTable.forEach((c) => {
4682 if (filters.classes.includes(c.id)) {
4683 (c.keptBy || []).forEach((rid) => matchedRuleIds.add(rid));
4684 }
4685 });
4686 }
4687 if (filters.fields.length > 0) {
4688 brData.keptFieldInfoTable.forEach((f) => {
4689 if (filters.fields.includes(f.id)) {
4690 (f.keptBy || []).forEach((rid) => matchedRuleIds.add(rid));
4691 }
4692 });
4693 }
4694 if (filters.methods.length > 0) {
4695 brData.keptMethodInfoTable.forEach((m) => {
4696 if (filters.methods.includes(m.id)) {
4697 (m.keptBy || []).forEach((rid) => matchedRuleIds.add(rid));
4698 }
4699 });
4700 }
4701 data = data.filter((r) => matchedRuleIds.has(r.id));
4702 }
4703 if (this.state.searchTerm) {
4704 const term = this.state.searchTerm.toLowerCase();
4705 data = data.filter((item) => item.name.toLowerCase().includes(term));
4706 }
4707 return data;
4708 },
4709 getSortedData(data) {
4710 const { by, order } = this.state.sort;
4711 if (!by) return data;
4712 const getVal = (obj, path) => {
4713 return path
4714 .split(".")
4715 .reduce((o, key) => (o && o[key] !== undefined ? o[key] : 0), obj);
4716 };
4717 return [...data].sort((a, b) => {
4718 const vA = getVal(a, by);
4719 const vB = getVal(b, by);
4720 if (typeof vA === "string")
4721 return order === "asc" ? vA.localeCompare(vB) : vB.localeCompare(vA);
4722 return order === "asc" ? (vA || 0) - (vB || 0) : (vB || 0) - (vA || 0);
4723 });
4724 },
4725 updateBreadcrumbs() {
4726 const bc = document.getElementById("flat-breadcrumbs");
4727 if (!bc) return;
4728 const linkClass = "breadcrumb-pill";
4729 const textClass = "breadcrumb-text";
4730 const sep = '<span class="text-gray-300 mx-1">/</span>';
4731 let html = "";
4732 const { module, pkg: pkg } = this.state.drillContext;
4733 const toggleText = this.state.statsVisible ? "Hide" : "Show";
4734 const toggleHtml = `
4735 <button class="dropdown-btn" data-action="toggle-stats" style="border: none; background: transparent; padding: 0.25rem 0.5rem; display: flex; align-items: center; border-radius: 4px; margin-left: auto; cursor: pointer;">
4736 <span style="font-weight: 500; color: var(--text-gray-400);">${toggleText}</span>
4737 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24"
4738 stroke="currentColor" style="margin-left: 0.25rem; color: var(--text-gray-500); transform: ${this.state.statsVisible ? "rotate(180deg)" : "rotate(0deg)"}; transition: transform 0.2s;">
4739 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
4740 </svg>
4741 </button>
4742 `;
4743 if (module) {
4744 // Drilled Down Path
4745 html += `<span class="${linkClass}" data-action="reset">R8 Optimization Levels</span>`;
4746 html += sep;
4747 if (pkg) {
4748 html += `<span class="${linkClass}" data-action="module" data-val="${escapeHTML(module)}">${escapeHTML(module)}</span>`;
4749 html += sep;
4750 html += `<span class="${textClass}">${escapeHTML(pkg)}</span>`;
4751 } else {
4752 html += `<span class="${textClass}">${escapeHTML(module)}</span>`;
4753 }
4754 } else {
4755 // Global View
4756 html = `<span class="${textClass}">R8 Optimization Levels</span>`;
4757 }
4758
4759 const subtextHtml = `
4760 <div style="font-size: 0.75rem; color: var(--text-gray-500); font-weight: 400; margin-top: 0.25rem; text-transform: none; letter-spacing: normal; padding: 0rem 0.5rem;">
4761 The percentage of your app’s codebase that R8 is allowed to shrink, optimize, and obfuscate. Achieving a higher percentage indicates a leaner, more performant application. The percentage here reflect the initial evaluation of the keep rules before optimizations. Although this percentage will change after further optimization, it is a very good proxy for analyzing the impact of keep rules. For the final percentage after all optimizations, build your app bundle, and see r8.json.
4762 </div>
4763 `;
4764 bc.style.display = "flex";
4765 bc.style.alignItems = "center";
4766 bc.style.width = "100%";
4767 bc.innerHTML = `
4768 <div style="display: flex; flex-direction: column; align-items: start;">
4769 <div style="display: flex; align-items: center;">
4770 ${html}
4771 </div>
4772 ${this.state.statsVisible ? subtextHtml : ""}
4773 </div>
4774 ${toggleHtml}
4775 `;
4776 // Breadcrumb Click Handlers
4777 bc.querySelectorAll("[data-action]").forEach((el) => {
4778 el.addEventListener("click", (e) => {
4779 const action = el.dataset.action;
4780 if (action === "reset") {
4781 this.state.drillContext = {
4782 module: null,
4783 pkg: null,
4784 };
4785 this.render();
4786 } else if (action === "module") {
4787 this.state.drillContext.pkg = null;
4788 this.render();
4789 } else if (action === "toggle-stats") {
4790 this.state.statsVisible = !this.state.statsVisible;
4791 this.render();
4792 }
4793 });
4794 });
4795 },
4796 render() {
4797 this.updateBreadcrumbs();
4798 if (this.state.currentView === CONSTANTS.VIEWS.FILE_DETAILS) {
4799 App.renderFileDetailsView(this.state.drillContext.fileOriginId);
4800 return;
4801 }
4802 if (this.state.currentView === CONSTANTS.VIEWS.DETAILS) {
4803 return;
4804 }
4805 // Update Label of Group Dropdown
4806 const currentView = this.state.currentView;
4807 const viewLabels = {
4808 modules: "Keep Rules",
4809 packages: "Keep Rule Files",
4810 };
4811 this.elements.grpText.textContent =
4812 viewLabels[currentView] ||
4813 currentView.charAt(0).toUpperCase() + currentView.slice(1);
4814 if (this.elements.grpList) {
4815 this.elements.grpList.querySelectorAll(".dropdown-item").forEach((el) => {
4816 if (el.dataset.value === currentView)
4817 el.classList.add("bg-gray-100", "text-gray-900", "font-semibold");
4818 else
4819 el.classList.remove("bg-gray-100", "text-gray-900", "font-semibold");
4820 });
4821 }
4822 // Process Data
4823 let data = this.getFilteredData();
4824 data = this.getSortedData(data);
4825 // Update Summary Counts
4826 let relevantClasses = [],
4827 modCount = 0;
4828 if (currentView === CONSTANTS.VIEWS.PACKAGES) {
4829 relevantClasses = data.flatMap((p) => p.classes || []);
4830 modCount = new Set(data.map((p) => p.moduleName)).size;
4831 } else {
4832 relevantClasses = data.flatMap((m) =>
4833 (m.packages || []).flatMap((p) => p.classes || []),
4834 );
4835 modCount = data.length;
4836 }
4837 // Stats table visibility
4838 if (this.elements.statsContainer) {
4839 this.elements.statsContainer.style.display = this.state.statsVisible
4840 ? "flex"
4841 : "none";
4842 this.elements.statsContainer.style.marginBottom = this.state.statsVisible
4843 ? "2rem"
4844 : "0";
4845 }
4846 const brData = App.keepRadiusData;
4847 const totalLive = getLiveItemCount(brData);
4848 const formatPerc = (disallowCount) =>
4849 totalLive > 0
4850 ? (100 - (disallowCount / totalLive) * 100).toFixed(1) + "%"
4851 : "--";
4852 const getPerc = (disallowCount) =>
4853 totalLive > 0 ? 100 - (disallowCount / totalLive) * 100 : "--";
4854 const setStatValue = (element, disallowCount) => {
4855 if (!brData) {
4856 element.textContent = "--";
4857 element.className = "stat-value " + UIUtils.getScoreClass("--");
4858 return;
4859 }
4860 const perc = getPerc(disallowCount);
4861 element.textContent = formatPerc(disallowCount);
4862 element.className = "stat-value " + UIUtils.getScoreClass(perc);
4863 };
4864 setStatValue(
4865 this.elements.totalObfuscation,
4866 getDisallowObfuscationCount(brData),
4867 );
4868 setStatValue(
4869 this.elements.totalOptimization,
4870 getDisallowOptimizationCount(brData),
4871 );
4872 setStatValue(
4873 this.elements.totalShrinking,
4874 getDisallowShrinkingCount(brData),
4875 );
4876 // Render Tables
4877 this.renderHeaders();
4878 this.renderFlatRows(data);
4879 this.renderStatsTable();
4880 },
4881 renderStatsTable() {
4882 const brData = App.keepRadiusData;
4883 if (!brData) return;
4884 const stats = getDetailedStats(brData);
4885 if (!stats) return;
4886 const updateCard = (key) => {
4887 const setStat = (id, disallowCount, total) => {
4888 const el = document.getElementById(id);
4889 const perc = total > 0 ? 100 - (disallowCount / total) * 100 : 100;
4890 if (el) {
4891 el.textContent = perc.toFixed(1) + "%";
4892 }
4893 return perc;
4894 };
4895 const totalPerc = setStat(
4896 `card-total-${key}`,
4897 stats.overall[key],
4898 stats.overall.total,
4899 );
4900 const updateItem = (type) => {
4901 const bar = document.getElementById(`card-${key}-${type}-bar`);
4902 const val = document.getElementById(`card-${key}-${type}-val`);
4903 if (bar && val) {
4904 const disallow = stats[type][key];
4905 const total = stats[type].total;
4906 const perc = total > 0 ? 100 - (disallow / total) * 100 : 100;
4907 bar.style.width = `${perc}%`;
4908 val.textContent = `${perc.toFixed(1)}%`;
4909 const colorClass = UIUtils.getScoreClass(perc);
4910 if (colorClass.includes("green")) {
4911 bar.style.background = "var(--text-green-600)";
4912 } else if (colorClass.includes("yellow")) {
4913 bar.style.background = "var(--text-yellow-600)";
4914 } else if (colorClass.includes("red")) {
4915 bar.style.background = "var(--text-red-600)";
4916 }
4917 }
4918 };
4919 updateItem("classes");
4920 updateItem("fields");
4921 updateItem("methods");
4922 };
4923 updateCard("obfuscation");
4924 updateCard("optimization");
4925 updateCard("shrinking");
4926 },
4927 renderHeaders() {
4928 const { currentView, sort } = this.state;
4929 const topHeader = document.createElement("tr");
4930 const subHeader = document.createElement("tr");
4931 const viewLabels = {
4932 modules: "RULE",
4933 packages: "KEEP RULE FILES",
4934 };
4935 const title = viewLabels[currentView];
4936 const ind = (key) => {
4937 if (sort.by !== key) return "";
4938 return sort.order === "asc"
4939 ? `<span class="sort-icon text-blue-600 ml-1">▲</span>`
4940 : `<span class="sort-icon text-blue-600 ml-1">▼</span>`;
4941 };
4942 if (currentView === CONSTANTS.VIEWS.PACKAGES) {
4943 topHeader.innerHTML = `
4944 <th rowspan="2" class="text-left sticky-name bg-gray-50 z-30" data-sort-by="name" style="width: 40%; min-width: 300px;">
4945 <div class="flex items-center cursor-pointer hover:text-blue-600" style="padding: 1rem;">
4946 ${title}${ind("name")}
4947 </div>
4948 </th>
4949 <th rowspan="2" class="text-center bg-gray-50 border-l border-gray-200 cursor-pointer hover:bg-gray-100" data-sort-by="keepRules" style="padding: 1rem; width: 12%;">Keep Rules${ind("keepRules")}</th>
4950 <th rowspan="2" class="text-center border-l border-gray-200 bg-gray-50 cursor-pointer hover:bg-gray-100" data-sort-by="matches.total" style="padding: 0.5rem; width: 12%;">Kept Items${ind("matches.total")}</th>
4951 <th rowspan="2" class="text-center text-xs font-medium text-gray-500 border-l border-gray-200 cursor-pointer hover:bg-gray-100" data-sort-by="impact.obfuscation" style="padding: 0.5rem; width: 12%;">Obfuscation Score${ind("impact.obfuscation")}</th>
4952 <th rowspan="2" class="text-center text-xs font-medium text-gray-500 border-l border-gray-200 cursor-pointer hover:bg-gray-100" data-sort-by="impact.optimization" style="padding: 0.5rem; width: 12%;">Optimization Score${ind("impact.optimization")}</th>
4953 <th rowspan="2" class="text-center text-xs font-medium text-gray-500 border-l border-gray-200 cursor-pointer hover:bg-gray-100" data-sort-by="impact.shrinking" style="padding: 0.5rem; width: 12%;">Shrinking Score${ind("impact.shrinking")}</th>
4954 `;
4955 subHeader.innerHTML = ``;
4956 } else {
4957 const lens = this.state.filters.keepRules[0];
4958 const isIdenticalLens =
4959 currentView === CONSTANTS.VIEWS.MODULES && lens === "Identical";
4960 const isSubsumedLens =
4961 currentView === CONSTANTS.VIEWS.MODULES && lens === "Subsumed";
4962 const isUnusedLens =
4963 currentView === CONSTANTS.VIEWS.MODULES && lens === "Unused";
4964 if (this.elements.mainTable) {
4965 if (isIdenticalLens || isSubsumedLens || isUnusedLens) {
4966 this.elements.mainTable.style.tableLayout = "fixed";
4967 } else {
4968 this.elements.mainTable.style.tableLayout = "";
4969 }
4970 }
4971 if (isIdenticalLens || isSubsumedLens) {
4972 topHeader.innerHTML = `
4973 <th rowspan="2" class="text-left sticky-name bg-gray-50 z-30" data-sort-by="name" style="width: 50%; min-width: 300px;">
4974 <div class="flex items-center cursor-pointer hover:text-blue-600" style="padding: 1rem;">
4975 ${title}${ind("name")}
4976 </div>
4977 </th>
4978 <th rowspan="2" class="text-left bg-gray-50 border-l border-gray-200" style="padding: 1rem; width: 50%;">${isIdenticalLens ? "Identical Rules" : "Subsumed By"}</th>
4979 `;
4980 subHeader.innerHTML = "";
4981 } else if (isUnusedLens) {
4982 topHeader.innerHTML = `
4983 <th rowspan="2" class="text-left sticky-name bg-gray-50 z-30" data-sort-by="name" style="width: 50%; min-width: 300px;">
4984 <div class="flex items-center cursor-pointer hover:text-blue-600" style="padding: 1rem;">
4985 ${title}${ind("name")}
4986 </div>
4987 </th>
4988 <th rowspan="2" class="text-left bg-gray-50 border-l border-gray-200" style="padding: 1rem; width: 50%;">Origin</th>
4989 `;
4990 subHeader.innerHTML = "";
4991 } else {
4992 topHeader.innerHTML = `
4993 <th rowspan="2" class="text-left sticky-name bg-gray-50 z-30" data-sort-by="name" style="width: 40%; min-width: 300px;">
4994 <div class="flex items-center cursor-pointer hover:text-blue-600" style="padding: 1rem;">
4995 ${title}${ind("name")}
4996 </div>
4997 </th>
4998 <th colspan="4" class="text-center border-l border-gray-200 bg-gray-50" style="padding: 0.5rem; width: 40%;">KEPT ITEMS <span style="color: var(--text-muted);text-transform: none;font-weight: 500;">Higher is worse</span> <button type="button" class="tooltip-icon" data-tooltip="Items retained in the app due to this rule" aria-label="Items retained in the app due to this rule">?</button></th>
4999 ${
5000 this.state.showBlockedByRule
Showing first 5,000 of 6,018 lines. View raw