@nocios/crudify-components 2.0.36 → 2.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/coverage/components/CrudiaAutoGenerate.tsx.html +583 -0
  2. package/coverage/components/CrudiaFileField/CrudiaFileField.tsx.html +1276 -0
  3. package/coverage/components/CrudiaFileField/components/DeleteConfirmationDialog.tsx.html +355 -0
  4. package/coverage/components/CrudiaFileField/components/DropZone.tsx.html +433 -0
  5. package/coverage/components/CrudiaFileField/components/FileItemRow.tsx.html +751 -0
  6. package/coverage/components/CrudiaFileField/components/FileThumbnail.tsx.html +1195 -0
  7. package/coverage/components/CrudiaFileField/components/index.html +176 -0
  8. package/coverage/components/CrudiaFileField/components/index.ts.html +115 -0
  9. package/coverage/components/CrudiaFileField/hooks/index.html +146 -0
  10. package/coverage/components/CrudiaFileField/hooks/index.ts.html +112 -0
  11. package/coverage/components/CrudiaFileField/hooks/useDeleteConfirmation.ts.html +421 -0
  12. package/coverage/components/CrudiaFileField/hooks/useDragDrop.ts.html +403 -0
  13. package/coverage/components/CrudiaFileField/index.html +131 -0
  14. package/coverage/components/CrudiaFileField/index.ts.html +112 -0
  15. package/coverage/components/CrudiaFileField/utils/formatters.ts.html +163 -0
  16. package/coverage/components/CrudiaFileField/utils/icons.tsx.html +253 -0
  17. package/coverage/components/CrudiaFileField/utils/index.html +131 -0
  18. package/coverage/components/CrudiaMarkdownField.tsx.html +619 -0
  19. package/coverage/components/CrudifyLogin/Forms/CheckCodeForm.tsx.html +586 -0
  20. package/coverage/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx.html +694 -0
  21. package/coverage/components/CrudifyLogin/Forms/LoginForm.tsx.html +835 -0
  22. package/coverage/components/CrudifyLogin/Forms/ResetPasswordForm.tsx.html +1180 -0
  23. package/coverage/components/CrudifyLogin/Forms/components/CodeInput.tsx.html +283 -0
  24. package/coverage/components/CrudifyLogin/Forms/components/FormAlert.tsx.html +202 -0
  25. package/coverage/components/CrudifyLogin/Forms/components/PasswordInput.tsx.html +340 -0
  26. package/coverage/components/CrudifyLogin/Forms/components/index.html +161 -0
  27. package/coverage/components/CrudifyLogin/Forms/components/index.ts.html +106 -0
  28. package/coverage/components/CrudifyLogin/Forms/index.html +161 -0
  29. package/coverage/components/CrudifyLogin/Forms/utils/errorTranslation.ts.html +268 -0
  30. package/coverage/components/CrudifyLogin/Forms/utils/index.html +161 -0
  31. package/coverage/components/CrudifyLogin/Forms/utils/index.ts.html +106 -0
  32. package/coverage/components/CrudifyLogin/Forms/utils/paramUtils.ts.html +478 -0
  33. package/coverage/components/CrudifyLogin/Forms/utils/validation.ts.html +289 -0
  34. package/coverage/components/CrudifyLogin/components/CrudifyInitializer.tsx.html +262 -0
  35. package/coverage/components/CrudifyLogin/components/index.html +116 -0
  36. package/coverage/components/CrudifyLogin/context/CrudifyProvider.tsx.html +382 -0
  37. package/coverage/components/CrudifyLogin/context/I18nProvider.tsx.html +397 -0
  38. package/coverage/components/CrudifyLogin/context/LoginStateProvider.tsx.html +1249 -0
  39. package/coverage/components/CrudifyLogin/context/index.html +146 -0
  40. package/coverage/components/CrudifyLogin/hooks/index.html +116 -0
  41. package/coverage/components/CrudifyLogin/hooks/useTranslationsFromUrl.ts.html +292 -0
  42. package/coverage/components/CrudifyLogin/index.html +116 -0
  43. package/coverage/components/CrudifyLogin/index.tsx.html +475 -0
  44. package/coverage/components/GlobalNotificationProvider.tsx.html +781 -0
  45. package/coverage/components/LoginComponent.tsx.html +727 -0
  46. package/coverage/components/PasswordRequirements/index.html +116 -0
  47. package/coverage/components/PasswordRequirements/index.tsx.html +226 -0
  48. package/coverage/components/PublicPolicies/FieldSelector/FieldSelector.tsx.html +982 -0
  49. package/coverage/components/PublicPolicies/FieldSelector/index.html +131 -0
  50. package/coverage/components/PublicPolicies/FieldSelector/index.ts.html +85 -0
  51. package/coverage/components/PublicPolicies/Policies.tsx.html +610 -0
  52. package/coverage/components/PublicPolicies/PolicyItem/PolicyItem.tsx.html +856 -0
  53. package/coverage/components/PublicPolicies/PolicyItem/index.html +131 -0
  54. package/coverage/components/PublicPolicies/PolicyItem/index.ts.html +85 -0
  55. package/coverage/components/PublicPolicies/constants.ts.html +127 -0
  56. package/coverage/components/PublicPolicies/index.html +131 -0
  57. package/coverage/components/SessionTimeIndicator/index.html +116 -0
  58. package/coverage/components/SessionTimeIndicator/index.tsx.html +505 -0
  59. package/coverage/components/UserProfile/UserProfileDisplay.tsx.html +826 -0
  60. package/coverage/components/UserProfile/index.html +131 -0
  61. package/coverage/components/UserProfile/index.ts.html +85 -0
  62. package/coverage/components/index.html +176 -0
  63. package/coverage/components/index.ts.html +160 -0
  64. package/coverage/core/CrossTabSyncManager.ts.html +814 -0
  65. package/coverage/core/CrudifyInitializationManager.ts.html +1132 -0
  66. package/coverage/core/SessionManager.ts.html +2764 -0
  67. package/coverage/core/index.html +146 -0
  68. package/coverage/coverage-final.json +52 -85
  69. package/coverage/hooks/index.html +131 -0
  70. package/coverage/hooks/useAutoGenerate.ts.html +562 -0
  71. package/coverage/hooks/useFileUpload/index.html +131 -0
  72. package/coverage/hooks/useFileUpload/index.ts.html +112 -0
  73. package/coverage/hooks/useFileUpload/services/index.html +116 -0
  74. package/coverage/hooks/useFileUpload/services/uploadService.ts.html +610 -0
  75. package/coverage/hooks/useFileUpload/useFileUpload.ts.html +1870 -0
  76. package/coverage/hooks/useFileUpload/utils/fileUtils.ts.html +271 -0
  77. package/coverage/hooks/useFileUpload/utils/index.html +146 -0
  78. package/coverage/hooks/useFileUpload/utils/mimeTypes.ts.html +235 -0
  79. package/coverage/hooks/useFileUpload/utils/validation.ts.html +379 -0
  80. package/coverage/hooks/useSession/constants.ts.html +217 -0
  81. package/coverage/hooks/useSession/hooks/index.html +176 -0
  82. package/coverage/hooks/useSession/hooks/useAuthEventSubscriber.ts.html +331 -0
  83. package/coverage/hooks/useSession/hooks/useCrossTabSync.ts.html +433 -0
  84. package/coverage/hooks/useSession/hooks/useSessionActions.ts.html +664 -0
  85. package/coverage/hooks/useSession/hooks/useSessionState.ts.html +295 -0
  86. package/coverage/hooks/useSession/hooks/useTokenRefreshScheduler.ts.html +490 -0
  87. package/coverage/hooks/useSession/index.html +161 -0
  88. package/coverage/hooks/useSession/index.ts.html +127 -0
  89. package/coverage/hooks/useSession/types.ts.html +427 -0
  90. package/coverage/hooks/useSession/useSession.ts.html +526 -0
  91. package/coverage/hooks/useSession/utils/index.html +131 -0
  92. package/coverage/hooks/useSession/utils/initializeSession.ts.html +424 -0
  93. package/coverage/hooks/useSession/utils/tokenUtils.ts.html +280 -0
  94. package/coverage/hooks/useUserProfile.ts.html +658 -0
  95. package/coverage/index.html +237 -357
  96. package/coverage/providers/SessionProvider.tsx.html +1150 -0
  97. package/coverage/providers/TranslationsProvider.tsx.html +1450 -0
  98. package/coverage/providers/index.html +131 -0
  99. package/coverage/services/credentialsEventBus.ts.html +310 -0
  100. package/coverage/services/index.html +131 -0
  101. package/coverage/services/translationService.ts.html +1318 -0
  102. package/coverage/translations/critical.ts.html +1195 -0
  103. package/coverage/translations/index.html +116 -0
  104. package/coverage/types/index.html +116 -0
  105. package/coverage/types/password.ts.html +178 -0
  106. package/coverage/utils/authEventBus.ts.html +454 -0
  107. package/coverage/utils/configResolver.ts.html +460 -0
  108. package/coverage/utils/cookieSync.ts.html +580 -0
  109. package/coverage/utils/errorHandler.ts.html +1264 -0
  110. package/coverage/utils/errorTranslation.ts.html +862 -0
  111. package/coverage/utils/index.html +296 -0
  112. package/coverage/utils/jwtUtils.ts.html +301 -0
  113. package/coverage/utils/logger.ts.html +901 -0
  114. package/coverage/utils/navigationTracker.ts.html +565 -0
  115. package/coverage/utils/passwordValidation.ts.html +259 -0
  116. package/coverage/utils/redirectSecurity.ts.html +715 -0
  117. package/coverage/utils/tenantConfig.ts.html +700 -0
  118. package/coverage/utils/tokenStorage.ts.html +1768 -0
  119. package/coverage/utils/webCrypto.ts.html +472 -0
  120. package/dist/{CrudiaMarkdownField-Bxl08sRG.d.mts → CrudiaMarkdownField-BvJn2GL8.d.mts} +5 -0
  121. package/dist/{CrudiaMarkdownField-DUJNOOzP.d.ts → CrudiaMarkdownField-CggOpcBM.d.ts} +5 -0
  122. package/dist/chunk-SYHNHKFA.mjs +1 -0
  123. package/dist/chunk-W4ZYKPOK.js +1 -0
  124. package/dist/components.d.mts +1 -1
  125. package/dist/components.d.ts +1 -1
  126. package/dist/components.js +1 -1
  127. package/dist/components.mjs +1 -1
  128. package/dist/index.d.mts +6 -2
  129. package/dist/index.d.ts +6 -2
  130. package/dist/index.js +2 -2
  131. package/dist/index.mjs +1 -1
  132. package/package.json +1 -1
  133. package/dist/chunk-QFACMV2I.mjs +0 -1
  134. package/dist/chunk-QZH3KSJZ.js +0 -1
@@ -0,0 +1,2764 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for core/SessionManager.ts</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> / <a href="index.html">core</a> SessionManager.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">22.17% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>198/893</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">100% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>0/0</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">0% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>0/24</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">22.17% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>198/893</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line low'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a>
91
+ <a name='L26'></a><a href='#L26'>26</a>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a>
99
+ <a name='L34'></a><a href='#L34'>34</a>
100
+ <a name='L35'></a><a href='#L35'>35</a>
101
+ <a name='L36'></a><a href='#L36'>36</a>
102
+ <a name='L37'></a><a href='#L37'>37</a>
103
+ <a name='L38'></a><a href='#L38'>38</a>
104
+ <a name='L39'></a><a href='#L39'>39</a>
105
+ <a name='L40'></a><a href='#L40'>40</a>
106
+ <a name='L41'></a><a href='#L41'>41</a>
107
+ <a name='L42'></a><a href='#L42'>42</a>
108
+ <a name='L43'></a><a href='#L43'>43</a>
109
+ <a name='L44'></a><a href='#L44'>44</a>
110
+ <a name='L45'></a><a href='#L45'>45</a>
111
+ <a name='L46'></a><a href='#L46'>46</a>
112
+ <a name='L47'></a><a href='#L47'>47</a>
113
+ <a name='L48'></a><a href='#L48'>48</a>
114
+ <a name='L49'></a><a href='#L49'>49</a>
115
+ <a name='L50'></a><a href='#L50'>50</a>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a>
141
+ <a name='L76'></a><a href='#L76'>76</a>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a>
151
+ <a name='L86'></a><a href='#L86'>86</a>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a>
155
+ <a name='L90'></a><a href='#L90'>90</a>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a>
163
+ <a name='L98'></a><a href='#L98'>98</a>
164
+ <a name='L99'></a><a href='#L99'>99</a>
165
+ <a name='L100'></a><a href='#L100'>100</a>
166
+ <a name='L101'></a><a href='#L101'>101</a>
167
+ <a name='L102'></a><a href='#L102'>102</a>
168
+ <a name='L103'></a><a href='#L103'>103</a>
169
+ <a name='L104'></a><a href='#L104'>104</a>
170
+ <a name='L105'></a><a href='#L105'>105</a>
171
+ <a name='L106'></a><a href='#L106'>106</a>
172
+ <a name='L107'></a><a href='#L107'>107</a>
173
+ <a name='L108'></a><a href='#L108'>108</a>
174
+ <a name='L109'></a><a href='#L109'>109</a>
175
+ <a name='L110'></a><a href='#L110'>110</a>
176
+ <a name='L111'></a><a href='#L111'>111</a>
177
+ <a name='L112'></a><a href='#L112'>112</a>
178
+ <a name='L113'></a><a href='#L113'>113</a>
179
+ <a name='L114'></a><a href='#L114'>114</a>
180
+ <a name='L115'></a><a href='#L115'>115</a>
181
+ <a name='L116'></a><a href='#L116'>116</a>
182
+ <a name='L117'></a><a href='#L117'>117</a>
183
+ <a name='L118'></a><a href='#L118'>118</a>
184
+ <a name='L119'></a><a href='#L119'>119</a>
185
+ <a name='L120'></a><a href='#L120'>120</a>
186
+ <a name='L121'></a><a href='#L121'>121</a>
187
+ <a name='L122'></a><a href='#L122'>122</a>
188
+ <a name='L123'></a><a href='#L123'>123</a>
189
+ <a name='L124'></a><a href='#L124'>124</a>
190
+ <a name='L125'></a><a href='#L125'>125</a>
191
+ <a name='L126'></a><a href='#L126'>126</a>
192
+ <a name='L127'></a><a href='#L127'>127</a>
193
+ <a name='L128'></a><a href='#L128'>128</a>
194
+ <a name='L129'></a><a href='#L129'>129</a>
195
+ <a name='L130'></a><a href='#L130'>130</a>
196
+ <a name='L131'></a><a href='#L131'>131</a>
197
+ <a name='L132'></a><a href='#L132'>132</a>
198
+ <a name='L133'></a><a href='#L133'>133</a>
199
+ <a name='L134'></a><a href='#L134'>134</a>
200
+ <a name='L135'></a><a href='#L135'>135</a>
201
+ <a name='L136'></a><a href='#L136'>136</a>
202
+ <a name='L137'></a><a href='#L137'>137</a>
203
+ <a name='L138'></a><a href='#L138'>138</a>
204
+ <a name='L139'></a><a href='#L139'>139</a>
205
+ <a name='L140'></a><a href='#L140'>140</a>
206
+ <a name='L141'></a><a href='#L141'>141</a>
207
+ <a name='L142'></a><a href='#L142'>142</a>
208
+ <a name='L143'></a><a href='#L143'>143</a>
209
+ <a name='L144'></a><a href='#L144'>144</a>
210
+ <a name='L145'></a><a href='#L145'>145</a>
211
+ <a name='L146'></a><a href='#L146'>146</a>
212
+ <a name='L147'></a><a href='#L147'>147</a>
213
+ <a name='L148'></a><a href='#L148'>148</a>
214
+ <a name='L149'></a><a href='#L149'>149</a>
215
+ <a name='L150'></a><a href='#L150'>150</a>
216
+ <a name='L151'></a><a href='#L151'>151</a>
217
+ <a name='L152'></a><a href='#L152'>152</a>
218
+ <a name='L153'></a><a href='#L153'>153</a>
219
+ <a name='L154'></a><a href='#L154'>154</a>
220
+ <a name='L155'></a><a href='#L155'>155</a>
221
+ <a name='L156'></a><a href='#L156'>156</a>
222
+ <a name='L157'></a><a href='#L157'>157</a>
223
+ <a name='L158'></a><a href='#L158'>158</a>
224
+ <a name='L159'></a><a href='#L159'>159</a>
225
+ <a name='L160'></a><a href='#L160'>160</a>
226
+ <a name='L161'></a><a href='#L161'>161</a>
227
+ <a name='L162'></a><a href='#L162'>162</a>
228
+ <a name='L163'></a><a href='#L163'>163</a>
229
+ <a name='L164'></a><a href='#L164'>164</a>
230
+ <a name='L165'></a><a href='#L165'>165</a>
231
+ <a name='L166'></a><a href='#L166'>166</a>
232
+ <a name='L167'></a><a href='#L167'>167</a>
233
+ <a name='L168'></a><a href='#L168'>168</a>
234
+ <a name='L169'></a><a href='#L169'>169</a>
235
+ <a name='L170'></a><a href='#L170'>170</a>
236
+ <a name='L171'></a><a href='#L171'>171</a>
237
+ <a name='L172'></a><a href='#L172'>172</a>
238
+ <a name='L173'></a><a href='#L173'>173</a>
239
+ <a name='L174'></a><a href='#L174'>174</a>
240
+ <a name='L175'></a><a href='#L175'>175</a>
241
+ <a name='L176'></a><a href='#L176'>176</a>
242
+ <a name='L177'></a><a href='#L177'>177</a>
243
+ <a name='L178'></a><a href='#L178'>178</a>
244
+ <a name='L179'></a><a href='#L179'>179</a>
245
+ <a name='L180'></a><a href='#L180'>180</a>
246
+ <a name='L181'></a><a href='#L181'>181</a>
247
+ <a name='L182'></a><a href='#L182'>182</a>
248
+ <a name='L183'></a><a href='#L183'>183</a>
249
+ <a name='L184'></a><a href='#L184'>184</a>
250
+ <a name='L185'></a><a href='#L185'>185</a>
251
+ <a name='L186'></a><a href='#L186'>186</a>
252
+ <a name='L187'></a><a href='#L187'>187</a>
253
+ <a name='L188'></a><a href='#L188'>188</a>
254
+ <a name='L189'></a><a href='#L189'>189</a>
255
+ <a name='L190'></a><a href='#L190'>190</a>
256
+ <a name='L191'></a><a href='#L191'>191</a>
257
+ <a name='L192'></a><a href='#L192'>192</a>
258
+ <a name='L193'></a><a href='#L193'>193</a>
259
+ <a name='L194'></a><a href='#L194'>194</a>
260
+ <a name='L195'></a><a href='#L195'>195</a>
261
+ <a name='L196'></a><a href='#L196'>196</a>
262
+ <a name='L197'></a><a href='#L197'>197</a>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a>
274
+ <a name='L209'></a><a href='#L209'>209</a>
275
+ <a name='L210'></a><a href='#L210'>210</a>
276
+ <a name='L211'></a><a href='#L211'>211</a>
277
+ <a name='L212'></a><a href='#L212'>212</a>
278
+ <a name='L213'></a><a href='#L213'>213</a>
279
+ <a name='L214'></a><a href='#L214'>214</a>
280
+ <a name='L215'></a><a href='#L215'>215</a>
281
+ <a name='L216'></a><a href='#L216'>216</a>
282
+ <a name='L217'></a><a href='#L217'>217</a>
283
+ <a name='L218'></a><a href='#L218'>218</a>
284
+ <a name='L219'></a><a href='#L219'>219</a>
285
+ <a name='L220'></a><a href='#L220'>220</a>
286
+ <a name='L221'></a><a href='#L221'>221</a>
287
+ <a name='L222'></a><a href='#L222'>222</a>
288
+ <a name='L223'></a><a href='#L223'>223</a>
289
+ <a name='L224'></a><a href='#L224'>224</a>
290
+ <a name='L225'></a><a href='#L225'>225</a>
291
+ <a name='L226'></a><a href='#L226'>226</a>
292
+ <a name='L227'></a><a href='#L227'>227</a>
293
+ <a name='L228'></a><a href='#L228'>228</a>
294
+ <a name='L229'></a><a href='#L229'>229</a>
295
+ <a name='L230'></a><a href='#L230'>230</a>
296
+ <a name='L231'></a><a href='#L231'>231</a>
297
+ <a name='L232'></a><a href='#L232'>232</a>
298
+ <a name='L233'></a><a href='#L233'>233</a>
299
+ <a name='L234'></a><a href='#L234'>234</a>
300
+ <a name='L235'></a><a href='#L235'>235</a>
301
+ <a name='L236'></a><a href='#L236'>236</a>
302
+ <a name='L237'></a><a href='#L237'>237</a>
303
+ <a name='L238'></a><a href='#L238'>238</a>
304
+ <a name='L239'></a><a href='#L239'>239</a>
305
+ <a name='L240'></a><a href='#L240'>240</a>
306
+ <a name='L241'></a><a href='#L241'>241</a>
307
+ <a name='L242'></a><a href='#L242'>242</a>
308
+ <a name='L243'></a><a href='#L243'>243</a>
309
+ <a name='L244'></a><a href='#L244'>244</a>
310
+ <a name='L245'></a><a href='#L245'>245</a>
311
+ <a name='L246'></a><a href='#L246'>246</a>
312
+ <a name='L247'></a><a href='#L247'>247</a>
313
+ <a name='L248'></a><a href='#L248'>248</a>
314
+ <a name='L249'></a><a href='#L249'>249</a>
315
+ <a name='L250'></a><a href='#L250'>250</a>
316
+ <a name='L251'></a><a href='#L251'>251</a>
317
+ <a name='L252'></a><a href='#L252'>252</a>
318
+ <a name='L253'></a><a href='#L253'>253</a>
319
+ <a name='L254'></a><a href='#L254'>254</a>
320
+ <a name='L255'></a><a href='#L255'>255</a>
321
+ <a name='L256'></a><a href='#L256'>256</a>
322
+ <a name='L257'></a><a href='#L257'>257</a>
323
+ <a name='L258'></a><a href='#L258'>258</a>
324
+ <a name='L259'></a><a href='#L259'>259</a>
325
+ <a name='L260'></a><a href='#L260'>260</a>
326
+ <a name='L261'></a><a href='#L261'>261</a>
327
+ <a name='L262'></a><a href='#L262'>262</a>
328
+ <a name='L263'></a><a href='#L263'>263</a>
329
+ <a name='L264'></a><a href='#L264'>264</a>
330
+ <a name='L265'></a><a href='#L265'>265</a>
331
+ <a name='L266'></a><a href='#L266'>266</a>
332
+ <a name='L267'></a><a href='#L267'>267</a>
333
+ <a name='L268'></a><a href='#L268'>268</a>
334
+ <a name='L269'></a><a href='#L269'>269</a>
335
+ <a name='L270'></a><a href='#L270'>270</a>
336
+ <a name='L271'></a><a href='#L271'>271</a>
337
+ <a name='L272'></a><a href='#L272'>272</a>
338
+ <a name='L273'></a><a href='#L273'>273</a>
339
+ <a name='L274'></a><a href='#L274'>274</a>
340
+ <a name='L275'></a><a href='#L275'>275</a>
341
+ <a name='L276'></a><a href='#L276'>276</a>
342
+ <a name='L277'></a><a href='#L277'>277</a>
343
+ <a name='L278'></a><a href='#L278'>278</a>
344
+ <a name='L279'></a><a href='#L279'>279</a>
345
+ <a name='L280'></a><a href='#L280'>280</a>
346
+ <a name='L281'></a><a href='#L281'>281</a>
347
+ <a name='L282'></a><a href='#L282'>282</a>
348
+ <a name='L283'></a><a href='#L283'>283</a>
349
+ <a name='L284'></a><a href='#L284'>284</a>
350
+ <a name='L285'></a><a href='#L285'>285</a>
351
+ <a name='L286'></a><a href='#L286'>286</a>
352
+ <a name='L287'></a><a href='#L287'>287</a>
353
+ <a name='L288'></a><a href='#L288'>288</a>
354
+ <a name='L289'></a><a href='#L289'>289</a>
355
+ <a name='L290'></a><a href='#L290'>290</a>
356
+ <a name='L291'></a><a href='#L291'>291</a>
357
+ <a name='L292'></a><a href='#L292'>292</a>
358
+ <a name='L293'></a><a href='#L293'>293</a>
359
+ <a name='L294'></a><a href='#L294'>294</a>
360
+ <a name='L295'></a><a href='#L295'>295</a>
361
+ <a name='L296'></a><a href='#L296'>296</a>
362
+ <a name='L297'></a><a href='#L297'>297</a>
363
+ <a name='L298'></a><a href='#L298'>298</a>
364
+ <a name='L299'></a><a href='#L299'>299</a>
365
+ <a name='L300'></a><a href='#L300'>300</a>
366
+ <a name='L301'></a><a href='#L301'>301</a>
367
+ <a name='L302'></a><a href='#L302'>302</a>
368
+ <a name='L303'></a><a href='#L303'>303</a>
369
+ <a name='L304'></a><a href='#L304'>304</a>
370
+ <a name='L305'></a><a href='#L305'>305</a>
371
+ <a name='L306'></a><a href='#L306'>306</a>
372
+ <a name='L307'></a><a href='#L307'>307</a>
373
+ <a name='L308'></a><a href='#L308'>308</a>
374
+ <a name='L309'></a><a href='#L309'>309</a>
375
+ <a name='L310'></a><a href='#L310'>310</a>
376
+ <a name='L311'></a><a href='#L311'>311</a>
377
+ <a name='L312'></a><a href='#L312'>312</a>
378
+ <a name='L313'></a><a href='#L313'>313</a>
379
+ <a name='L314'></a><a href='#L314'>314</a>
380
+ <a name='L315'></a><a href='#L315'>315</a>
381
+ <a name='L316'></a><a href='#L316'>316</a>
382
+ <a name='L317'></a><a href='#L317'>317</a>
383
+ <a name='L318'></a><a href='#L318'>318</a>
384
+ <a name='L319'></a><a href='#L319'>319</a>
385
+ <a name='L320'></a><a href='#L320'>320</a>
386
+ <a name='L321'></a><a href='#L321'>321</a>
387
+ <a name='L322'></a><a href='#L322'>322</a>
388
+ <a name='L323'></a><a href='#L323'>323</a>
389
+ <a name='L324'></a><a href='#L324'>324</a>
390
+ <a name='L325'></a><a href='#L325'>325</a>
391
+ <a name='L326'></a><a href='#L326'>326</a>
392
+ <a name='L327'></a><a href='#L327'>327</a>
393
+ <a name='L328'></a><a href='#L328'>328</a>
394
+ <a name='L329'></a><a href='#L329'>329</a>
395
+ <a name='L330'></a><a href='#L330'>330</a>
396
+ <a name='L331'></a><a href='#L331'>331</a>
397
+ <a name='L332'></a><a href='#L332'>332</a>
398
+ <a name='L333'></a><a href='#L333'>333</a>
399
+ <a name='L334'></a><a href='#L334'>334</a>
400
+ <a name='L335'></a><a href='#L335'>335</a>
401
+ <a name='L336'></a><a href='#L336'>336</a>
402
+ <a name='L337'></a><a href='#L337'>337</a>
403
+ <a name='L338'></a><a href='#L338'>338</a>
404
+ <a name='L339'></a><a href='#L339'>339</a>
405
+ <a name='L340'></a><a href='#L340'>340</a>
406
+ <a name='L341'></a><a href='#L341'>341</a>
407
+ <a name='L342'></a><a href='#L342'>342</a>
408
+ <a name='L343'></a><a href='#L343'>343</a>
409
+ <a name='L344'></a><a href='#L344'>344</a>
410
+ <a name='L345'></a><a href='#L345'>345</a>
411
+ <a name='L346'></a><a href='#L346'>346</a>
412
+ <a name='L347'></a><a href='#L347'>347</a>
413
+ <a name='L348'></a><a href='#L348'>348</a>
414
+ <a name='L349'></a><a href='#L349'>349</a>
415
+ <a name='L350'></a><a href='#L350'>350</a>
416
+ <a name='L351'></a><a href='#L351'>351</a>
417
+ <a name='L352'></a><a href='#L352'>352</a>
418
+ <a name='L353'></a><a href='#L353'>353</a>
419
+ <a name='L354'></a><a href='#L354'>354</a>
420
+ <a name='L355'></a><a href='#L355'>355</a>
421
+ <a name='L356'></a><a href='#L356'>356</a>
422
+ <a name='L357'></a><a href='#L357'>357</a>
423
+ <a name='L358'></a><a href='#L358'>358</a>
424
+ <a name='L359'></a><a href='#L359'>359</a>
425
+ <a name='L360'></a><a href='#L360'>360</a>
426
+ <a name='L361'></a><a href='#L361'>361</a>
427
+ <a name='L362'></a><a href='#L362'>362</a>
428
+ <a name='L363'></a><a href='#L363'>363</a>
429
+ <a name='L364'></a><a href='#L364'>364</a>
430
+ <a name='L365'></a><a href='#L365'>365</a>
431
+ <a name='L366'></a><a href='#L366'>366</a>
432
+ <a name='L367'></a><a href='#L367'>367</a>
433
+ <a name='L368'></a><a href='#L368'>368</a>
434
+ <a name='L369'></a><a href='#L369'>369</a>
435
+ <a name='L370'></a><a href='#L370'>370</a>
436
+ <a name='L371'></a><a href='#L371'>371</a>
437
+ <a name='L372'></a><a href='#L372'>372</a>
438
+ <a name='L373'></a><a href='#L373'>373</a>
439
+ <a name='L374'></a><a href='#L374'>374</a>
440
+ <a name='L375'></a><a href='#L375'>375</a>
441
+ <a name='L376'></a><a href='#L376'>376</a>
442
+ <a name='L377'></a><a href='#L377'>377</a>
443
+ <a name='L378'></a><a href='#L378'>378</a>
444
+ <a name='L379'></a><a href='#L379'>379</a>
445
+ <a name='L380'></a><a href='#L380'>380</a>
446
+ <a name='L381'></a><a href='#L381'>381</a>
447
+ <a name='L382'></a><a href='#L382'>382</a>
448
+ <a name='L383'></a><a href='#L383'>383</a>
449
+ <a name='L384'></a><a href='#L384'>384</a>
450
+ <a name='L385'></a><a href='#L385'>385</a>
451
+ <a name='L386'></a><a href='#L386'>386</a>
452
+ <a name='L387'></a><a href='#L387'>387</a>
453
+ <a name='L388'></a><a href='#L388'>388</a>
454
+ <a name='L389'></a><a href='#L389'>389</a>
455
+ <a name='L390'></a><a href='#L390'>390</a>
456
+ <a name='L391'></a><a href='#L391'>391</a>
457
+ <a name='L392'></a><a href='#L392'>392</a>
458
+ <a name='L393'></a><a href='#L393'>393</a>
459
+ <a name='L394'></a><a href='#L394'>394</a>
460
+ <a name='L395'></a><a href='#L395'>395</a>
461
+ <a name='L396'></a><a href='#L396'>396</a>
462
+ <a name='L397'></a><a href='#L397'>397</a>
463
+ <a name='L398'></a><a href='#L398'>398</a>
464
+ <a name='L399'></a><a href='#L399'>399</a>
465
+ <a name='L400'></a><a href='#L400'>400</a>
466
+ <a name='L401'></a><a href='#L401'>401</a>
467
+ <a name='L402'></a><a href='#L402'>402</a>
468
+ <a name='L403'></a><a href='#L403'>403</a>
469
+ <a name='L404'></a><a href='#L404'>404</a>
470
+ <a name='L405'></a><a href='#L405'>405</a>
471
+ <a name='L406'></a><a href='#L406'>406</a>
472
+ <a name='L407'></a><a href='#L407'>407</a>
473
+ <a name='L408'></a><a href='#L408'>408</a>
474
+ <a name='L409'></a><a href='#L409'>409</a>
475
+ <a name='L410'></a><a href='#L410'>410</a>
476
+ <a name='L411'></a><a href='#L411'>411</a>
477
+ <a name='L412'></a><a href='#L412'>412</a>
478
+ <a name='L413'></a><a href='#L413'>413</a>
479
+ <a name='L414'></a><a href='#L414'>414</a>
480
+ <a name='L415'></a><a href='#L415'>415</a>
481
+ <a name='L416'></a><a href='#L416'>416</a>
482
+ <a name='L417'></a><a href='#L417'>417</a>
483
+ <a name='L418'></a><a href='#L418'>418</a>
484
+ <a name='L419'></a><a href='#L419'>419</a>
485
+ <a name='L420'></a><a href='#L420'>420</a>
486
+ <a name='L421'></a><a href='#L421'>421</a>
487
+ <a name='L422'></a><a href='#L422'>422</a>
488
+ <a name='L423'></a><a href='#L423'>423</a>
489
+ <a name='L424'></a><a href='#L424'>424</a>
490
+ <a name='L425'></a><a href='#L425'>425</a>
491
+ <a name='L426'></a><a href='#L426'>426</a>
492
+ <a name='L427'></a><a href='#L427'>427</a>
493
+ <a name='L428'></a><a href='#L428'>428</a>
494
+ <a name='L429'></a><a href='#L429'>429</a>
495
+ <a name='L430'></a><a href='#L430'>430</a>
496
+ <a name='L431'></a><a href='#L431'>431</a>
497
+ <a name='L432'></a><a href='#L432'>432</a>
498
+ <a name='L433'></a><a href='#L433'>433</a>
499
+ <a name='L434'></a><a href='#L434'>434</a>
500
+ <a name='L435'></a><a href='#L435'>435</a>
501
+ <a name='L436'></a><a href='#L436'>436</a>
502
+ <a name='L437'></a><a href='#L437'>437</a>
503
+ <a name='L438'></a><a href='#L438'>438</a>
504
+ <a name='L439'></a><a href='#L439'>439</a>
505
+ <a name='L440'></a><a href='#L440'>440</a>
506
+ <a name='L441'></a><a href='#L441'>441</a>
507
+ <a name='L442'></a><a href='#L442'>442</a>
508
+ <a name='L443'></a><a href='#L443'>443</a>
509
+ <a name='L444'></a><a href='#L444'>444</a>
510
+ <a name='L445'></a><a href='#L445'>445</a>
511
+ <a name='L446'></a><a href='#L446'>446</a>
512
+ <a name='L447'></a><a href='#L447'>447</a>
513
+ <a name='L448'></a><a href='#L448'>448</a>
514
+ <a name='L449'></a><a href='#L449'>449</a>
515
+ <a name='L450'></a><a href='#L450'>450</a>
516
+ <a name='L451'></a><a href='#L451'>451</a>
517
+ <a name='L452'></a><a href='#L452'>452</a>
518
+ <a name='L453'></a><a href='#L453'>453</a>
519
+ <a name='L454'></a><a href='#L454'>454</a>
520
+ <a name='L455'></a><a href='#L455'>455</a>
521
+ <a name='L456'></a><a href='#L456'>456</a>
522
+ <a name='L457'></a><a href='#L457'>457</a>
523
+ <a name='L458'></a><a href='#L458'>458</a>
524
+ <a name='L459'></a><a href='#L459'>459</a>
525
+ <a name='L460'></a><a href='#L460'>460</a>
526
+ <a name='L461'></a><a href='#L461'>461</a>
527
+ <a name='L462'></a><a href='#L462'>462</a>
528
+ <a name='L463'></a><a href='#L463'>463</a>
529
+ <a name='L464'></a><a href='#L464'>464</a>
530
+ <a name='L465'></a><a href='#L465'>465</a>
531
+ <a name='L466'></a><a href='#L466'>466</a>
532
+ <a name='L467'></a><a href='#L467'>467</a>
533
+ <a name='L468'></a><a href='#L468'>468</a>
534
+ <a name='L469'></a><a href='#L469'>469</a>
535
+ <a name='L470'></a><a href='#L470'>470</a>
536
+ <a name='L471'></a><a href='#L471'>471</a>
537
+ <a name='L472'></a><a href='#L472'>472</a>
538
+ <a name='L473'></a><a href='#L473'>473</a>
539
+ <a name='L474'></a><a href='#L474'>474</a>
540
+ <a name='L475'></a><a href='#L475'>475</a>
541
+ <a name='L476'></a><a href='#L476'>476</a>
542
+ <a name='L477'></a><a href='#L477'>477</a>
543
+ <a name='L478'></a><a href='#L478'>478</a>
544
+ <a name='L479'></a><a href='#L479'>479</a>
545
+ <a name='L480'></a><a href='#L480'>480</a>
546
+ <a name='L481'></a><a href='#L481'>481</a>
547
+ <a name='L482'></a><a href='#L482'>482</a>
548
+ <a name='L483'></a><a href='#L483'>483</a>
549
+ <a name='L484'></a><a href='#L484'>484</a>
550
+ <a name='L485'></a><a href='#L485'>485</a>
551
+ <a name='L486'></a><a href='#L486'>486</a>
552
+ <a name='L487'></a><a href='#L487'>487</a>
553
+ <a name='L488'></a><a href='#L488'>488</a>
554
+ <a name='L489'></a><a href='#L489'>489</a>
555
+ <a name='L490'></a><a href='#L490'>490</a>
556
+ <a name='L491'></a><a href='#L491'>491</a>
557
+ <a name='L492'></a><a href='#L492'>492</a>
558
+ <a name='L493'></a><a href='#L493'>493</a>
559
+ <a name='L494'></a><a href='#L494'>494</a>
560
+ <a name='L495'></a><a href='#L495'>495</a>
561
+ <a name='L496'></a><a href='#L496'>496</a>
562
+ <a name='L497'></a><a href='#L497'>497</a>
563
+ <a name='L498'></a><a href='#L498'>498</a>
564
+ <a name='L499'></a><a href='#L499'>499</a>
565
+ <a name='L500'></a><a href='#L500'>500</a>
566
+ <a name='L501'></a><a href='#L501'>501</a>
567
+ <a name='L502'></a><a href='#L502'>502</a>
568
+ <a name='L503'></a><a href='#L503'>503</a>
569
+ <a name='L504'></a><a href='#L504'>504</a>
570
+ <a name='L505'></a><a href='#L505'>505</a>
571
+ <a name='L506'></a><a href='#L506'>506</a>
572
+ <a name='L507'></a><a href='#L507'>507</a>
573
+ <a name='L508'></a><a href='#L508'>508</a>
574
+ <a name='L509'></a><a href='#L509'>509</a>
575
+ <a name='L510'></a><a href='#L510'>510</a>
576
+ <a name='L511'></a><a href='#L511'>511</a>
577
+ <a name='L512'></a><a href='#L512'>512</a>
578
+ <a name='L513'></a><a href='#L513'>513</a>
579
+ <a name='L514'></a><a href='#L514'>514</a>
580
+ <a name='L515'></a><a href='#L515'>515</a>
581
+ <a name='L516'></a><a href='#L516'>516</a>
582
+ <a name='L517'></a><a href='#L517'>517</a>
583
+ <a name='L518'></a><a href='#L518'>518</a>
584
+ <a name='L519'></a><a href='#L519'>519</a>
585
+ <a name='L520'></a><a href='#L520'>520</a>
586
+ <a name='L521'></a><a href='#L521'>521</a>
587
+ <a name='L522'></a><a href='#L522'>522</a>
588
+ <a name='L523'></a><a href='#L523'>523</a>
589
+ <a name='L524'></a><a href='#L524'>524</a>
590
+ <a name='L525'></a><a href='#L525'>525</a>
591
+ <a name='L526'></a><a href='#L526'>526</a>
592
+ <a name='L527'></a><a href='#L527'>527</a>
593
+ <a name='L528'></a><a href='#L528'>528</a>
594
+ <a name='L529'></a><a href='#L529'>529</a>
595
+ <a name='L530'></a><a href='#L530'>530</a>
596
+ <a name='L531'></a><a href='#L531'>531</a>
597
+ <a name='L532'></a><a href='#L532'>532</a>
598
+ <a name='L533'></a><a href='#L533'>533</a>
599
+ <a name='L534'></a><a href='#L534'>534</a>
600
+ <a name='L535'></a><a href='#L535'>535</a>
601
+ <a name='L536'></a><a href='#L536'>536</a>
602
+ <a name='L537'></a><a href='#L537'>537</a>
603
+ <a name='L538'></a><a href='#L538'>538</a>
604
+ <a name='L539'></a><a href='#L539'>539</a>
605
+ <a name='L540'></a><a href='#L540'>540</a>
606
+ <a name='L541'></a><a href='#L541'>541</a>
607
+ <a name='L542'></a><a href='#L542'>542</a>
608
+ <a name='L543'></a><a href='#L543'>543</a>
609
+ <a name='L544'></a><a href='#L544'>544</a>
610
+ <a name='L545'></a><a href='#L545'>545</a>
611
+ <a name='L546'></a><a href='#L546'>546</a>
612
+ <a name='L547'></a><a href='#L547'>547</a>
613
+ <a name='L548'></a><a href='#L548'>548</a>
614
+ <a name='L549'></a><a href='#L549'>549</a>
615
+ <a name='L550'></a><a href='#L550'>550</a>
616
+ <a name='L551'></a><a href='#L551'>551</a>
617
+ <a name='L552'></a><a href='#L552'>552</a>
618
+ <a name='L553'></a><a href='#L553'>553</a>
619
+ <a name='L554'></a><a href='#L554'>554</a>
620
+ <a name='L555'></a><a href='#L555'>555</a>
621
+ <a name='L556'></a><a href='#L556'>556</a>
622
+ <a name='L557'></a><a href='#L557'>557</a>
623
+ <a name='L558'></a><a href='#L558'>558</a>
624
+ <a name='L559'></a><a href='#L559'>559</a>
625
+ <a name='L560'></a><a href='#L560'>560</a>
626
+ <a name='L561'></a><a href='#L561'>561</a>
627
+ <a name='L562'></a><a href='#L562'>562</a>
628
+ <a name='L563'></a><a href='#L563'>563</a>
629
+ <a name='L564'></a><a href='#L564'>564</a>
630
+ <a name='L565'></a><a href='#L565'>565</a>
631
+ <a name='L566'></a><a href='#L566'>566</a>
632
+ <a name='L567'></a><a href='#L567'>567</a>
633
+ <a name='L568'></a><a href='#L568'>568</a>
634
+ <a name='L569'></a><a href='#L569'>569</a>
635
+ <a name='L570'></a><a href='#L570'>570</a>
636
+ <a name='L571'></a><a href='#L571'>571</a>
637
+ <a name='L572'></a><a href='#L572'>572</a>
638
+ <a name='L573'></a><a href='#L573'>573</a>
639
+ <a name='L574'></a><a href='#L574'>574</a>
640
+ <a name='L575'></a><a href='#L575'>575</a>
641
+ <a name='L576'></a><a href='#L576'>576</a>
642
+ <a name='L577'></a><a href='#L577'>577</a>
643
+ <a name='L578'></a><a href='#L578'>578</a>
644
+ <a name='L579'></a><a href='#L579'>579</a>
645
+ <a name='L580'></a><a href='#L580'>580</a>
646
+ <a name='L581'></a><a href='#L581'>581</a>
647
+ <a name='L582'></a><a href='#L582'>582</a>
648
+ <a name='L583'></a><a href='#L583'>583</a>
649
+ <a name='L584'></a><a href='#L584'>584</a>
650
+ <a name='L585'></a><a href='#L585'>585</a>
651
+ <a name='L586'></a><a href='#L586'>586</a>
652
+ <a name='L587'></a><a href='#L587'>587</a>
653
+ <a name='L588'></a><a href='#L588'>588</a>
654
+ <a name='L589'></a><a href='#L589'>589</a>
655
+ <a name='L590'></a><a href='#L590'>590</a>
656
+ <a name='L591'></a><a href='#L591'>591</a>
657
+ <a name='L592'></a><a href='#L592'>592</a>
658
+ <a name='L593'></a><a href='#L593'>593</a>
659
+ <a name='L594'></a><a href='#L594'>594</a>
660
+ <a name='L595'></a><a href='#L595'>595</a>
661
+ <a name='L596'></a><a href='#L596'>596</a>
662
+ <a name='L597'></a><a href='#L597'>597</a>
663
+ <a name='L598'></a><a href='#L598'>598</a>
664
+ <a name='L599'></a><a href='#L599'>599</a>
665
+ <a name='L600'></a><a href='#L600'>600</a>
666
+ <a name='L601'></a><a href='#L601'>601</a>
667
+ <a name='L602'></a><a href='#L602'>602</a>
668
+ <a name='L603'></a><a href='#L603'>603</a>
669
+ <a name='L604'></a><a href='#L604'>604</a>
670
+ <a name='L605'></a><a href='#L605'>605</a>
671
+ <a name='L606'></a><a href='#L606'>606</a>
672
+ <a name='L607'></a><a href='#L607'>607</a>
673
+ <a name='L608'></a><a href='#L608'>608</a>
674
+ <a name='L609'></a><a href='#L609'>609</a>
675
+ <a name='L610'></a><a href='#L610'>610</a>
676
+ <a name='L611'></a><a href='#L611'>611</a>
677
+ <a name='L612'></a><a href='#L612'>612</a>
678
+ <a name='L613'></a><a href='#L613'>613</a>
679
+ <a name='L614'></a><a href='#L614'>614</a>
680
+ <a name='L615'></a><a href='#L615'>615</a>
681
+ <a name='L616'></a><a href='#L616'>616</a>
682
+ <a name='L617'></a><a href='#L617'>617</a>
683
+ <a name='L618'></a><a href='#L618'>618</a>
684
+ <a name='L619'></a><a href='#L619'>619</a>
685
+ <a name='L620'></a><a href='#L620'>620</a>
686
+ <a name='L621'></a><a href='#L621'>621</a>
687
+ <a name='L622'></a><a href='#L622'>622</a>
688
+ <a name='L623'></a><a href='#L623'>623</a>
689
+ <a name='L624'></a><a href='#L624'>624</a>
690
+ <a name='L625'></a><a href='#L625'>625</a>
691
+ <a name='L626'></a><a href='#L626'>626</a>
692
+ <a name='L627'></a><a href='#L627'>627</a>
693
+ <a name='L628'></a><a href='#L628'>628</a>
694
+ <a name='L629'></a><a href='#L629'>629</a>
695
+ <a name='L630'></a><a href='#L630'>630</a>
696
+ <a name='L631'></a><a href='#L631'>631</a>
697
+ <a name='L632'></a><a href='#L632'>632</a>
698
+ <a name='L633'></a><a href='#L633'>633</a>
699
+ <a name='L634'></a><a href='#L634'>634</a>
700
+ <a name='L635'></a><a href='#L635'>635</a>
701
+ <a name='L636'></a><a href='#L636'>636</a>
702
+ <a name='L637'></a><a href='#L637'>637</a>
703
+ <a name='L638'></a><a href='#L638'>638</a>
704
+ <a name='L639'></a><a href='#L639'>639</a>
705
+ <a name='L640'></a><a href='#L640'>640</a>
706
+ <a name='L641'></a><a href='#L641'>641</a>
707
+ <a name='L642'></a><a href='#L642'>642</a>
708
+ <a name='L643'></a><a href='#L643'>643</a>
709
+ <a name='L644'></a><a href='#L644'>644</a>
710
+ <a name='L645'></a><a href='#L645'>645</a>
711
+ <a name='L646'></a><a href='#L646'>646</a>
712
+ <a name='L647'></a><a href='#L647'>647</a>
713
+ <a name='L648'></a><a href='#L648'>648</a>
714
+ <a name='L649'></a><a href='#L649'>649</a>
715
+ <a name='L650'></a><a href='#L650'>650</a>
716
+ <a name='L651'></a><a href='#L651'>651</a>
717
+ <a name='L652'></a><a href='#L652'>652</a>
718
+ <a name='L653'></a><a href='#L653'>653</a>
719
+ <a name='L654'></a><a href='#L654'>654</a>
720
+ <a name='L655'></a><a href='#L655'>655</a>
721
+ <a name='L656'></a><a href='#L656'>656</a>
722
+ <a name='L657'></a><a href='#L657'>657</a>
723
+ <a name='L658'></a><a href='#L658'>658</a>
724
+ <a name='L659'></a><a href='#L659'>659</a>
725
+ <a name='L660'></a><a href='#L660'>660</a>
726
+ <a name='L661'></a><a href='#L661'>661</a>
727
+ <a name='L662'></a><a href='#L662'>662</a>
728
+ <a name='L663'></a><a href='#L663'>663</a>
729
+ <a name='L664'></a><a href='#L664'>664</a>
730
+ <a name='L665'></a><a href='#L665'>665</a>
731
+ <a name='L666'></a><a href='#L666'>666</a>
732
+ <a name='L667'></a><a href='#L667'>667</a>
733
+ <a name='L668'></a><a href='#L668'>668</a>
734
+ <a name='L669'></a><a href='#L669'>669</a>
735
+ <a name='L670'></a><a href='#L670'>670</a>
736
+ <a name='L671'></a><a href='#L671'>671</a>
737
+ <a name='L672'></a><a href='#L672'>672</a>
738
+ <a name='L673'></a><a href='#L673'>673</a>
739
+ <a name='L674'></a><a href='#L674'>674</a>
740
+ <a name='L675'></a><a href='#L675'>675</a>
741
+ <a name='L676'></a><a href='#L676'>676</a>
742
+ <a name='L677'></a><a href='#L677'>677</a>
743
+ <a name='L678'></a><a href='#L678'>678</a>
744
+ <a name='L679'></a><a href='#L679'>679</a>
745
+ <a name='L680'></a><a href='#L680'>680</a>
746
+ <a name='L681'></a><a href='#L681'>681</a>
747
+ <a name='L682'></a><a href='#L682'>682</a>
748
+ <a name='L683'></a><a href='#L683'>683</a>
749
+ <a name='L684'></a><a href='#L684'>684</a>
750
+ <a name='L685'></a><a href='#L685'>685</a>
751
+ <a name='L686'></a><a href='#L686'>686</a>
752
+ <a name='L687'></a><a href='#L687'>687</a>
753
+ <a name='L688'></a><a href='#L688'>688</a>
754
+ <a name='L689'></a><a href='#L689'>689</a>
755
+ <a name='L690'></a><a href='#L690'>690</a>
756
+ <a name='L691'></a><a href='#L691'>691</a>
757
+ <a name='L692'></a><a href='#L692'>692</a>
758
+ <a name='L693'></a><a href='#L693'>693</a>
759
+ <a name='L694'></a><a href='#L694'>694</a>
760
+ <a name='L695'></a><a href='#L695'>695</a>
761
+ <a name='L696'></a><a href='#L696'>696</a>
762
+ <a name='L697'></a><a href='#L697'>697</a>
763
+ <a name='L698'></a><a href='#L698'>698</a>
764
+ <a name='L699'></a><a href='#L699'>699</a>
765
+ <a name='L700'></a><a href='#L700'>700</a>
766
+ <a name='L701'></a><a href='#L701'>701</a>
767
+ <a name='L702'></a><a href='#L702'>702</a>
768
+ <a name='L703'></a><a href='#L703'>703</a>
769
+ <a name='L704'></a><a href='#L704'>704</a>
770
+ <a name='L705'></a><a href='#L705'>705</a>
771
+ <a name='L706'></a><a href='#L706'>706</a>
772
+ <a name='L707'></a><a href='#L707'>707</a>
773
+ <a name='L708'></a><a href='#L708'>708</a>
774
+ <a name='L709'></a><a href='#L709'>709</a>
775
+ <a name='L710'></a><a href='#L710'>710</a>
776
+ <a name='L711'></a><a href='#L711'>711</a>
777
+ <a name='L712'></a><a href='#L712'>712</a>
778
+ <a name='L713'></a><a href='#L713'>713</a>
779
+ <a name='L714'></a><a href='#L714'>714</a>
780
+ <a name='L715'></a><a href='#L715'>715</a>
781
+ <a name='L716'></a><a href='#L716'>716</a>
782
+ <a name='L717'></a><a href='#L717'>717</a>
783
+ <a name='L718'></a><a href='#L718'>718</a>
784
+ <a name='L719'></a><a href='#L719'>719</a>
785
+ <a name='L720'></a><a href='#L720'>720</a>
786
+ <a name='L721'></a><a href='#L721'>721</a>
787
+ <a name='L722'></a><a href='#L722'>722</a>
788
+ <a name='L723'></a><a href='#L723'>723</a>
789
+ <a name='L724'></a><a href='#L724'>724</a>
790
+ <a name='L725'></a><a href='#L725'>725</a>
791
+ <a name='L726'></a><a href='#L726'>726</a>
792
+ <a name='L727'></a><a href='#L727'>727</a>
793
+ <a name='L728'></a><a href='#L728'>728</a>
794
+ <a name='L729'></a><a href='#L729'>729</a>
795
+ <a name='L730'></a><a href='#L730'>730</a>
796
+ <a name='L731'></a><a href='#L731'>731</a>
797
+ <a name='L732'></a><a href='#L732'>732</a>
798
+ <a name='L733'></a><a href='#L733'>733</a>
799
+ <a name='L734'></a><a href='#L734'>734</a>
800
+ <a name='L735'></a><a href='#L735'>735</a>
801
+ <a name='L736'></a><a href='#L736'>736</a>
802
+ <a name='L737'></a><a href='#L737'>737</a>
803
+ <a name='L738'></a><a href='#L738'>738</a>
804
+ <a name='L739'></a><a href='#L739'>739</a>
805
+ <a name='L740'></a><a href='#L740'>740</a>
806
+ <a name='L741'></a><a href='#L741'>741</a>
807
+ <a name='L742'></a><a href='#L742'>742</a>
808
+ <a name='L743'></a><a href='#L743'>743</a>
809
+ <a name='L744'></a><a href='#L744'>744</a>
810
+ <a name='L745'></a><a href='#L745'>745</a>
811
+ <a name='L746'></a><a href='#L746'>746</a>
812
+ <a name='L747'></a><a href='#L747'>747</a>
813
+ <a name='L748'></a><a href='#L748'>748</a>
814
+ <a name='L749'></a><a href='#L749'>749</a>
815
+ <a name='L750'></a><a href='#L750'>750</a>
816
+ <a name='L751'></a><a href='#L751'>751</a>
817
+ <a name='L752'></a><a href='#L752'>752</a>
818
+ <a name='L753'></a><a href='#L753'>753</a>
819
+ <a name='L754'></a><a href='#L754'>754</a>
820
+ <a name='L755'></a><a href='#L755'>755</a>
821
+ <a name='L756'></a><a href='#L756'>756</a>
822
+ <a name='L757'></a><a href='#L757'>757</a>
823
+ <a name='L758'></a><a href='#L758'>758</a>
824
+ <a name='L759'></a><a href='#L759'>759</a>
825
+ <a name='L760'></a><a href='#L760'>760</a>
826
+ <a name='L761'></a><a href='#L761'>761</a>
827
+ <a name='L762'></a><a href='#L762'>762</a>
828
+ <a name='L763'></a><a href='#L763'>763</a>
829
+ <a name='L764'></a><a href='#L764'>764</a>
830
+ <a name='L765'></a><a href='#L765'>765</a>
831
+ <a name='L766'></a><a href='#L766'>766</a>
832
+ <a name='L767'></a><a href='#L767'>767</a>
833
+ <a name='L768'></a><a href='#L768'>768</a>
834
+ <a name='L769'></a><a href='#L769'>769</a>
835
+ <a name='L770'></a><a href='#L770'>770</a>
836
+ <a name='L771'></a><a href='#L771'>771</a>
837
+ <a name='L772'></a><a href='#L772'>772</a>
838
+ <a name='L773'></a><a href='#L773'>773</a>
839
+ <a name='L774'></a><a href='#L774'>774</a>
840
+ <a name='L775'></a><a href='#L775'>775</a>
841
+ <a name='L776'></a><a href='#L776'>776</a>
842
+ <a name='L777'></a><a href='#L777'>777</a>
843
+ <a name='L778'></a><a href='#L778'>778</a>
844
+ <a name='L779'></a><a href='#L779'>779</a>
845
+ <a name='L780'></a><a href='#L780'>780</a>
846
+ <a name='L781'></a><a href='#L781'>781</a>
847
+ <a name='L782'></a><a href='#L782'>782</a>
848
+ <a name='L783'></a><a href='#L783'>783</a>
849
+ <a name='L784'></a><a href='#L784'>784</a>
850
+ <a name='L785'></a><a href='#L785'>785</a>
851
+ <a name='L786'></a><a href='#L786'>786</a>
852
+ <a name='L787'></a><a href='#L787'>787</a>
853
+ <a name='L788'></a><a href='#L788'>788</a>
854
+ <a name='L789'></a><a href='#L789'>789</a>
855
+ <a name='L790'></a><a href='#L790'>790</a>
856
+ <a name='L791'></a><a href='#L791'>791</a>
857
+ <a name='L792'></a><a href='#L792'>792</a>
858
+ <a name='L793'></a><a href='#L793'>793</a>
859
+ <a name='L794'></a><a href='#L794'>794</a>
860
+ <a name='L795'></a><a href='#L795'>795</a>
861
+ <a name='L796'></a><a href='#L796'>796</a>
862
+ <a name='L797'></a><a href='#L797'>797</a>
863
+ <a name='L798'></a><a href='#L798'>798</a>
864
+ <a name='L799'></a><a href='#L799'>799</a>
865
+ <a name='L800'></a><a href='#L800'>800</a>
866
+ <a name='L801'></a><a href='#L801'>801</a>
867
+ <a name='L802'></a><a href='#L802'>802</a>
868
+ <a name='L803'></a><a href='#L803'>803</a>
869
+ <a name='L804'></a><a href='#L804'>804</a>
870
+ <a name='L805'></a><a href='#L805'>805</a>
871
+ <a name='L806'></a><a href='#L806'>806</a>
872
+ <a name='L807'></a><a href='#L807'>807</a>
873
+ <a name='L808'></a><a href='#L808'>808</a>
874
+ <a name='L809'></a><a href='#L809'>809</a>
875
+ <a name='L810'></a><a href='#L810'>810</a>
876
+ <a name='L811'></a><a href='#L811'>811</a>
877
+ <a name='L812'></a><a href='#L812'>812</a>
878
+ <a name='L813'></a><a href='#L813'>813</a>
879
+ <a name='L814'></a><a href='#L814'>814</a>
880
+ <a name='L815'></a><a href='#L815'>815</a>
881
+ <a name='L816'></a><a href='#L816'>816</a>
882
+ <a name='L817'></a><a href='#L817'>817</a>
883
+ <a name='L818'></a><a href='#L818'>818</a>
884
+ <a name='L819'></a><a href='#L819'>819</a>
885
+ <a name='L820'></a><a href='#L820'>820</a>
886
+ <a name='L821'></a><a href='#L821'>821</a>
887
+ <a name='L822'></a><a href='#L822'>822</a>
888
+ <a name='L823'></a><a href='#L823'>823</a>
889
+ <a name='L824'></a><a href='#L824'>824</a>
890
+ <a name='L825'></a><a href='#L825'>825</a>
891
+ <a name='L826'></a><a href='#L826'>826</a>
892
+ <a name='L827'></a><a href='#L827'>827</a>
893
+ <a name='L828'></a><a href='#L828'>828</a>
894
+ <a name='L829'></a><a href='#L829'>829</a>
895
+ <a name='L830'></a><a href='#L830'>830</a>
896
+ <a name='L831'></a><a href='#L831'>831</a>
897
+ <a name='L832'></a><a href='#L832'>832</a>
898
+ <a name='L833'></a><a href='#L833'>833</a>
899
+ <a name='L834'></a><a href='#L834'>834</a>
900
+ <a name='L835'></a><a href='#L835'>835</a>
901
+ <a name='L836'></a><a href='#L836'>836</a>
902
+ <a name='L837'></a><a href='#L837'>837</a>
903
+ <a name='L838'></a><a href='#L838'>838</a>
904
+ <a name='L839'></a><a href='#L839'>839</a>
905
+ <a name='L840'></a><a href='#L840'>840</a>
906
+ <a name='L841'></a><a href='#L841'>841</a>
907
+ <a name='L842'></a><a href='#L842'>842</a>
908
+ <a name='L843'></a><a href='#L843'>843</a>
909
+ <a name='L844'></a><a href='#L844'>844</a>
910
+ <a name='L845'></a><a href='#L845'>845</a>
911
+ <a name='L846'></a><a href='#L846'>846</a>
912
+ <a name='L847'></a><a href='#L847'>847</a>
913
+ <a name='L848'></a><a href='#L848'>848</a>
914
+ <a name='L849'></a><a href='#L849'>849</a>
915
+ <a name='L850'></a><a href='#L850'>850</a>
916
+ <a name='L851'></a><a href='#L851'>851</a>
917
+ <a name='L852'></a><a href='#L852'>852</a>
918
+ <a name='L853'></a><a href='#L853'>853</a>
919
+ <a name='L854'></a><a href='#L854'>854</a>
920
+ <a name='L855'></a><a href='#L855'>855</a>
921
+ <a name='L856'></a><a href='#L856'>856</a>
922
+ <a name='L857'></a><a href='#L857'>857</a>
923
+ <a name='L858'></a><a href='#L858'>858</a>
924
+ <a name='L859'></a><a href='#L859'>859</a>
925
+ <a name='L860'></a><a href='#L860'>860</a>
926
+ <a name='L861'></a><a href='#L861'>861</a>
927
+ <a name='L862'></a><a href='#L862'>862</a>
928
+ <a name='L863'></a><a href='#L863'>863</a>
929
+ <a name='L864'></a><a href='#L864'>864</a>
930
+ <a name='L865'></a><a href='#L865'>865</a>
931
+ <a name='L866'></a><a href='#L866'>866</a>
932
+ <a name='L867'></a><a href='#L867'>867</a>
933
+ <a name='L868'></a><a href='#L868'>868</a>
934
+ <a name='L869'></a><a href='#L869'>869</a>
935
+ <a name='L870'></a><a href='#L870'>870</a>
936
+ <a name='L871'></a><a href='#L871'>871</a>
937
+ <a name='L872'></a><a href='#L872'>872</a>
938
+ <a name='L873'></a><a href='#L873'>873</a>
939
+ <a name='L874'></a><a href='#L874'>874</a>
940
+ <a name='L875'></a><a href='#L875'>875</a>
941
+ <a name='L876'></a><a href='#L876'>876</a>
942
+ <a name='L877'></a><a href='#L877'>877</a>
943
+ <a name='L878'></a><a href='#L878'>878</a>
944
+ <a name='L879'></a><a href='#L879'>879</a>
945
+ <a name='L880'></a><a href='#L880'>880</a>
946
+ <a name='L881'></a><a href='#L881'>881</a>
947
+ <a name='L882'></a><a href='#L882'>882</a>
948
+ <a name='L883'></a><a href='#L883'>883</a>
949
+ <a name='L884'></a><a href='#L884'>884</a>
950
+ <a name='L885'></a><a href='#L885'>885</a>
951
+ <a name='L886'></a><a href='#L886'>886</a>
952
+ <a name='L887'></a><a href='#L887'>887</a>
953
+ <a name='L888'></a><a href='#L888'>888</a>
954
+ <a name='L889'></a><a href='#L889'>889</a>
955
+ <a name='L890'></a><a href='#L890'>890</a>
956
+ <a name='L891'></a><a href='#L891'>891</a>
957
+ <a name='L892'></a><a href='#L892'>892</a>
958
+ <a name='L893'></a><a href='#L893'>893</a>
959
+ <a name='L894'></a><a href='#L894'>894</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
960
+ <span class="cline-any cline-yes">1x</span>
961
+ <span class="cline-any cline-yes">1x</span>
962
+ <span class="cline-any cline-yes">1x</span>
963
+ <span class="cline-any cline-yes">1x</span>
964
+ <span class="cline-any cline-yes">1x</span>
965
+ <span class="cline-any cline-yes">1x</span>
966
+ <span class="cline-any cline-yes">1x</span>
967
+ <span class="cline-any cline-yes">1x</span>
968
+ <span class="cline-any cline-yes">1x</span>
969
+ <span class="cline-any cline-yes">1x</span>
970
+ <span class="cline-any cline-yes">1x</span>
971
+ <span class="cline-any cline-yes">1x</span>
972
+ <span class="cline-any cline-yes">1x</span>
973
+ <span class="cline-any cline-yes">1x</span>
974
+ <span class="cline-any cline-yes">1x</span>
975
+ <span class="cline-any cline-yes">1x</span>
976
+ <span class="cline-any cline-yes">1x</span>
977
+ <span class="cline-any cline-yes">1x</span>
978
+ <span class="cline-any cline-yes">1x</span>
979
+ <span class="cline-any cline-yes">1x</span>
980
+ <span class="cline-any cline-yes">1x</span>
981
+ <span class="cline-any cline-yes">1x</span>
982
+ <span class="cline-any cline-yes">1x</span>
983
+ <span class="cline-any cline-yes">1x</span>
984
+ <span class="cline-any cline-yes">1x</span>
985
+ <span class="cline-any cline-yes">1x</span>
986
+ <span class="cline-any cline-yes">1x</span>
987
+ <span class="cline-any cline-yes">1x</span>
988
+ <span class="cline-any cline-yes">1x</span>
989
+ <span class="cline-any cline-yes">1x</span>
990
+ <span class="cline-any cline-yes">1x</span>
991
+ <span class="cline-any cline-yes">1x</span>
992
+ <span class="cline-any cline-yes">1x</span>
993
+ <span class="cline-any cline-yes">1x</span>
994
+ <span class="cline-any cline-yes">1x</span>
995
+ <span class="cline-any cline-yes">1x</span>
996
+ <span class="cline-any cline-yes">1x</span>
997
+ <span class="cline-any cline-yes">1x</span>
998
+ <span class="cline-any cline-yes">1x</span>
999
+ <span class="cline-any cline-yes">1x</span>
1000
+ <span class="cline-any cline-yes">1x</span>
1001
+ <span class="cline-any cline-yes">1x</span>
1002
+ <span class="cline-any cline-yes">1x</span>
1003
+ <span class="cline-any cline-yes">1x</span>
1004
+ <span class="cline-any cline-yes">1x</span>
1005
+ <span class="cline-any cline-yes">1x</span>
1006
+ <span class="cline-any cline-yes">1x</span>
1007
+ <span class="cline-any cline-yes">1x</span>
1008
+ <span class="cline-any cline-yes">1x</span>
1009
+ <span class="cline-any cline-yes">1x</span>
1010
+ <span class="cline-any cline-yes">1x</span>
1011
+ <span class="cline-any cline-yes">1x</span>
1012
+ <span class="cline-any cline-yes">1x</span>
1013
+ <span class="cline-any cline-yes">1x</span>
1014
+ <span class="cline-any cline-yes">1x</span>
1015
+ <span class="cline-any cline-yes">1x</span>
1016
+ <span class="cline-any cline-yes">1x</span>
1017
+ <span class="cline-any cline-yes">1x</span>
1018
+ <span class="cline-any cline-yes">1x</span>
1019
+ <span class="cline-any cline-yes">1x</span>
1020
+ <span class="cline-any cline-yes">1x</span>
1021
+ <span class="cline-any cline-yes">1x</span>
1022
+ <span class="cline-any cline-yes">1x</span>
1023
+ <span class="cline-any cline-yes">1x</span>
1024
+ <span class="cline-any cline-yes">1x</span>
1025
+ <span class="cline-any cline-yes">1x</span>
1026
+ <span class="cline-any cline-yes">1x</span>
1027
+ <span class="cline-any cline-yes">1x</span>
1028
+ <span class="cline-any cline-yes">1x</span>
1029
+ <span class="cline-any cline-no">&nbsp;</span>
1030
+ <span class="cline-any cline-no">&nbsp;</span>
1031
+ <span class="cline-any cline-no">&nbsp;</span>
1032
+ <span class="cline-any cline-no">&nbsp;</span>
1033
+ <span class="cline-any cline-no">&nbsp;</span>
1034
+ <span class="cline-any cline-yes">1x</span>
1035
+ <span class="cline-any cline-yes">1x</span>
1036
+ <span class="cline-any cline-yes">1x</span>
1037
+ <span class="cline-any cline-yes">1x</span>
1038
+ <span class="cline-any cline-yes">1x</span>
1039
+ <span class="cline-any cline-no">&nbsp;</span>
1040
+ <span class="cline-any cline-no">&nbsp;</span>
1041
+ <span class="cline-any cline-no">&nbsp;</span>
1042
+ <span class="cline-any cline-no">&nbsp;</span>
1043
+ <span class="cline-any cline-no">&nbsp;</span>
1044
+ <span class="cline-any cline-no">&nbsp;</span>
1045
+ <span class="cline-any cline-no">&nbsp;</span>
1046
+ <span class="cline-any cline-no">&nbsp;</span>
1047
+ <span class="cline-any cline-no">&nbsp;</span>
1048
+ <span class="cline-any cline-no">&nbsp;</span>
1049
+ <span class="cline-any cline-no">&nbsp;</span>
1050
+ <span class="cline-any cline-no">&nbsp;</span>
1051
+ <span class="cline-any cline-no">&nbsp;</span>
1052
+ <span class="cline-any cline-no">&nbsp;</span>
1053
+ <span class="cline-any cline-no">&nbsp;</span>
1054
+ <span class="cline-any cline-no">&nbsp;</span>
1055
+ <span class="cline-any cline-no">&nbsp;</span>
1056
+ <span class="cline-any cline-no">&nbsp;</span>
1057
+ <span class="cline-any cline-no">&nbsp;</span>
1058
+ <span class="cline-any cline-no">&nbsp;</span>
1059
+ <span class="cline-any cline-no">&nbsp;</span>
1060
+ <span class="cline-any cline-no">&nbsp;</span>
1061
+ <span class="cline-any cline-no">&nbsp;</span>
1062
+ <span class="cline-any cline-no">&nbsp;</span>
1063
+ <span class="cline-any cline-no">&nbsp;</span>
1064
+ <span class="cline-any cline-no">&nbsp;</span>
1065
+ <span class="cline-any cline-no">&nbsp;</span>
1066
+ <span class="cline-any cline-no">&nbsp;</span>
1067
+ <span class="cline-any cline-no">&nbsp;</span>
1068
+ <span class="cline-any cline-no">&nbsp;</span>
1069
+ <span class="cline-any cline-no">&nbsp;</span>
1070
+ <span class="cline-any cline-no">&nbsp;</span>
1071
+ <span class="cline-any cline-no">&nbsp;</span>
1072
+ <span class="cline-any cline-no">&nbsp;</span>
1073
+ <span class="cline-any cline-no">&nbsp;</span>
1074
+ <span class="cline-any cline-no">&nbsp;</span>
1075
+ <span class="cline-any cline-no">&nbsp;</span>
1076
+ <span class="cline-any cline-no">&nbsp;</span>
1077
+ <span class="cline-any cline-no">&nbsp;</span>
1078
+ <span class="cline-any cline-no">&nbsp;</span>
1079
+ <span class="cline-any cline-no">&nbsp;</span>
1080
+ <span class="cline-any cline-no">&nbsp;</span>
1081
+ <span class="cline-any cline-no">&nbsp;</span>
1082
+ <span class="cline-any cline-no">&nbsp;</span>
1083
+ <span class="cline-any cline-no">&nbsp;</span>
1084
+ <span class="cline-any cline-no">&nbsp;</span>
1085
+ <span class="cline-any cline-no">&nbsp;</span>
1086
+ <span class="cline-any cline-no">&nbsp;</span>
1087
+ <span class="cline-any cline-no">&nbsp;</span>
1088
+ <span class="cline-any cline-no">&nbsp;</span>
1089
+ <span class="cline-any cline-yes">1x</span>
1090
+ <span class="cline-any cline-yes">1x</span>
1091
+ <span class="cline-any cline-yes">1x</span>
1092
+ <span class="cline-any cline-yes">1x</span>
1093
+ <span class="cline-any cline-yes">1x</span>
1094
+ <span class="cline-any cline-no">&nbsp;</span>
1095
+ <span class="cline-any cline-no">&nbsp;</span>
1096
+ <span class="cline-any cline-no">&nbsp;</span>
1097
+ <span class="cline-any cline-no">&nbsp;</span>
1098
+ <span class="cline-any cline-no">&nbsp;</span>
1099
+ <span class="cline-any cline-no">&nbsp;</span>
1100
+ <span class="cline-any cline-no">&nbsp;</span>
1101
+ <span class="cline-any cline-no">&nbsp;</span>
1102
+ <span class="cline-any cline-no">&nbsp;</span>
1103
+ <span class="cline-any cline-no">&nbsp;</span>
1104
+ <span class="cline-any cline-no">&nbsp;</span>
1105
+ <span class="cline-any cline-no">&nbsp;</span>
1106
+ <span class="cline-any cline-no">&nbsp;</span>
1107
+ <span class="cline-any cline-no">&nbsp;</span>
1108
+ <span class="cline-any cline-no">&nbsp;</span>
1109
+ <span class="cline-any cline-no">&nbsp;</span>
1110
+ <span class="cline-any cline-no">&nbsp;</span>
1111
+ <span class="cline-any cline-no">&nbsp;</span>
1112
+ <span class="cline-any cline-no">&nbsp;</span>
1113
+ <span class="cline-any cline-no">&nbsp;</span>
1114
+ <span class="cline-any cline-no">&nbsp;</span>
1115
+ <span class="cline-any cline-no">&nbsp;</span>
1116
+ <span class="cline-any cline-no">&nbsp;</span>
1117
+ <span class="cline-any cline-no">&nbsp;</span>
1118
+ <span class="cline-any cline-no">&nbsp;</span>
1119
+ <span class="cline-any cline-no">&nbsp;</span>
1120
+ <span class="cline-any cline-no">&nbsp;</span>
1121
+ <span class="cline-any cline-no">&nbsp;</span>
1122
+ <span class="cline-any cline-no">&nbsp;</span>
1123
+ <span class="cline-any cline-no">&nbsp;</span>
1124
+ <span class="cline-any cline-no">&nbsp;</span>
1125
+ <span class="cline-any cline-no">&nbsp;</span>
1126
+ <span class="cline-any cline-no">&nbsp;</span>
1127
+ <span class="cline-any cline-no">&nbsp;</span>
1128
+ <span class="cline-any cline-no">&nbsp;</span>
1129
+ <span class="cline-any cline-no">&nbsp;</span>
1130
+ <span class="cline-any cline-no">&nbsp;</span>
1131
+ <span class="cline-any cline-no">&nbsp;</span>
1132
+ <span class="cline-any cline-no">&nbsp;</span>
1133
+ <span class="cline-any cline-no">&nbsp;</span>
1134
+ <span class="cline-any cline-no">&nbsp;</span>
1135
+ <span class="cline-any cline-no">&nbsp;</span>
1136
+ <span class="cline-any cline-no">&nbsp;</span>
1137
+ <span class="cline-any cline-no">&nbsp;</span>
1138
+ <span class="cline-any cline-no">&nbsp;</span>
1139
+ <span class="cline-any cline-no">&nbsp;</span>
1140
+ <span class="cline-any cline-no">&nbsp;</span>
1141
+ <span class="cline-any cline-no">&nbsp;</span>
1142
+ <span class="cline-any cline-no">&nbsp;</span>
1143
+ <span class="cline-any cline-no">&nbsp;</span>
1144
+ <span class="cline-any cline-no">&nbsp;</span>
1145
+ <span class="cline-any cline-no">&nbsp;</span>
1146
+ <span class="cline-any cline-yes">1x</span>
1147
+ <span class="cline-any cline-yes">1x</span>
1148
+ <span class="cline-any cline-yes">1x</span>
1149
+ <span class="cline-any cline-yes">1x</span>
1150
+ <span class="cline-any cline-yes">1x</span>
1151
+ <span class="cline-any cline-no">&nbsp;</span>
1152
+ <span class="cline-any cline-no">&nbsp;</span>
1153
+ <span class="cline-any cline-no">&nbsp;</span>
1154
+ <span class="cline-any cline-no">&nbsp;</span>
1155
+ <span class="cline-any cline-no">&nbsp;</span>
1156
+ <span class="cline-any cline-no">&nbsp;</span>
1157
+ <span class="cline-any cline-no">&nbsp;</span>
1158
+ <span class="cline-any cline-no">&nbsp;</span>
1159
+ <span class="cline-any cline-no">&nbsp;</span>
1160
+ <span class="cline-any cline-no">&nbsp;</span>
1161
+ <span class="cline-any cline-no">&nbsp;</span>
1162
+ <span class="cline-any cline-no">&nbsp;</span>
1163
+ <span class="cline-any cline-no">&nbsp;</span>
1164
+ <span class="cline-any cline-no">&nbsp;</span>
1165
+ <span class="cline-any cline-no">&nbsp;</span>
1166
+ <span class="cline-any cline-no">&nbsp;</span>
1167
+ <span class="cline-any cline-no">&nbsp;</span>
1168
+ <span class="cline-any cline-no">&nbsp;</span>
1169
+ <span class="cline-any cline-no">&nbsp;</span>
1170
+ <span class="cline-any cline-no">&nbsp;</span>
1171
+ <span class="cline-any cline-no">&nbsp;</span>
1172
+ <span class="cline-any cline-no">&nbsp;</span>
1173
+ <span class="cline-any cline-no">&nbsp;</span>
1174
+ <span class="cline-any cline-no">&nbsp;</span>
1175
+ <span class="cline-any cline-no">&nbsp;</span>
1176
+ <span class="cline-any cline-no">&nbsp;</span>
1177
+ <span class="cline-any cline-no">&nbsp;</span>
1178
+ <span class="cline-any cline-no">&nbsp;</span>
1179
+ <span class="cline-any cline-no">&nbsp;</span>
1180
+ <span class="cline-any cline-no">&nbsp;</span>
1181
+ <span class="cline-any cline-no">&nbsp;</span>
1182
+ <span class="cline-any cline-no">&nbsp;</span>
1183
+ <span class="cline-any cline-no">&nbsp;</span>
1184
+ <span class="cline-any cline-no">&nbsp;</span>
1185
+ <span class="cline-any cline-no">&nbsp;</span>
1186
+ <span class="cline-any cline-no">&nbsp;</span>
1187
+ <span class="cline-any cline-no">&nbsp;</span>
1188
+ <span class="cline-any cline-yes">1x</span>
1189
+ <span class="cline-any cline-yes">1x</span>
1190
+ <span class="cline-any cline-yes">1x</span>
1191
+ <span class="cline-any cline-yes">1x</span>
1192
+ <span class="cline-any cline-yes">1x</span>
1193
+ <span class="cline-any cline-yes">1x</span>
1194
+ <span class="cline-any cline-no">&nbsp;</span>
1195
+ <span class="cline-any cline-no">&nbsp;</span>
1196
+ <span class="cline-any cline-no">&nbsp;</span>
1197
+ <span class="cline-any cline-no">&nbsp;</span>
1198
+ <span class="cline-any cline-no">&nbsp;</span>
1199
+ <span class="cline-any cline-no">&nbsp;</span>
1200
+ <span class="cline-any cline-no">&nbsp;</span>
1201
+ <span class="cline-any cline-no">&nbsp;</span>
1202
+ <span class="cline-any cline-no">&nbsp;</span>
1203
+ <span class="cline-any cline-no">&nbsp;</span>
1204
+ <span class="cline-any cline-no">&nbsp;</span>
1205
+ <span class="cline-any cline-no">&nbsp;</span>
1206
+ <span class="cline-any cline-no">&nbsp;</span>
1207
+ <span class="cline-any cline-no">&nbsp;</span>
1208
+ <span class="cline-any cline-no">&nbsp;</span>
1209
+ <span class="cline-any cline-no">&nbsp;</span>
1210
+ <span class="cline-any cline-no">&nbsp;</span>
1211
+ <span class="cline-any cline-no">&nbsp;</span>
1212
+ <span class="cline-any cline-no">&nbsp;</span>
1213
+ <span class="cline-any cline-no">&nbsp;</span>
1214
+ <span class="cline-any cline-no">&nbsp;</span>
1215
+ <span class="cline-any cline-no">&nbsp;</span>
1216
+ <span class="cline-any cline-no">&nbsp;</span>
1217
+ <span class="cline-any cline-no">&nbsp;</span>
1218
+ <span class="cline-any cline-no">&nbsp;</span>
1219
+ <span class="cline-any cline-no">&nbsp;</span>
1220
+ <span class="cline-any cline-no">&nbsp;</span>
1221
+ <span class="cline-any cline-no">&nbsp;</span>
1222
+ <span class="cline-any cline-no">&nbsp;</span>
1223
+ <span class="cline-any cline-no">&nbsp;</span>
1224
+ <span class="cline-any cline-no">&nbsp;</span>
1225
+ <span class="cline-any cline-no">&nbsp;</span>
1226
+ <span class="cline-any cline-no">&nbsp;</span>
1227
+ <span class="cline-any cline-no">&nbsp;</span>
1228
+ <span class="cline-any cline-no">&nbsp;</span>
1229
+ <span class="cline-any cline-no">&nbsp;</span>
1230
+ <span class="cline-any cline-no">&nbsp;</span>
1231
+ <span class="cline-any cline-no">&nbsp;</span>
1232
+ <span class="cline-any cline-no">&nbsp;</span>
1233
+ <span class="cline-any cline-no">&nbsp;</span>
1234
+ <span class="cline-any cline-no">&nbsp;</span>
1235
+ <span class="cline-any cline-no">&nbsp;</span>
1236
+ <span class="cline-any cline-no">&nbsp;</span>
1237
+ <span class="cline-any cline-no">&nbsp;</span>
1238
+ <span class="cline-any cline-no">&nbsp;</span>
1239
+ <span class="cline-any cline-no">&nbsp;</span>
1240
+ <span class="cline-any cline-no">&nbsp;</span>
1241
+ <span class="cline-any cline-no">&nbsp;</span>
1242
+ <span class="cline-any cline-no">&nbsp;</span>
1243
+ <span class="cline-any cline-no">&nbsp;</span>
1244
+ <span class="cline-any cline-no">&nbsp;</span>
1245
+ <span class="cline-any cline-no">&nbsp;</span>
1246
+ <span class="cline-any cline-no">&nbsp;</span>
1247
+ <span class="cline-any cline-no">&nbsp;</span>
1248
+ <span class="cline-any cline-no">&nbsp;</span>
1249
+ <span class="cline-any cline-no">&nbsp;</span>
1250
+ <span class="cline-any cline-no">&nbsp;</span>
1251
+ <span class="cline-any cline-no">&nbsp;</span>
1252
+ <span class="cline-any cline-no">&nbsp;</span>
1253
+ <span class="cline-any cline-no">&nbsp;</span>
1254
+ <span class="cline-any cline-no">&nbsp;</span>
1255
+ <span class="cline-any cline-no">&nbsp;</span>
1256
+ <span class="cline-any cline-no">&nbsp;</span>
1257
+ <span class="cline-any cline-no">&nbsp;</span>
1258
+ <span class="cline-any cline-no">&nbsp;</span>
1259
+ <span class="cline-any cline-no">&nbsp;</span>
1260
+ <span class="cline-any cline-no">&nbsp;</span>
1261
+ <span class="cline-any cline-no">&nbsp;</span>
1262
+ <span class="cline-any cline-no">&nbsp;</span>
1263
+ <span class="cline-any cline-no">&nbsp;</span>
1264
+ <span class="cline-any cline-no">&nbsp;</span>
1265
+ <span class="cline-any cline-no">&nbsp;</span>
1266
+ <span class="cline-any cline-no">&nbsp;</span>
1267
+ <span class="cline-any cline-no">&nbsp;</span>
1268
+ <span class="cline-any cline-no">&nbsp;</span>
1269
+ <span class="cline-any cline-no">&nbsp;</span>
1270
+ <span class="cline-any cline-no">&nbsp;</span>
1271
+ <span class="cline-any cline-no">&nbsp;</span>
1272
+ <span class="cline-any cline-no">&nbsp;</span>
1273
+ <span class="cline-any cline-no">&nbsp;</span>
1274
+ <span class="cline-any cline-no">&nbsp;</span>
1275
+ <span class="cline-any cline-no">&nbsp;</span>
1276
+ <span class="cline-any cline-no">&nbsp;</span>
1277
+ <span class="cline-any cline-no">&nbsp;</span>
1278
+ <span class="cline-any cline-yes">1x</span>
1279
+ <span class="cline-any cline-yes">1x</span>
1280
+ <span class="cline-any cline-yes">1x</span>
1281
+ <span class="cline-any cline-yes">1x</span>
1282
+ <span class="cline-any cline-yes">1x</span>
1283
+ <span class="cline-any cline-no">&nbsp;</span>
1284
+ <span class="cline-any cline-no">&nbsp;</span>
1285
+ <span class="cline-any cline-yes">1x</span>
1286
+ <span class="cline-any cline-yes">1x</span>
1287
+ <span class="cline-any cline-yes">1x</span>
1288
+ <span class="cline-any cline-yes">1x</span>
1289
+ <span class="cline-any cline-yes">1x</span>
1290
+ <span class="cline-any cline-no">&nbsp;</span>
1291
+ <span class="cline-any cline-no">&nbsp;</span>
1292
+ <span class="cline-any cline-no">&nbsp;</span>
1293
+ <span class="cline-any cline-no">&nbsp;</span>
1294
+ <span class="cline-any cline-no">&nbsp;</span>
1295
+ <span class="cline-any cline-no">&nbsp;</span>
1296
+ <span class="cline-any cline-no">&nbsp;</span>
1297
+ <span class="cline-any cline-no">&nbsp;</span>
1298
+ <span class="cline-any cline-no">&nbsp;</span>
1299
+ <span class="cline-any cline-no">&nbsp;</span>
1300
+ <span class="cline-any cline-no">&nbsp;</span>
1301
+ <span class="cline-any cline-no">&nbsp;</span>
1302
+ <span class="cline-any cline-no">&nbsp;</span>
1303
+ <span class="cline-any cline-no">&nbsp;</span>
1304
+ <span class="cline-any cline-yes">1x</span>
1305
+ <span class="cline-any cline-yes">1x</span>
1306
+ <span class="cline-any cline-yes">1x</span>
1307
+ <span class="cline-any cline-yes">1x</span>
1308
+ <span class="cline-any cline-yes">1x</span>
1309
+ <span class="cline-any cline-no">&nbsp;</span>
1310
+ <span class="cline-any cline-no">&nbsp;</span>
1311
+ <span class="cline-any cline-no">&nbsp;</span>
1312
+ <span class="cline-any cline-no">&nbsp;</span>
1313
+ <span class="cline-any cline-no">&nbsp;</span>
1314
+ <span class="cline-any cline-no">&nbsp;</span>
1315
+ <span class="cline-any cline-no">&nbsp;</span>
1316
+ <span class="cline-any cline-no">&nbsp;</span>
1317
+ <span class="cline-any cline-no">&nbsp;</span>
1318
+ <span class="cline-any cline-no">&nbsp;</span>
1319
+ <span class="cline-any cline-no">&nbsp;</span>
1320
+ <span class="cline-any cline-no">&nbsp;</span>
1321
+ <span class="cline-any cline-no">&nbsp;</span>
1322
+ <span class="cline-any cline-no">&nbsp;</span>
1323
+ <span class="cline-any cline-no">&nbsp;</span>
1324
+ <span class="cline-any cline-no">&nbsp;</span>
1325
+ <span class="cline-any cline-no">&nbsp;</span>
1326
+ <span class="cline-any cline-no">&nbsp;</span>
1327
+ <span class="cline-any cline-no">&nbsp;</span>
1328
+ <span class="cline-any cline-no">&nbsp;</span>
1329
+ <span class="cline-any cline-no">&nbsp;</span>
1330
+ <span class="cline-any cline-yes">1x</span>
1331
+ <span class="cline-any cline-yes">1x</span>
1332
+ <span class="cline-any cline-yes">1x</span>
1333
+ <span class="cline-any cline-yes">1x</span>
1334
+ <span class="cline-any cline-yes">1x</span>
1335
+ <span class="cline-any cline-yes">1x</span>
1336
+ <span class="cline-any cline-no">&nbsp;</span>
1337
+ <span class="cline-any cline-no">&nbsp;</span>
1338
+ <span class="cline-any cline-no">&nbsp;</span>
1339
+ <span class="cline-any cline-no">&nbsp;</span>
1340
+ <span class="cline-any cline-no">&nbsp;</span>
1341
+ <span class="cline-any cline-no">&nbsp;</span>
1342
+ <span class="cline-any cline-no">&nbsp;</span>
1343
+ <span class="cline-any cline-no">&nbsp;</span>
1344
+ <span class="cline-any cline-no">&nbsp;</span>
1345
+ <span class="cline-any cline-no">&nbsp;</span>
1346
+ <span class="cline-any cline-no">&nbsp;</span>
1347
+ <span class="cline-any cline-no">&nbsp;</span>
1348
+ <span class="cline-any cline-no">&nbsp;</span>
1349
+ <span class="cline-any cline-no">&nbsp;</span>
1350
+ <span class="cline-any cline-no">&nbsp;</span>
1351
+ <span class="cline-any cline-no">&nbsp;</span>
1352
+ <span class="cline-any cline-no">&nbsp;</span>
1353
+ <span class="cline-any cline-no">&nbsp;</span>
1354
+ <span class="cline-any cline-no">&nbsp;</span>
1355
+ <span class="cline-any cline-no">&nbsp;</span>
1356
+ <span class="cline-any cline-no">&nbsp;</span>
1357
+ <span class="cline-any cline-no">&nbsp;</span>
1358
+ <span class="cline-any cline-no">&nbsp;</span>
1359
+ <span class="cline-any cline-no">&nbsp;</span>
1360
+ <span class="cline-any cline-no">&nbsp;</span>
1361
+ <span class="cline-any cline-no">&nbsp;</span>
1362
+ <span class="cline-any cline-no">&nbsp;</span>
1363
+ <span class="cline-any cline-no">&nbsp;</span>
1364
+ <span class="cline-any cline-no">&nbsp;</span>
1365
+ <span class="cline-any cline-no">&nbsp;</span>
1366
+ <span class="cline-any cline-no">&nbsp;</span>
1367
+ <span class="cline-any cline-no">&nbsp;</span>
1368
+ <span class="cline-any cline-no">&nbsp;</span>
1369
+ <span class="cline-any cline-no">&nbsp;</span>
1370
+ <span class="cline-any cline-no">&nbsp;</span>
1371
+ <span class="cline-any cline-no">&nbsp;</span>
1372
+ <span class="cline-any cline-no">&nbsp;</span>
1373
+ <span class="cline-any cline-no">&nbsp;</span>
1374
+ <span class="cline-any cline-no">&nbsp;</span>
1375
+ <span class="cline-any cline-no">&nbsp;</span>
1376
+ <span class="cline-any cline-no">&nbsp;</span>
1377
+ <span class="cline-any cline-no">&nbsp;</span>
1378
+ <span class="cline-any cline-no">&nbsp;</span>
1379
+ <span class="cline-any cline-no">&nbsp;</span>
1380
+ <span class="cline-any cline-no">&nbsp;</span>
1381
+ <span class="cline-any cline-no">&nbsp;</span>
1382
+ <span class="cline-any cline-no">&nbsp;</span>
1383
+ <span class="cline-any cline-no">&nbsp;</span>
1384
+ <span class="cline-any cline-no">&nbsp;</span>
1385
+ <span class="cline-any cline-no">&nbsp;</span>
1386
+ <span class="cline-any cline-no">&nbsp;</span>
1387
+ <span class="cline-any cline-no">&nbsp;</span>
1388
+ <span class="cline-any cline-no">&nbsp;</span>
1389
+ <span class="cline-any cline-no">&nbsp;</span>
1390
+ <span class="cline-any cline-no">&nbsp;</span>
1391
+ <span class="cline-any cline-no">&nbsp;</span>
1392
+ <span class="cline-any cline-no">&nbsp;</span>
1393
+ <span class="cline-any cline-yes">1x</span>
1394
+ <span class="cline-any cline-yes">1x</span>
1395
+ <span class="cline-any cline-yes">1x</span>
1396
+ <span class="cline-any cline-yes">1x</span>
1397
+ <span class="cline-any cline-yes">1x</span>
1398
+ <span class="cline-any cline-no">&nbsp;</span>
1399
+ <span class="cline-any cline-no">&nbsp;</span>
1400
+ <span class="cline-any cline-yes">1x</span>
1401
+ <span class="cline-any cline-yes">1x</span>
1402
+ <span class="cline-any cline-yes">1x</span>
1403
+ <span class="cline-any cline-yes">1x</span>
1404
+ <span class="cline-any cline-yes">1x</span>
1405
+ <span class="cline-any cline-yes">1x</span>
1406
+ <span class="cline-any cline-no">&nbsp;</span>
1407
+ <span class="cline-any cline-no">&nbsp;</span>
1408
+ <span class="cline-any cline-no">&nbsp;</span>
1409
+ <span class="cline-any cline-no">&nbsp;</span>
1410
+ <span class="cline-any cline-no">&nbsp;</span>
1411
+ <span class="cline-any cline-no">&nbsp;</span>
1412
+ <span class="cline-any cline-no">&nbsp;</span>
1413
+ <span class="cline-any cline-no">&nbsp;</span>
1414
+ <span class="cline-any cline-no">&nbsp;</span>
1415
+ <span class="cline-any cline-no">&nbsp;</span>
1416
+ <span class="cline-any cline-no">&nbsp;</span>
1417
+ <span class="cline-any cline-no">&nbsp;</span>
1418
+ <span class="cline-any cline-no">&nbsp;</span>
1419
+ <span class="cline-any cline-no">&nbsp;</span>
1420
+ <span class="cline-any cline-no">&nbsp;</span>
1421
+ <span class="cline-any cline-no">&nbsp;</span>
1422
+ <span class="cline-any cline-yes">1x</span>
1423
+ <span class="cline-any cline-yes">1x</span>
1424
+ <span class="cline-any cline-yes">1x</span>
1425
+ <span class="cline-any cline-yes">1x</span>
1426
+ <span class="cline-any cline-yes">1x</span>
1427
+ <span class="cline-any cline-yes">1x</span>
1428
+ <span class="cline-any cline-no">&nbsp;</span>
1429
+ <span class="cline-any cline-no">&nbsp;</span>
1430
+ <span class="cline-any cline-no">&nbsp;</span>
1431
+ <span class="cline-any cline-no">&nbsp;</span>
1432
+ <span class="cline-any cline-no">&nbsp;</span>
1433
+ <span class="cline-any cline-no">&nbsp;</span>
1434
+ <span class="cline-any cline-no">&nbsp;</span>
1435
+ <span class="cline-any cline-no">&nbsp;</span>
1436
+ <span class="cline-any cline-no">&nbsp;</span>
1437
+ <span class="cline-any cline-no">&nbsp;</span>
1438
+ <span class="cline-any cline-no">&nbsp;</span>
1439
+ <span class="cline-any cline-no">&nbsp;</span>
1440
+ <span class="cline-any cline-no">&nbsp;</span>
1441
+ <span class="cline-any cline-no">&nbsp;</span>
1442
+ <span class="cline-any cline-no">&nbsp;</span>
1443
+ <span class="cline-any cline-no">&nbsp;</span>
1444
+ <span class="cline-any cline-no">&nbsp;</span>
1445
+ <span class="cline-any cline-no">&nbsp;</span>
1446
+ <span class="cline-any cline-no">&nbsp;</span>
1447
+ <span class="cline-any cline-no">&nbsp;</span>
1448
+ <span class="cline-any cline-no">&nbsp;</span>
1449
+ <span class="cline-any cline-no">&nbsp;</span>
1450
+ <span class="cline-any cline-no">&nbsp;</span>
1451
+ <span class="cline-any cline-no">&nbsp;</span>
1452
+ <span class="cline-any cline-no">&nbsp;</span>
1453
+ <span class="cline-any cline-no">&nbsp;</span>
1454
+ <span class="cline-any cline-no">&nbsp;</span>
1455
+ <span class="cline-any cline-no">&nbsp;</span>
1456
+ <span class="cline-any cline-no">&nbsp;</span>
1457
+ <span class="cline-any cline-no">&nbsp;</span>
1458
+ <span class="cline-any cline-no">&nbsp;</span>
1459
+ <span class="cline-any cline-no">&nbsp;</span>
1460
+ <span class="cline-any cline-no">&nbsp;</span>
1461
+ <span class="cline-any cline-no">&nbsp;</span>
1462
+ <span class="cline-any cline-no">&nbsp;</span>
1463
+ <span class="cline-any cline-no">&nbsp;</span>
1464
+ <span class="cline-any cline-no">&nbsp;</span>
1465
+ <span class="cline-any cline-no">&nbsp;</span>
1466
+ <span class="cline-any cline-no">&nbsp;</span>
1467
+ <span class="cline-any cline-no">&nbsp;</span>
1468
+ <span class="cline-any cline-no">&nbsp;</span>
1469
+ <span class="cline-any cline-yes">1x</span>
1470
+ <span class="cline-any cline-yes">1x</span>
1471
+ <span class="cline-any cline-yes">1x</span>
1472
+ <span class="cline-any cline-yes">1x</span>
1473
+ <span class="cline-any cline-yes">1x</span>
1474
+ <span class="cline-any cline-no">&nbsp;</span>
1475
+ <span class="cline-any cline-no">&nbsp;</span>
1476
+ <span class="cline-any cline-yes">1x</span>
1477
+ <span class="cline-any cline-yes">1x</span>
1478
+ <span class="cline-any cline-yes">1x</span>
1479
+ <span class="cline-any cline-yes">1x</span>
1480
+ <span class="cline-any cline-yes">1x</span>
1481
+ <span class="cline-any cline-yes">1x</span>
1482
+ <span class="cline-any cline-no">&nbsp;</span>
1483
+ <span class="cline-any cline-no">&nbsp;</span>
1484
+ <span class="cline-any cline-no">&nbsp;</span>
1485
+ <span class="cline-any cline-no">&nbsp;</span>
1486
+ <span class="cline-any cline-no">&nbsp;</span>
1487
+ <span class="cline-any cline-no">&nbsp;</span>
1488
+ <span class="cline-any cline-no">&nbsp;</span>
1489
+ <span class="cline-any cline-no">&nbsp;</span>
1490
+ <span class="cline-any cline-no">&nbsp;</span>
1491
+ <span class="cline-any cline-no">&nbsp;</span>
1492
+ <span class="cline-any cline-no">&nbsp;</span>
1493
+ <span class="cline-any cline-no">&nbsp;</span>
1494
+ <span class="cline-any cline-no">&nbsp;</span>
1495
+ <span class="cline-any cline-no">&nbsp;</span>
1496
+ <span class="cline-any cline-no">&nbsp;</span>
1497
+ <span class="cline-any cline-no">&nbsp;</span>
1498
+ <span class="cline-any cline-no">&nbsp;</span>
1499
+ <span class="cline-any cline-no">&nbsp;</span>
1500
+ <span class="cline-any cline-no">&nbsp;</span>
1501
+ <span class="cline-any cline-no">&nbsp;</span>
1502
+ <span class="cline-any cline-no">&nbsp;</span>
1503
+ <span class="cline-any cline-no">&nbsp;</span>
1504
+ <span class="cline-any cline-no">&nbsp;</span>
1505
+ <span class="cline-any cline-no">&nbsp;</span>
1506
+ <span class="cline-any cline-no">&nbsp;</span>
1507
+ <span class="cline-any cline-no">&nbsp;</span>
1508
+ <span class="cline-any cline-no">&nbsp;</span>
1509
+ <span class="cline-any cline-no">&nbsp;</span>
1510
+ <span class="cline-any cline-no">&nbsp;</span>
1511
+ <span class="cline-any cline-no">&nbsp;</span>
1512
+ <span class="cline-any cline-no">&nbsp;</span>
1513
+ <span class="cline-any cline-no">&nbsp;</span>
1514
+ <span class="cline-any cline-no">&nbsp;</span>
1515
+ <span class="cline-any cline-no">&nbsp;</span>
1516
+ <span class="cline-any cline-no">&nbsp;</span>
1517
+ <span class="cline-any cline-no">&nbsp;</span>
1518
+ <span class="cline-any cline-no">&nbsp;</span>
1519
+ <span class="cline-any cline-no">&nbsp;</span>
1520
+ <span class="cline-any cline-no">&nbsp;</span>
1521
+ <span class="cline-any cline-no">&nbsp;</span>
1522
+ <span class="cline-any cline-no">&nbsp;</span>
1523
+ <span class="cline-any cline-no">&nbsp;</span>
1524
+ <span class="cline-any cline-no">&nbsp;</span>
1525
+ <span class="cline-any cline-no">&nbsp;</span>
1526
+ <span class="cline-any cline-no">&nbsp;</span>
1527
+ <span class="cline-any cline-no">&nbsp;</span>
1528
+ <span class="cline-any cline-no">&nbsp;</span>
1529
+ <span class="cline-any cline-no">&nbsp;</span>
1530
+ <span class="cline-any cline-no">&nbsp;</span>
1531
+ <span class="cline-any cline-no">&nbsp;</span>
1532
+ <span class="cline-any cline-no">&nbsp;</span>
1533
+ <span class="cline-any cline-no">&nbsp;</span>
1534
+ <span class="cline-any cline-no">&nbsp;</span>
1535
+ <span class="cline-any cline-yes">1x</span>
1536
+ <span class="cline-any cline-yes">1x</span>
1537
+ <span class="cline-any cline-yes">1x</span>
1538
+ <span class="cline-any cline-yes">1x</span>
1539
+ <span class="cline-any cline-yes">1x</span>
1540
+ <span class="cline-any cline-yes">1x</span>
1541
+ <span class="cline-any cline-no">&nbsp;</span>
1542
+ <span class="cline-any cline-no">&nbsp;</span>
1543
+ <span class="cline-any cline-no">&nbsp;</span>
1544
+ <span class="cline-any cline-no">&nbsp;</span>
1545
+ <span class="cline-any cline-no">&nbsp;</span>
1546
+ <span class="cline-any cline-no">&nbsp;</span>
1547
+ <span class="cline-any cline-no">&nbsp;</span>
1548
+ <span class="cline-any cline-no">&nbsp;</span>
1549
+ <span class="cline-any cline-no">&nbsp;</span>
1550
+ <span class="cline-any cline-no">&nbsp;</span>
1551
+ <span class="cline-any cline-no">&nbsp;</span>
1552
+ <span class="cline-any cline-no">&nbsp;</span>
1553
+ <span class="cline-any cline-no">&nbsp;</span>
1554
+ <span class="cline-any cline-no">&nbsp;</span>
1555
+ <span class="cline-any cline-no">&nbsp;</span>
1556
+ <span class="cline-any cline-no">&nbsp;</span>
1557
+ <span class="cline-any cline-no">&nbsp;</span>
1558
+ <span class="cline-any cline-no">&nbsp;</span>
1559
+ <span class="cline-any cline-no">&nbsp;</span>
1560
+ <span class="cline-any cline-no">&nbsp;</span>
1561
+ <span class="cline-any cline-no">&nbsp;</span>
1562
+ <span class="cline-any cline-no">&nbsp;</span>
1563
+ <span class="cline-any cline-no">&nbsp;</span>
1564
+ <span class="cline-any cline-no">&nbsp;</span>
1565
+ <span class="cline-any cline-no">&nbsp;</span>
1566
+ <span class="cline-any cline-no">&nbsp;</span>
1567
+ <span class="cline-any cline-no">&nbsp;</span>
1568
+ <span class="cline-any cline-no">&nbsp;</span>
1569
+ <span class="cline-any cline-no">&nbsp;</span>
1570
+ <span class="cline-any cline-no">&nbsp;</span>
1571
+ <span class="cline-any cline-no">&nbsp;</span>
1572
+ <span class="cline-any cline-no">&nbsp;</span>
1573
+ <span class="cline-any cline-no">&nbsp;</span>
1574
+ <span class="cline-any cline-no">&nbsp;</span>
1575
+ <span class="cline-any cline-no">&nbsp;</span>
1576
+ <span class="cline-any cline-no">&nbsp;</span>
1577
+ <span class="cline-any cline-no">&nbsp;</span>
1578
+ <span class="cline-any cline-no">&nbsp;</span>
1579
+ <span class="cline-any cline-no">&nbsp;</span>
1580
+ <span class="cline-any cline-yes">1x</span>
1581
+ <span class="cline-any cline-yes">1x</span>
1582
+ <span class="cline-any cline-yes">1x</span>
1583
+ <span class="cline-any cline-yes">1x</span>
1584
+ <span class="cline-any cline-yes">1x</span>
1585
+ <span class="cline-any cline-yes">1x</span>
1586
+ <span class="cline-any cline-yes">1x</span>
1587
+ <span class="cline-any cline-yes">1x</span>
1588
+ <span class="cline-any cline-yes">1x</span>
1589
+ <span class="cline-any cline-yes">1x</span>
1590
+ <span class="cline-any cline-yes">1x</span>
1591
+ <span class="cline-any cline-yes">1x</span>
1592
+ <span class="cline-any cline-yes">1x</span>
1593
+ <span class="cline-any cline-yes">1x</span>
1594
+ <span class="cline-any cline-yes">1x</span>
1595
+ <span class="cline-any cline-yes">1x</span>
1596
+ <span class="cline-any cline-no">&nbsp;</span>
1597
+ <span class="cline-any cline-no">&nbsp;</span>
1598
+ <span class="cline-any cline-no">&nbsp;</span>
1599
+ <span class="cline-any cline-no">&nbsp;</span>
1600
+ <span class="cline-any cline-no">&nbsp;</span>
1601
+ <span class="cline-any cline-no">&nbsp;</span>
1602
+ <span class="cline-any cline-no">&nbsp;</span>
1603
+ <span class="cline-any cline-no">&nbsp;</span>
1604
+ <span class="cline-any cline-no">&nbsp;</span>
1605
+ <span class="cline-any cline-no">&nbsp;</span>
1606
+ <span class="cline-any cline-no">&nbsp;</span>
1607
+ <span class="cline-any cline-no">&nbsp;</span>
1608
+ <span class="cline-any cline-no">&nbsp;</span>
1609
+ <span class="cline-any cline-no">&nbsp;</span>
1610
+ <span class="cline-any cline-no">&nbsp;</span>
1611
+ <span class="cline-any cline-no">&nbsp;</span>
1612
+ <span class="cline-any cline-no">&nbsp;</span>
1613
+ <span class="cline-any cline-no">&nbsp;</span>
1614
+ <span class="cline-any cline-no">&nbsp;</span>
1615
+ <span class="cline-any cline-no">&nbsp;</span>
1616
+ <span class="cline-any cline-no">&nbsp;</span>
1617
+ <span class="cline-any cline-no">&nbsp;</span>
1618
+ <span class="cline-any cline-no">&nbsp;</span>
1619
+ <span class="cline-any cline-no">&nbsp;</span>
1620
+ <span class="cline-any cline-no">&nbsp;</span>
1621
+ <span class="cline-any cline-no">&nbsp;</span>
1622
+ <span class="cline-any cline-no">&nbsp;</span>
1623
+ <span class="cline-any cline-no">&nbsp;</span>
1624
+ <span class="cline-any cline-no">&nbsp;</span>
1625
+ <span class="cline-any cline-no">&nbsp;</span>
1626
+ <span class="cline-any cline-no">&nbsp;</span>
1627
+ <span class="cline-any cline-no">&nbsp;</span>
1628
+ <span class="cline-any cline-no">&nbsp;</span>
1629
+ <span class="cline-any cline-no">&nbsp;</span>
1630
+ <span class="cline-any cline-no">&nbsp;</span>
1631
+ <span class="cline-any cline-no">&nbsp;</span>
1632
+ <span class="cline-any cline-no">&nbsp;</span>
1633
+ <span class="cline-any cline-no">&nbsp;</span>
1634
+ <span class="cline-any cline-no">&nbsp;</span>
1635
+ <span class="cline-any cline-no">&nbsp;</span>
1636
+ <span class="cline-any cline-no">&nbsp;</span>
1637
+ <span class="cline-any cline-no">&nbsp;</span>
1638
+ <span class="cline-any cline-no">&nbsp;</span>
1639
+ <span class="cline-any cline-no">&nbsp;</span>
1640
+ <span class="cline-any cline-no">&nbsp;</span>
1641
+ <span class="cline-any cline-no">&nbsp;</span>
1642
+ <span class="cline-any cline-no">&nbsp;</span>
1643
+ <span class="cline-any cline-no">&nbsp;</span>
1644
+ <span class="cline-any cline-no">&nbsp;</span>
1645
+ <span class="cline-any cline-no">&nbsp;</span>
1646
+ <span class="cline-any cline-no">&nbsp;</span>
1647
+ <span class="cline-any cline-no">&nbsp;</span>
1648
+ <span class="cline-any cline-no">&nbsp;</span>
1649
+ <span class="cline-any cline-no">&nbsp;</span>
1650
+ <span class="cline-any cline-no">&nbsp;</span>
1651
+ <span class="cline-any cline-no">&nbsp;</span>
1652
+ <span class="cline-any cline-no">&nbsp;</span>
1653
+ <span class="cline-any cline-no">&nbsp;</span>
1654
+ <span class="cline-any cline-no">&nbsp;</span>
1655
+ <span class="cline-any cline-no">&nbsp;</span>
1656
+ <span class="cline-any cline-no">&nbsp;</span>
1657
+ <span class="cline-any cline-no">&nbsp;</span>
1658
+ <span class="cline-any cline-no">&nbsp;</span>
1659
+ <span class="cline-any cline-no">&nbsp;</span>
1660
+ <span class="cline-any cline-no">&nbsp;</span>
1661
+ <span class="cline-any cline-no">&nbsp;</span>
1662
+ <span class="cline-any cline-no">&nbsp;</span>
1663
+ <span class="cline-any cline-no">&nbsp;</span>
1664
+ <span class="cline-any cline-no">&nbsp;</span>
1665
+ <span class="cline-any cline-no">&nbsp;</span>
1666
+ <span class="cline-any cline-no">&nbsp;</span>
1667
+ <span class="cline-any cline-no">&nbsp;</span>
1668
+ <span class="cline-any cline-no">&nbsp;</span>
1669
+ <span class="cline-any cline-no">&nbsp;</span>
1670
+ <span class="cline-any cline-no">&nbsp;</span>
1671
+ <span class="cline-any cline-no">&nbsp;</span>
1672
+ <span class="cline-any cline-no">&nbsp;</span>
1673
+ <span class="cline-any cline-no">&nbsp;</span>
1674
+ <span class="cline-any cline-no">&nbsp;</span>
1675
+ <span class="cline-any cline-no">&nbsp;</span>
1676
+ <span class="cline-any cline-no">&nbsp;</span>
1677
+ <span class="cline-any cline-no">&nbsp;</span>
1678
+ <span class="cline-any cline-no">&nbsp;</span>
1679
+ <span class="cline-any cline-no">&nbsp;</span>
1680
+ <span class="cline-any cline-no">&nbsp;</span>
1681
+ <span class="cline-any cline-no">&nbsp;</span>
1682
+ <span class="cline-any cline-no">&nbsp;</span>
1683
+ <span class="cline-any cline-no">&nbsp;</span>
1684
+ <span class="cline-any cline-no">&nbsp;</span>
1685
+ <span class="cline-any cline-no">&nbsp;</span>
1686
+ <span class="cline-any cline-no">&nbsp;</span>
1687
+ <span class="cline-any cline-no">&nbsp;</span>
1688
+ <span class="cline-any cline-no">&nbsp;</span>
1689
+ <span class="cline-any cline-no">&nbsp;</span>
1690
+ <span class="cline-any cline-no">&nbsp;</span>
1691
+ <span class="cline-any cline-no">&nbsp;</span>
1692
+ <span class="cline-any cline-no">&nbsp;</span>
1693
+ <span class="cline-any cline-no">&nbsp;</span>
1694
+ <span class="cline-any cline-no">&nbsp;</span>
1695
+ <span class="cline-any cline-no">&nbsp;</span>
1696
+ <span class="cline-any cline-no">&nbsp;</span>
1697
+ <span class="cline-any cline-no">&nbsp;</span>
1698
+ <span class="cline-any cline-no">&nbsp;</span>
1699
+ <span class="cline-any cline-no">&nbsp;</span>
1700
+ <span class="cline-any cline-no">&nbsp;</span>
1701
+ <span class="cline-any cline-no">&nbsp;</span>
1702
+ <span class="cline-any cline-no">&nbsp;</span>
1703
+ <span class="cline-any cline-no">&nbsp;</span>
1704
+ <span class="cline-any cline-no">&nbsp;</span>
1705
+ <span class="cline-any cline-no">&nbsp;</span>
1706
+ <span class="cline-any cline-no">&nbsp;</span>
1707
+ <span class="cline-any cline-no">&nbsp;</span>
1708
+ <span class="cline-any cline-no">&nbsp;</span>
1709
+ <span class="cline-any cline-no">&nbsp;</span>
1710
+ <span class="cline-any cline-no">&nbsp;</span>
1711
+ <span class="cline-any cline-no">&nbsp;</span>
1712
+ <span class="cline-any cline-no">&nbsp;</span>
1713
+ <span class="cline-any cline-no">&nbsp;</span>
1714
+ <span class="cline-any cline-no">&nbsp;</span>
1715
+ <span class="cline-any cline-no">&nbsp;</span>
1716
+ <span class="cline-any cline-no">&nbsp;</span>
1717
+ <span class="cline-any cline-no">&nbsp;</span>
1718
+ <span class="cline-any cline-no">&nbsp;</span>
1719
+ <span class="cline-any cline-no">&nbsp;</span>
1720
+ <span class="cline-any cline-no">&nbsp;</span>
1721
+ <span class="cline-any cline-no">&nbsp;</span>
1722
+ <span class="cline-any cline-no">&nbsp;</span>
1723
+ <span class="cline-any cline-no">&nbsp;</span>
1724
+ <span class="cline-any cline-no">&nbsp;</span>
1725
+ <span class="cline-any cline-no">&nbsp;</span>
1726
+ <span class="cline-any cline-no">&nbsp;</span>
1727
+ <span class="cline-any cline-no">&nbsp;</span>
1728
+ <span class="cline-any cline-no">&nbsp;</span>
1729
+ <span class="cline-any cline-no">&nbsp;</span>
1730
+ <span class="cline-any cline-no">&nbsp;</span>
1731
+ <span class="cline-any cline-no">&nbsp;</span>
1732
+ <span class="cline-any cline-no">&nbsp;</span>
1733
+ <span class="cline-any cline-no">&nbsp;</span>
1734
+ <span class="cline-any cline-no">&nbsp;</span>
1735
+ <span class="cline-any cline-no">&nbsp;</span>
1736
+ <span class="cline-any cline-no">&nbsp;</span>
1737
+ <span class="cline-any cline-no">&nbsp;</span>
1738
+ <span class="cline-any cline-no">&nbsp;</span>
1739
+ <span class="cline-any cline-no">&nbsp;</span>
1740
+ <span class="cline-any cline-no">&nbsp;</span>
1741
+ <span class="cline-any cline-no">&nbsp;</span>
1742
+ <span class="cline-any cline-no">&nbsp;</span>
1743
+ <span class="cline-any cline-no">&nbsp;</span>
1744
+ <span class="cline-any cline-no">&nbsp;</span>
1745
+ <span class="cline-any cline-no">&nbsp;</span>
1746
+ <span class="cline-any cline-no">&nbsp;</span>
1747
+ <span class="cline-any cline-no">&nbsp;</span>
1748
+ <span class="cline-any cline-no">&nbsp;</span>
1749
+ <span class="cline-any cline-no">&nbsp;</span>
1750
+ <span class="cline-any cline-no">&nbsp;</span>
1751
+ <span class="cline-any cline-no">&nbsp;</span>
1752
+ <span class="cline-any cline-no">&nbsp;</span>
1753
+ <span class="cline-any cline-no">&nbsp;</span>
1754
+ <span class="cline-any cline-no">&nbsp;</span>
1755
+ <span class="cline-any cline-no">&nbsp;</span>
1756
+ <span class="cline-any cline-no">&nbsp;</span>
1757
+ <span class="cline-any cline-no">&nbsp;</span>
1758
+ <span class="cline-any cline-no">&nbsp;</span>
1759
+ <span class="cline-any cline-no">&nbsp;</span>
1760
+ <span class="cline-any cline-no">&nbsp;</span>
1761
+ <span class="cline-any cline-yes">1x</span>
1762
+ <span class="cline-any cline-yes">1x</span>
1763
+ <span class="cline-any cline-yes">1x</span>
1764
+ <span class="cline-any cline-yes">1x</span>
1765
+ <span class="cline-any cline-yes">1x</span>
1766
+ <span class="cline-any cline-yes">1x</span>
1767
+ <span class="cline-any cline-no">&nbsp;</span>
1768
+ <span class="cline-any cline-no">&nbsp;</span>
1769
+ <span class="cline-any cline-no">&nbsp;</span>
1770
+ <span class="cline-any cline-yes">1x</span>
1771
+ <span class="cline-any cline-yes">1x</span>
1772
+ <span class="cline-any cline-yes">1x</span>
1773
+ <span class="cline-any cline-yes">1x</span>
1774
+ <span class="cline-any cline-yes">1x</span>
1775
+ <span class="cline-any cline-no">&nbsp;</span>
1776
+ <span class="cline-any cline-no">&nbsp;</span>
1777
+ <span class="cline-any cline-no">&nbsp;</span>
1778
+ <span class="cline-any cline-yes">1x</span>
1779
+ <span class="cline-any cline-yes">1x</span>
1780
+ <span class="cline-any cline-yes">1x</span>
1781
+ <span class="cline-any cline-yes">1x</span>
1782
+ <span class="cline-any cline-yes">1x</span>
1783
+ <span class="cline-any cline-yes">1x</span>
1784
+ <span class="cline-any cline-yes">1x</span>
1785
+ <span class="cline-any cline-yes">1x</span>
1786
+ <span class="cline-any cline-no">&nbsp;</span>
1787
+ <span class="cline-any cline-no">&nbsp;</span>
1788
+ <span class="cline-any cline-no">&nbsp;</span>
1789
+ <span class="cline-any cline-no">&nbsp;</span>
1790
+ <span class="cline-any cline-no">&nbsp;</span>
1791
+ <span class="cline-any cline-no">&nbsp;</span>
1792
+ <span class="cline-any cline-no">&nbsp;</span>
1793
+ <span class="cline-any cline-no">&nbsp;</span>
1794
+ <span class="cline-any cline-no">&nbsp;</span>
1795
+ <span class="cline-any cline-no">&nbsp;</span>
1796
+ <span class="cline-any cline-no">&nbsp;</span>
1797
+ <span class="cline-any cline-no">&nbsp;</span>
1798
+ <span class="cline-any cline-no">&nbsp;</span>
1799
+ <span class="cline-any cline-no">&nbsp;</span>
1800
+ <span class="cline-any cline-no">&nbsp;</span>
1801
+ <span class="cline-any cline-no">&nbsp;</span>
1802
+ <span class="cline-any cline-no">&nbsp;</span>
1803
+ <span class="cline-any cline-no">&nbsp;</span>
1804
+ <span class="cline-any cline-no">&nbsp;</span>
1805
+ <span class="cline-any cline-yes">1x</span>
1806
+ <span class="cline-any cline-yes">1x</span>
1807
+ <span class="cline-any cline-yes">1x</span>
1808
+ <span class="cline-any cline-yes">1x</span>
1809
+ <span class="cline-any cline-yes">1x</span>
1810
+ <span class="cline-any cline-no">&nbsp;</span>
1811
+ <span class="cline-any cline-no">&nbsp;</span>
1812
+ <span class="cline-any cline-no">&nbsp;</span>
1813
+ <span class="cline-any cline-no">&nbsp;</span>
1814
+ <span class="cline-any cline-no">&nbsp;</span>
1815
+ <span class="cline-any cline-no">&nbsp;</span>
1816
+ <span class="cline-any cline-yes">1x</span>
1817
+ <span class="cline-any cline-yes">1x</span>
1818
+ <span class="cline-any cline-yes">1x</span>
1819
+ <span class="cline-any cline-yes">1x</span>
1820
+ <span class="cline-any cline-yes">1x</span>
1821
+ <span class="cline-any cline-no">&nbsp;</span>
1822
+ <span class="cline-any cline-no">&nbsp;</span>
1823
+ <span class="cline-any cline-no">&nbsp;</span>
1824
+ <span class="cline-any cline-no">&nbsp;</span>
1825
+ <span class="cline-any cline-no">&nbsp;</span>
1826
+ <span class="cline-any cline-no">&nbsp;</span>
1827
+ <span class="cline-any cline-no">&nbsp;</span>
1828
+ <span class="cline-any cline-no">&nbsp;</span>
1829
+ <span class="cline-any cline-no">&nbsp;</span>
1830
+ <span class="cline-any cline-yes">1x</span>
1831
+ <span class="cline-any cline-yes">1x</span>
1832
+ <span class="cline-any cline-yes">1x</span>
1833
+ <span class="cline-any cline-yes">1x</span>
1834
+ <span class="cline-any cline-no">&nbsp;</span>
1835
+ <span class="cline-any cline-no">&nbsp;</span>
1836
+ <span class="cline-any cline-no">&nbsp;</span>
1837
+ <span class="cline-any cline-no">&nbsp;</span>
1838
+ <span class="cline-any cline-yes">1x</span>
1839
+ <span class="cline-any cline-yes">1x</span>
1840
+ <span class="cline-any cline-no">&nbsp;</span>
1841
+ <span class="cline-any cline-no">&nbsp;</span>
1842
+ <span class="cline-any cline-no">&nbsp;</span>
1843
+ <span class="cline-any cline-no">&nbsp;</span>
1844
+ <span class="cline-any cline-no">&nbsp;</span>
1845
+ <span class="cline-any cline-no">&nbsp;</span>
1846
+ <span class="cline-any cline-no">&nbsp;</span>
1847
+ <span class="cline-any cline-no">&nbsp;</span>
1848
+ <span class="cline-any cline-no">&nbsp;</span>
1849
+ <span class="cline-any cline-no">&nbsp;</span>
1850
+ <span class="cline-any cline-no">&nbsp;</span>
1851
+ <span class="cline-any cline-yes">1x</span>
1852
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">// =============================
1853
+ // SESSION MANAGER
1854
+ // =============================
1855
+ &nbsp;
1856
+ /**
1857
+ * SessionManager para manejo automático de sesiones con Refresh Token Pattern
1858
+ * Proporciona una API simple para manejar autenticación con persistencia automática
1859
+ */
1860
+ &nbsp;
1861
+ import crudify from "@nocios/crudify-sdk";
1862
+ import { TokenStorage, TokenData, StorageType } from "../utils/tokenStorage";
1863
+ import { translateErrorCode } from "../utils/errorTranslation";
1864
+ import { authEventBus } from "../utils/authEventBus"; // ✅ FASE 3
1865
+ import { crossTabSync } from "./CrossTabSyncManager"; // ✅ Cross-tab sync
1866
+ import { logger } from "../utils/logger";
1867
+ import { CookieSync } from "../utils/cookieSync"; // Cookie cross-subdomain para files
1868
+ import type { CrudifyTokenData, CrudifyInitResponse } from "../types/crudify-sdk";
1869
+ &nbsp;
1870
+ export type SessionConfig = {
1871
+ storageType?: StorageType;
1872
+ autoRestore?: boolean;
1873
+ enableLogging?: boolean;
1874
+ onSessionExpired?: () =&gt; void;
1875
+ onSessionRestored?: (tokens: TokenData) =&gt; void;
1876
+ onLoginSuccess?: (tokens: TokenData) =&gt; void;
1877
+ onLogout?: () =&gt; void;
1878
+ showNotification?: (message: string, severity?: "error" | "info" | "success" | "warning") =&gt; void;
1879
+ translateFn?: (key: string) =&gt; string;
1880
+ apiEndpointAdmin?: string;
1881
+ apiKeyEndpointAdmin?: string;
1882
+ publicApiKey?: string;
1883
+ env?: "dev" | "stg" | "api" | "prod";
1884
+ };
1885
+ &nbsp;
1886
+ /**
1887
+ * Response data from a successful login
1888
+ */
1889
+ export interface LoginResponseData {
1890
+ token: string;
1891
+ refreshToken: string;
1892
+ expiresAt: number;
1893
+ refreshExpiresAt: number;
1894
+ [key: string]: unknown;
1895
+ }
1896
+ &nbsp;
1897
+ export type LoginResult = {
1898
+ success: boolean;
1899
+ tokens?: TokenData;
1900
+ data?: LoginResponseData; // Datos del usuario cuando login es exitoso
1901
+ error?: string;
1902
+ rawResponse?: Record&lt;string, unknown&gt;; // Respuesta cruda de la API para manejo específico de errores
1903
+ };
1904
+ &nbsp;
1905
+ export class SessionManager {
1906
+ private static instance: SessionManager;
1907
+ private config: SessionConfig = {};
1908
+ private initialized = false;
1909
+ private crudifyInitialized = false;
1910
+ // ✅ FASE 2: Tracker de última actividad del usuario
1911
+ private lastActivityTime: number = 0;
1912
+ // ✅ FASE 4: Flags para prevenir refreshes concurrentes
1913
+ private isRefreshingLocally = false;
1914
+ private refreshPromise: Promise&lt;boolean&gt; | null = null;
1915
+ // ✅ FASE 5: Flags para distinguir sesión activa vs cold start
1916
+ private isSessionActive = false;
1917
+ private isColdStartRestore = false;
1918
+ &nbsp;
1919
+ private <span class="fstat-no" title="function not covered" >constructor() {}</span>
1920
+ &nbsp;
1921
+ public static <span class="fstat-no" title="function not covered" >getInstance(): SessionManager {</span>
1922
+ <span class="cstat-no" title="statement not covered" > if (!SessionManager.instance) {</span>
1923
+ <span class="cstat-no" title="statement not covered" > SessionManager.instance = new SessionManager();</span>
1924
+ <span class="cstat-no" title="statement not covered" > }</span>
1925
+ <span class="cstat-no" title="statement not covered" > return SessionManager.instance;</span>
1926
+ <span class="cstat-no" title="statement not covered" > }</span>
1927
+ &nbsp;
1928
+ /**
1929
+ * Initialize SessionManager with configuration
1930
+ */
1931
+ <span class="fstat-no" title="function not covered" > public async initialize(config: SessionConfig = {}): Promise&lt;void&gt; {</span>
1932
+ <span class="cstat-no" title="statement not covered" > if (this.initialized) return;</span>
1933
+ <span class="cstat-no" title="statement not covered" ></span>
1934
+ <span class="cstat-no" title="statement not covered" > this.config = {</span>
1935
+ <span class="cstat-no" title="statement not covered" > storageType: "localStorage",</span>
1936
+ <span class="cstat-no" title="statement not covered" > autoRestore: true,</span>
1937
+ <span class="cstat-no" title="statement not covered" > enableLogging: false,</span>
1938
+ <span class="cstat-no" title="statement not covered" > env: "stg",</span>
1939
+ <span class="cstat-no" title="statement not covered" > ...config,</span>
1940
+ <span class="cstat-no" title="statement not covered" > };</span>
1941
+ <span class="cstat-no" title="statement not covered" ></span>
1942
+ <span class="cstat-no" title="statement not covered" > TokenStorage.setStorageType(this.config.storageType || "localStorage");</span>
1943
+ <span class="cstat-no" title="statement not covered" ></span>
1944
+ <span class="cstat-no" title="statement not covered" > // Initialize crudify SDK if publicApiKey is provided</span>
1945
+ <span class="cstat-no" title="statement not covered" > if (this.config.publicApiKey &amp;&amp; !this.crudifyInitialized) await this.ensureCrudifyInitialized();</span>
1946
+ <span class="cstat-no" title="statement not covered" ></span>
1947
+ <span class="cstat-no" title="statement not covered" > // Configure token invalidation callback</span>
1948
+ <span class="cstat-no" title="statement not covered" > // @ts-ignore - New method in crudify-core</span>
1949
+ <span class="cstat-no" title="statement not covered" > crudify.setTokenInvalidationCallback(() =&gt; {</span>
1950
+ <span class="cstat-no" title="statement not covered" > this.log("Tokens invalidated by crudify-core");</span>
1951
+ <span class="cstat-no" title="statement not covered" > authEventBus.emit("SESSION_EXPIRED", {</span>
1952
+ <span class="cstat-no" title="statement not covered" > message: "Your session has expired. Please log in again.",</span>
1953
+ <span class="cstat-no" title="statement not covered" > source: "crudify-core.clearTokensAndRefreshState",</span>
1954
+ <span class="cstat-no" title="statement not covered" > });</span>
1955
+ <span class="cstat-no" title="statement not covered" > });</span>
1956
+ <span class="cstat-no" title="statement not covered" ></span>
1957
+ <span class="cstat-no" title="statement not covered" > // Store admin credentials if provided</span>
1958
+ <span class="cstat-no" title="statement not covered" > if (this.config.apiEndpointAdmin &amp;&amp; this.config.apiKeyEndpointAdmin) {</span>
1959
+ <span class="cstat-no" title="statement not covered" > const existingTokens = await TokenStorage.getTokens();</span>
1960
+ <span class="cstat-no" title="statement not covered" > if (existingTokens) {</span>
1961
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.saveTokens({</span>
1962
+ <span class="cstat-no" title="statement not covered" > ...existingTokens,</span>
1963
+ <span class="cstat-no" title="statement not covered" > apiEndpointAdmin: this.config.apiEndpointAdmin,</span>
1964
+ <span class="cstat-no" title="statement not covered" > apiKeyEndpointAdmin: this.config.apiKeyEndpointAdmin,</span>
1965
+ <span class="cstat-no" title="statement not covered" > });</span>
1966
+ <span class="cstat-no" title="statement not covered" > } else {</span>
1967
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.saveTokens({</span>
1968
+ <span class="cstat-no" title="statement not covered" > accessToken: "",</span>
1969
+ <span class="cstat-no" title="statement not covered" > refreshToken: "",</span>
1970
+ <span class="cstat-no" title="statement not covered" > expiresAt: 0,</span>
1971
+ <span class="cstat-no" title="statement not covered" > refreshExpiresAt: 0,</span>
1972
+ <span class="cstat-no" title="statement not covered" > apiEndpointAdmin: this.config.apiEndpointAdmin,</span>
1973
+ <span class="cstat-no" title="statement not covered" > apiKeyEndpointAdmin: this.config.apiKeyEndpointAdmin,</span>
1974
+ <span class="cstat-no" title="statement not covered" > });</span>
1975
+ <span class="cstat-no" title="statement not covered" > }</span>
1976
+ <span class="cstat-no" title="statement not covered" > }</span>
1977
+ <span class="cstat-no" title="statement not covered" ></span>
1978
+ <span class="cstat-no" title="statement not covered" > if (this.config.autoRestore) await this.restoreSession();</span>
1979
+ <span class="cstat-no" title="statement not covered" ></span>
1980
+ <span class="cstat-no" title="statement not covered" > this.initialized = true;</span>
1981
+ <span class="cstat-no" title="statement not covered" > }</span>
1982
+ &nbsp;
1983
+ /**
1984
+ * Login with automatic token persistence
1985
+ */
1986
+ <span class="fstat-no" title="function not covered" > public async login(email: string, password: string): Promise&lt;LoginResult&gt; {</span>
1987
+ <span class="cstat-no" title="statement not covered" > try {</span>
1988
+ <span class="cstat-no" title="statement not covered" > const response = await crudify.login(email, password);</span>
1989
+ <span class="cstat-no" title="statement not covered" ></span>
1990
+ <span class="cstat-no" title="statement not covered" > if (!response.success) {</span>
1991
+ <span class="cstat-no" title="statement not covered" > return {</span>
1992
+ <span class="cstat-no" title="statement not covered" > success: false,</span>
1993
+ <span class="cstat-no" title="statement not covered" > error: this.formatError(response.errors),</span>
1994
+ <span class="cstat-no" title="statement not covered" > rawResponse: response as Record&lt;string, unknown&gt;,</span>
1995
+ <span class="cstat-no" title="statement not covered" > };</span>
1996
+ <span class="cstat-no" title="statement not covered" > }</span>
1997
+ <span class="cstat-no" title="statement not covered" ></span>
1998
+ <span class="cstat-no" title="statement not covered" > const existingTokens = await TokenStorage.getTokens();</span>
1999
+ <span class="cstat-no" title="statement not covered" > const loginData = response.data as LoginResponseData;</span>
2000
+ <span class="cstat-no" title="statement not covered" ></span>
2001
+ <span class="cstat-no" title="statement not covered" > const tokens: TokenData = {</span>
2002
+ <span class="cstat-no" title="statement not covered" > accessToken: loginData.token,</span>
2003
+ <span class="cstat-no" title="statement not covered" > refreshToken: loginData.refreshToken,</span>
2004
+ <span class="cstat-no" title="statement not covered" > expiresAt: loginData.expiresAt,</span>
2005
+ <span class="cstat-no" title="statement not covered" > refreshExpiresAt: loginData.refreshExpiresAt,</span>
2006
+ <span class="cstat-no" title="statement not covered" > apiEndpointAdmin: existingTokens?.apiEndpointAdmin || this.config.apiEndpointAdmin,</span>
2007
+ <span class="cstat-no" title="statement not covered" > apiKeyEndpointAdmin: existingTokens?.apiKeyEndpointAdmin || this.config.apiKeyEndpointAdmin,</span>
2008
+ <span class="cstat-no" title="statement not covered" > };</span>
2009
+ <span class="cstat-no" title="statement not covered" ></span>
2010
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.saveTokens(tokens);</span>
2011
+ <span class="cstat-no" title="statement not covered" > TokenStorage.markSuccessfulLogin(); // Iniciar grace period para proteger contra eventos cross-tab</span>
2012
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = Date.now();</span>
2013
+ <span class="cstat-no" title="statement not covered" ></span>
2014
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Marcar sesión como activa después de login exitoso</span>
2015
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = true;</span>
2016
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2017
+ <span class="cstat-no" title="statement not covered" ></span>
2018
+ <span class="cstat-no" title="statement not covered" > // Sincronizar token a cookie cross-subdomain para files.nocios.link</span>
2019
+ <span class="cstat-no" title="statement not covered" > CookieSync.setCrossSubdomainCookie(tokens.accessToken, tokens.expiresAt, this.config.env);</span>
2020
+ <span class="cstat-no" title="statement not covered" ></span>
2021
+ <span class="cstat-no" title="statement not covered" > // ✅ Notificar a otras pestañas del login exitoso</span>
2022
+ <span class="cstat-no" title="statement not covered" > crossTabSync.notifyLogin();</span>
2023
+ <span class="cstat-no" title="statement not covered" ></span>
2024
+ <span class="cstat-no" title="statement not covered" > this.config.onLoginSuccess?.(tokens);</span>
2025
+ <span class="cstat-no" title="statement not covered" ></span>
2026
+ <span class="cstat-no" title="statement not covered" > return {</span>
2027
+ <span class="cstat-no" title="statement not covered" > success: true,</span>
2028
+ <span class="cstat-no" title="statement not covered" > tokens,</span>
2029
+ <span class="cstat-no" title="statement not covered" > data: loginData,</span>
2030
+ <span class="cstat-no" title="statement not covered" > };</span>
2031
+ <span class="cstat-no" title="statement not covered" > } catch (error: unknown) {</span>
2032
+ <span class="cstat-no" title="statement not covered" > logger.error("[SessionManager] Login error", error instanceof Error ? error : { message: String(error) });</span>
2033
+ <span class="cstat-no" title="statement not covered" > return {</span>
2034
+ <span class="cstat-no" title="statement not covered" > success: false,</span>
2035
+ <span class="cstat-no" title="statement not covered" > error: error instanceof Error ? error.message : "Unknown error",</span>
2036
+ <span class="cstat-no" title="statement not covered" > };</span>
2037
+ <span class="cstat-no" title="statement not covered" > }</span>
2038
+ <span class="cstat-no" title="statement not covered" > }</span>
2039
+ &nbsp;
2040
+ /**
2041
+ * Logout con limpieza de tokens
2042
+ */
2043
+ <span class="fstat-no" title="function not covered" > public async logout(): Promise&lt;void&gt; {</span>
2044
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Marcar sesión como inactiva al inicio del logout</span>
2045
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = false;</span>
2046
+ <span class="cstat-no" title="statement not covered" ></span>
2047
+ <span class="cstat-no" title="statement not covered" > try {</span>
2048
+ <span class="cstat-no" title="statement not covered" > this.log("Logging out...");</span>
2049
+ <span class="cstat-no" title="statement not covered" ></span>
2050
+ <span class="cstat-no" title="statement not covered" > // Emit LOGOUT event BEFORE clearing tokens to notify subscribers</span>
2051
+ <span class="cstat-no" title="statement not covered" > // This allows LoginStateProvider to clear searchParams and formData</span>
2052
+ <span class="cstat-no" title="statement not covered" > authEventBus.emit("LOGOUT", {</span>
2053
+ <span class="cstat-no" title="statement not covered" > message: "User logged out",</span>
2054
+ <span class="cstat-no" title="statement not covered" > source: "SessionManager.logout",</span>
2055
+ <span class="cstat-no" title="statement not covered" > });</span>
2056
+ <span class="cstat-no" title="statement not covered" ></span>
2057
+ <span class="cstat-no" title="statement not covered" > // Limpiar cookie cross-subdomain antes de logout</span>
2058
+ <span class="cstat-no" title="statement not covered" > CookieSync.clearCrossSubdomainCookie(this.config.env);</span>
2059
+ <span class="cstat-no" title="statement not covered" ></span>
2060
+ <span class="cstat-no" title="statement not covered" > // Logout de crudify</span>
2061
+ <span class="cstat-no" title="statement not covered" > await crudify.logout();</span>
2062
+ <span class="cstat-no" title="statement not covered" ></span>
2063
+ <span class="cstat-no" title="statement not covered" > // Limpiar storage</span>
2064
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2065
+ <span class="cstat-no" title="statement not covered" ></span>
2066
+ <span class="cstat-no" title="statement not covered" > // ✅ Notificar a otras pestañas del logout</span>
2067
+ <span class="cstat-no" title="statement not covered" > crossTabSync.notifyLogout();</span>
2068
+ <span class="cstat-no" title="statement not covered" ></span>
2069
+ <span class="cstat-no" title="statement not covered" > this.log("Logout successful");</span>
2070
+ <span class="cstat-no" title="statement not covered" ></span>
2071
+ <span class="cstat-no" title="statement not covered" > // Callback de logout</span>
2072
+ <span class="cstat-no" title="statement not covered" > this.config.onLogout?.();</span>
2073
+ <span class="cstat-no" title="statement not covered" > } catch (error) {</span>
2074
+ <span class="cstat-no" title="statement not covered" > this.log("Logout error", { error: error instanceof Error ? error.message : String(error) });</span>
2075
+ <span class="cstat-no" title="statement not covered" > // Aún así limpiar tokens locales</span>
2076
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2077
+ <span class="cstat-no" title="statement not covered" > // ✅ Notificar logout incluso en caso de error</span>
2078
+ <span class="cstat-no" title="statement not covered" > crossTabSync.notifyLogout();</span>
2079
+ <span class="cstat-no" title="statement not covered" > }</span>
2080
+ <span class="cstat-no" title="statement not covered" > }</span>
2081
+ &nbsp;
2082
+ /**
2083
+ * ✅ MEJORADO FASE 5: Restaurar sesión desde storage con validación robusta
2084
+ * Usa refresh silencioso para evitar mostrar notificaciones durante cold start
2085
+ */
2086
+ <span class="fstat-no" title="function not covered" > public async restoreSession(): Promise&lt;boolean&gt; {</span>
2087
+ <span class="cstat-no" title="statement not covered" > try {</span>
2088
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Marcar que estamos en cold start restore</span>
2089
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = true;</span>
2090
+ <span class="cstat-no" title="statement not covered" > this.log("Attempting to restore session (cold start)...");</span>
2091
+ <span class="cstat-no" title="statement not covered" ></span>
2092
+ <span class="cstat-no" title="statement not covered" > const savedTokens = await TokenStorage.getTokens();</span>
2093
+ <span class="cstat-no" title="statement not covered" > if (!savedTokens) {</span>
2094
+ <span class="cstat-no" title="statement not covered" > this.log("No valid tokens found in storage");</span>
2095
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2096
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2097
+ <span class="cstat-no" title="statement not covered" > }</span>
2098
+ <span class="cstat-no" title="statement not covered" ></span>
2099
+ <span class="cstat-no" title="statement not covered" > // Validar que refresh token no esté expirado</span>
2100
+ <span class="cstat-no" title="statement not covered" > if (Date.now() &gt;= savedTokens.refreshExpiresAt) {</span>
2101
+ <span class="cstat-no" title="statement not covered" > this.log("Refresh token expired, clearing storage (cold start - no notification)");</span>
2102
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2103
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2104
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: NO emitir SESSION_EXPIRED - el usuario simplemente estuvo mucho tiempo fuera</span>
2105
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2106
+ <span class="cstat-no" title="statement not covered" > }</span>
2107
+ <span class="cstat-no" title="statement not covered" ></span>
2108
+ <span class="cstat-no" title="statement not covered" > // Configurar tokens en crudify</span>
2109
+ <span class="cstat-no" title="statement not covered" > crudify.setTokens({</span>
2110
+ <span class="cstat-no" title="statement not covered" > accessToken: savedTokens.accessToken,</span>
2111
+ <span class="cstat-no" title="statement not covered" > refreshToken: savedTokens.refreshToken,</span>
2112
+ <span class="cstat-no" title="statement not covered" > expiresAt: savedTokens.expiresAt,</span>
2113
+ <span class="cstat-no" title="statement not covered" > refreshExpiresAt: savedTokens.refreshExpiresAt,</span>
2114
+ <span class="cstat-no" title="statement not covered" > });</span>
2115
+ <span class="cstat-no" title="statement not covered" ></span>
2116
+ <span class="cstat-no" title="statement not covered" > // Validar con método mejorado</span>
2117
+ <span class="cstat-no" title="statement not covered" > const tokenInfo = crudify.getTokenData();</span>
2118
+ <span class="cstat-no" title="statement not covered" ></span>
2119
+ <span class="cstat-no" title="statement not covered" > if (tokenInfo.isValid === false) {</span>
2120
+ <span class="cstat-no" title="statement not covered" > this.log("Restored access token is invalid or expired");</span>
2121
+ <span class="cstat-no" title="statement not covered" ></span>
2122
+ <span class="cstat-no" title="statement not covered" > // Si access token expiró pero refresh es válido, intentar refresh silencioso</span>
2123
+ <span class="cstat-no" title="statement not covered" > if (Date.now() &lt; savedTokens.refreshExpiresAt) {</span>
2124
+ <span class="cstat-no" title="statement not covered" > this.log("Access token expired but refresh is valid, attempting silent refresh...");</span>
2125
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Usar refresh SILENCIOSO para cold start (sin notificaciones)</span>
2126
+ <span class="cstat-no" title="statement not covered" > const refreshSuccess = await this.refreshTokensSilent();</span>
2127
+ <span class="cstat-no" title="statement not covered" ></span>
2128
+ <span class="cstat-no" title="statement not covered" > if (refreshSuccess) {</span>
2129
+ <span class="cstat-no" title="statement not covered" > this.log("Session restored successfully via silent token refresh");</span>
2130
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = true; // ✅ Ahora la sesión está activa</span>
2131
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2132
+ <span class="cstat-no" title="statement not covered" > const newTokens = await TokenStorage.getTokens();</span>
2133
+ <span class="cstat-no" title="statement not covered" > if (newTokens) {</span>
2134
+ <span class="cstat-no" title="statement not covered" > this.config.onSessionRestored?.(newTokens);</span>
2135
+ <span class="cstat-no" title="statement not covered" > }</span>
2136
+ <span class="cstat-no" title="statement not covered" > return true;</span>
2137
+ <span class="cstat-no" title="statement not covered" > }</span>
2138
+ <span class="cstat-no" title="statement not covered" > }</span>
2139
+ <span class="cstat-no" title="statement not covered" ></span>
2140
+ <span class="cstat-no" title="statement not covered" > // Si no se pudo refresh, limpiar todo SIN emitir eventos</span>
2141
+ <span class="cstat-no" title="statement not covered" > this.log("Could not restore session, clearing tokens (cold start - no notification)");</span>
2142
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2143
+ <span class="cstat-no" title="statement not covered" > await crudify.logout();</span>
2144
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2145
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: NO llamar a onSessionExpired ni mostrar notificación</span>
2146
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2147
+ <span class="cstat-no" title="statement not covered" > }</span>
2148
+ <span class="cstat-no" title="statement not covered" ></span>
2149
+ <span class="cstat-no" title="statement not covered" > this.log("Session restored successfully");</span>
2150
+ <span class="cstat-no" title="statement not covered" ></span>
2151
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Marcar sesión como activa después de restauración exitosa</span>
2152
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = true;</span>
2153
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2154
+ <span class="cstat-no" title="statement not covered" ></span>
2155
+ <span class="cstat-no" title="statement not covered" > // Actualizar última actividad al restaurar sesión</span>
2156
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = Date.now();</span>
2157
+ <span class="cstat-no" title="statement not covered" ></span>
2158
+ <span class="cstat-no" title="statement not covered" > // Callback de restauración</span>
2159
+ <span class="cstat-no" title="statement not covered" > this.config.onSessionRestored?.(savedTokens);</span>
2160
+ <span class="cstat-no" title="statement not covered" ></span>
2161
+ <span class="cstat-no" title="statement not covered" > return true;</span>
2162
+ <span class="cstat-no" title="statement not covered" > } catch (error) {</span>
2163
+ <span class="cstat-no" title="statement not covered" > this.log("Session restore error", { error: error instanceof Error ? error.message : String(error) });</span>
2164
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens(); // Limpiar tokens corruptos</span>
2165
+ <span class="cstat-no" title="statement not covered" > await crudify.logout();</span>
2166
+ <span class="cstat-no" title="statement not covered" > this.isColdStartRestore = false;</span>
2167
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: NO emitir eventos en caso de error durante cold start</span>
2168
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2169
+ <span class="cstat-no" title="statement not covered" > }</span>
2170
+ <span class="cstat-no" title="statement not covered" > }</span>
2171
+ &nbsp;
2172
+ /**
2173
+ * Verificar si el usuario está autenticado
2174
+ */
2175
+ <span class="fstat-no" title="function not covered" > public async isAuthenticated(): Promise&lt;boolean&gt; {</span>
2176
+ <span class="cstat-no" title="statement not covered" > return crudify.isLogin() || (await TokenStorage.hasValidTokens());</span>
2177
+ <span class="cstat-no" title="statement not covered" > }</span>
2178
+ &nbsp;
2179
+ /**
2180
+ * Obtener información de tokens actuales
2181
+ */
2182
+ <span class="fstat-no" title="function not covered" > public async getTokenInfo() {</span>
2183
+ <span class="cstat-no" title="statement not covered" > const crudifyTokens = crudify.getTokenData();</span>
2184
+ <span class="cstat-no" title="statement not covered" > const storageInfo = await TokenStorage.getExpirationInfo();</span>
2185
+ <span class="cstat-no" title="statement not covered" > const savedTokens = await TokenStorage.getTokens();</span>
2186
+ <span class="cstat-no" title="statement not covered" ></span>
2187
+ <span class="cstat-no" title="statement not covered" > return {</span>
2188
+ <span class="cstat-no" title="statement not covered" > isLoggedIn: await this.isAuthenticated(),</span>
2189
+ <span class="cstat-no" title="statement not covered" > crudifyTokens,</span>
2190
+ <span class="cstat-no" title="statement not covered" > storageInfo,</span>
2191
+ <span class="cstat-no" title="statement not covered" > hasValidTokens: await TokenStorage.hasValidTokens(),</span>
2192
+ <span class="cstat-no" title="statement not covered" > // Admin API credentials from storage</span>
2193
+ <span class="cstat-no" title="statement not covered" > apiEndpointAdmin: savedTokens?.apiEndpointAdmin,</span>
2194
+ <span class="cstat-no" title="statement not covered" > apiKeyEndpointAdmin: savedTokens?.apiKeyEndpointAdmin,</span>
2195
+ <span class="cstat-no" title="statement not covered" > };</span>
2196
+ <span class="cstat-no" title="statement not covered" > }</span>
2197
+ &nbsp;
2198
+ /**
2199
+ * ✅ FASE 4: Refrescar tokens con protección contra concurrencia
2200
+ */
2201
+ <span class="fstat-no" title="function not covered" > public async refreshTokens(): Promise&lt;boolean&gt; {</span>
2202
+ <span class="cstat-no" title="statement not covered" > // ✅ PROTECCIÓN 1: Si ya hay refresh en progreso, retornar la promesa existente</span>
2203
+ <span class="cstat-no" title="statement not covered" > if (this.isRefreshingLocally &amp;&amp; this.refreshPromise) {</span>
2204
+ <span class="cstat-no" title="statement not covered" > this.log("Refresh already in progress, waiting for existing promise...");</span>
2205
+ <span class="cstat-no" title="statement not covered" > return this.refreshPromise;</span>
2206
+ <span class="cstat-no" title="statement not covered" > }</span>
2207
+ <span class="cstat-no" title="statement not covered" ></span>
2208
+ <span class="cstat-no" title="statement not covered" > // ✅ PROTECCIÓN 2: Marcar como en progreso</span>
2209
+ <span class="cstat-no" title="statement not covered" > this.isRefreshingLocally = true;</span>
2210
+ <span class="cstat-no" title="statement not covered" ></span>
2211
+ <span class="cstat-no" title="statement not covered" > // ✅ PROTECCIÓN 3: Crear y guardar la promesa</span>
2212
+ <span class="cstat-no" title="statement not covered" > this.refreshPromise = this._performRefresh();</span>
2213
+ <span class="cstat-no" title="statement not covered" ></span>
2214
+ <span class="cstat-no" title="statement not covered" > try {</span>
2215
+ <span class="cstat-no" title="statement not covered" > const result = await this.refreshPromise;</span>
2216
+ <span class="cstat-no" title="statement not covered" > return result;</span>
2217
+ <span class="cstat-no" title="statement not covered" > } finally {</span>
2218
+ <span class="cstat-no" title="statement not covered" > // ✅ PROTECCIÓN 4: Limpiar estado al finalizar</span>
2219
+ <span class="cstat-no" title="statement not covered" > this.isRefreshingLocally = false;</span>
2220
+ <span class="cstat-no" title="statement not covered" > this.refreshPromise = null;</span>
2221
+ <span class="cstat-no" title="statement not covered" > }</span>
2222
+ <span class="cstat-no" title="statement not covered" > }</span>
2223
+ &nbsp;
2224
+ /**
2225
+ * ✅ FASE 4 + FASE 5: Método privado que realiza el refresh real
2226
+ * Solo muestra notificación si había una sesión activa (no en cold start)
2227
+ */
2228
+ <span class="fstat-no" title="function not covered" > private async _performRefresh(): Promise&lt;boolean&gt; {</span>
2229
+ <span class="cstat-no" title="statement not covered" > try {</span>
2230
+ <span class="cstat-no" title="statement not covered" > this.log("Starting token refresh...");</span>
2231
+ <span class="cstat-no" title="statement not covered" ></span>
2232
+ <span class="cstat-no" title="statement not covered" > const response = await crudify.refreshAccessToken();</span>
2233
+ <span class="cstat-no" title="statement not covered" ></span>
2234
+ <span class="cstat-no" title="statement not covered" > if (!response.success) {</span>
2235
+ <span class="cstat-no" title="statement not covered" > this.log("Token refresh failed", { errors: response.errors });</span>
2236
+ <span class="cstat-no" title="statement not covered" ></span>
2237
+ <span class="cstat-no" title="statement not covered" > // Si refresh falla, limpiar todo</span>
2238
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2239
+ <span class="cstat-no" title="statement not covered" ></span>
2240
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Solo mostrar notificación si había sesión activa (no en cold start)</span>
2241
+ <span class="cstat-no" title="statement not covered" > if (this.isSessionActive &amp;&amp; !this.isColdStartRestore) {</span>
2242
+ <span class="cstat-no" title="statement not covered" > this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");</span>
2243
+ <span class="cstat-no" title="statement not covered" > }</span>
2244
+ <span class="cstat-no" title="statement not covered" ></span>
2245
+ <span class="cstat-no" title="statement not covered" > this.config.onSessionExpired?.();</span>
2246
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = false;</span>
2247
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2248
+ <span class="cstat-no" title="statement not covered" > }</span>
2249
+ <span class="cstat-no" title="statement not covered" ></span>
2250
+ <span class="cstat-no" title="statement not covered" > // Actualizar storage con nuevos tokens</span>
2251
+ <span class="cstat-no" title="statement not covered" > const refreshData = response.data as LoginResponseData;</span>
2252
+ <span class="cstat-no" title="statement not covered" > const newTokens: TokenData = {</span>
2253
+ <span class="cstat-no" title="statement not covered" > accessToken: refreshData.token,</span>
2254
+ <span class="cstat-no" title="statement not covered" > refreshToken: refreshData.refreshToken,</span>
2255
+ <span class="cstat-no" title="statement not covered" > expiresAt: refreshData.expiresAt,</span>
2256
+ <span class="cstat-no" title="statement not covered" > refreshExpiresAt: refreshData.refreshExpiresAt,</span>
2257
+ <span class="cstat-no" title="statement not covered" > };</span>
2258
+ <span class="cstat-no" title="statement not covered" ></span>
2259
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.saveTokens(newTokens);</span>
2260
+ <span class="cstat-no" title="statement not covered" > this.log("Tokens refreshed and saved successfully");</span>
2261
+ <span class="cstat-no" title="statement not covered" ></span>
2262
+ <span class="cstat-no" title="statement not covered" > // Actualizar cookie cross-subdomain con nuevo token</span>
2263
+ <span class="cstat-no" title="statement not covered" > CookieSync.setCrossSubdomainCookie(newTokens.accessToken, newTokens.expiresAt, this.config.env);</span>
2264
+ <span class="cstat-no" title="statement not covered" ></span>
2265
+ <span class="cstat-no" title="statement not covered" > // Actualizar última actividad al renovar tokens</span>
2266
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = Date.now();</span>
2267
+ <span class="cstat-no" title="statement not covered" ></span>
2268
+ <span class="cstat-no" title="statement not covered" > // Notificar a otras pestañas del refresh de tokens</span>
2269
+ <span class="cstat-no" title="statement not covered" > crossTabSync.notifyTokenRefreshed();</span>
2270
+ <span class="cstat-no" title="statement not covered" ></span>
2271
+ <span class="cstat-no" title="statement not covered" > return true;</span>
2272
+ <span class="cstat-no" title="statement not covered" > } catch (error) {</span>
2273
+ <span class="cstat-no" title="statement not covered" > this.log("Token refresh error", { error: error instanceof Error ? error.message : String(error) });</span>
2274
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2275
+ <span class="cstat-no" title="statement not covered" ></span>
2276
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 5: Solo mostrar notificación si había sesión activa (no en cold start)</span>
2277
+ <span class="cstat-no" title="statement not covered" > if (this.isSessionActive &amp;&amp; !this.isColdStartRestore) {</span>
2278
+ <span class="cstat-no" title="statement not covered" > this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");</span>
2279
+ <span class="cstat-no" title="statement not covered" > }</span>
2280
+ <span class="cstat-no" title="statement not covered" ></span>
2281
+ <span class="cstat-no" title="statement not covered" > this.config.onSessionExpired?.();</span>
2282
+ <span class="cstat-no" title="statement not covered" > this.isSessionActive = false;</span>
2283
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2284
+ <span class="cstat-no" title="statement not covered" > }</span>
2285
+ <span class="cstat-no" title="statement not covered" > }</span>
2286
+ &nbsp;
2287
+ /**
2288
+ * ✅ FASE 4: Verificar si hay un refresh en progreso
2289
+ */
2290
+ public <span class="fstat-no" title="function not covered" >isRefreshing(): boolean {</span>
2291
+ <span class="cstat-no" title="statement not covered" > return this.isRefreshingLocally;</span>
2292
+ <span class="cstat-no" title="statement not covered" > }</span>
2293
+ &nbsp;
2294
+ /**
2295
+ * ✅ FASE 5: Refrescar tokens de forma silenciosa (sin notificaciones ni eventos)
2296
+ * Usado durante cold start restore para no mostrar mensajes inapropiados
2297
+ */
2298
+ <span class="fstat-no" title="function not covered" > public async refreshTokensSilent(): Promise&lt;boolean&gt; {</span>
2299
+ <span class="cstat-no" title="statement not covered" > // PROTECCIÓN: Si ya hay refresh en progreso, retornar la promesa existente</span>
2300
+ <span class="cstat-no" title="statement not covered" > if (this.isRefreshingLocally &amp;&amp; this.refreshPromise) {</span>
2301
+ <span class="cstat-no" title="statement not covered" > this.log("Silent refresh: waiting for existing refresh promise...");</span>
2302
+ <span class="cstat-no" title="statement not covered" > return this.refreshPromise;</span>
2303
+ <span class="cstat-no" title="statement not covered" > }</span>
2304
+ <span class="cstat-no" title="statement not covered" ></span>
2305
+ <span class="cstat-no" title="statement not covered" > this.isRefreshingLocally = true;</span>
2306
+ <span class="cstat-no" title="statement not covered" > this.refreshPromise = this._performRefreshSilent();</span>
2307
+ <span class="cstat-no" title="statement not covered" ></span>
2308
+ <span class="cstat-no" title="statement not covered" > try {</span>
2309
+ <span class="cstat-no" title="statement not covered" > return await this.refreshPromise;</span>
2310
+ <span class="cstat-no" title="statement not covered" > } finally {</span>
2311
+ <span class="cstat-no" title="statement not covered" > this.isRefreshingLocally = false;</span>
2312
+ <span class="cstat-no" title="statement not covered" > this.refreshPromise = null;</span>
2313
+ <span class="cstat-no" title="statement not covered" > }</span>
2314
+ <span class="cstat-no" title="statement not covered" > }</span>
2315
+ &nbsp;
2316
+ /**
2317
+ * ✅ FASE 5: Refresh interno sin notificaciones ni eventos SESSION_EXPIRED
2318
+ * Solo se usa durante cold start restore
2319
+ */
2320
+ <span class="fstat-no" title="function not covered" > private async _performRefreshSilent(): Promise&lt;boolean&gt; {</span>
2321
+ <span class="cstat-no" title="statement not covered" > try {</span>
2322
+ <span class="cstat-no" title="statement not covered" > this.log("Starting silent token refresh (cold start)...");</span>
2323
+ <span class="cstat-no" title="statement not covered" ></span>
2324
+ <span class="cstat-no" title="statement not covered" > const response = await crudify.refreshAccessToken();</span>
2325
+ <span class="cstat-no" title="statement not covered" ></span>
2326
+ <span class="cstat-no" title="statement not covered" > if (!response.success) {</span>
2327
+ <span class="cstat-no" title="statement not covered" > this.log("Silent token refresh failed", { errors: response.errors });</span>
2328
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2329
+ <span class="cstat-no" title="statement not covered" > // ✅ NO emitir notificación ni onSessionExpired - cold start silencioso</span>
2330
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2331
+ <span class="cstat-no" title="statement not covered" > }</span>
2332
+ <span class="cstat-no" title="statement not covered" ></span>
2333
+ <span class="cstat-no" title="statement not covered" > // Actualizar storage con nuevos tokens</span>
2334
+ <span class="cstat-no" title="statement not covered" > const refreshData = response.data as LoginResponseData;</span>
2335
+ <span class="cstat-no" title="statement not covered" > const newTokens: TokenData = {</span>
2336
+ <span class="cstat-no" title="statement not covered" > accessToken: refreshData.token,</span>
2337
+ <span class="cstat-no" title="statement not covered" > refreshToken: refreshData.refreshToken,</span>
2338
+ <span class="cstat-no" title="statement not covered" > expiresAt: refreshData.expiresAt,</span>
2339
+ <span class="cstat-no" title="statement not covered" > refreshExpiresAt: refreshData.refreshExpiresAt,</span>
2340
+ <span class="cstat-no" title="statement not covered" > };</span>
2341
+ <span class="cstat-no" title="statement not covered" ></span>
2342
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.saveTokens(newTokens);</span>
2343
+ <span class="cstat-no" title="statement not covered" > this.log("Tokens refreshed silently and saved successfully");</span>
2344
+ <span class="cstat-no" title="statement not covered" ></span>
2345
+ <span class="cstat-no" title="statement not covered" > // Actualizar cookie cross-subdomain con nuevo token</span>
2346
+ <span class="cstat-no" title="statement not covered" > CookieSync.setCrossSubdomainCookie(newTokens.accessToken, newTokens.expiresAt, this.config.env);</span>
2347
+ <span class="cstat-no" title="statement not covered" ></span>
2348
+ <span class="cstat-no" title="statement not covered" > // Actualizar última actividad</span>
2349
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = Date.now();</span>
2350
+ <span class="cstat-no" title="statement not covered" ></span>
2351
+ <span class="cstat-no" title="statement not covered" > // Notificar a otras pestañas del refresh de tokens</span>
2352
+ <span class="cstat-no" title="statement not covered" > crossTabSync.notifyTokenRefreshed();</span>
2353
+ <span class="cstat-no" title="statement not covered" ></span>
2354
+ <span class="cstat-no" title="statement not covered" > return true;</span>
2355
+ <span class="cstat-no" title="statement not covered" > } catch (error) {</span>
2356
+ <span class="cstat-no" title="statement not covered" > this.log("Silent token refresh error", { error: error instanceof Error ? error.message : String(error) });</span>
2357
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2358
+ <span class="cstat-no" title="statement not covered" > // ✅ NO emitir notificación ni evento - cold start silencioso</span>
2359
+ <span class="cstat-no" title="statement not covered" > return false;</span>
2360
+ <span class="cstat-no" title="statement not covered" > }</span>
2361
+ <span class="cstat-no" title="statement not covered" > }</span>
2362
+ &nbsp;
2363
+ /**
2364
+ * ✅ FASE 5: Verificar si la sesión está activa (usuario logueado y usando la app)
2365
+ */
2366
+ public <span class="fstat-no" title="function not covered" >getIsSessionActive(): boolean {</span>
2367
+ <span class="cstat-no" title="statement not covered" > return this.isSessionActive;</span>
2368
+ <span class="cstat-no" title="statement not covered" > }</span>
2369
+ &nbsp;
2370
+ /**
2371
+ * ✅ MEJORADO FASE 3: Configurar interceptor de respuesta NON-BLOCKING
2372
+ * El interceptor NO debe hacer await, solo detectar y emitir eventos
2373
+ */
2374
+ public <span class="fstat-no" title="function not covered" >setupResponseInterceptor(): void {</span>
2375
+ <span class="cstat-no" title="statement not covered" > crudify.setResponseInterceptor(async (response) =&gt; {</span>
2376
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 2: Actualizar actividad en cada llamada (tanto exitosa como fallida, indica uso)</span>
2377
+ <span class="cstat-no" title="statement not covered" > this.updateLastActivity();</span>
2378
+ <span class="cstat-no" title="statement not covered" ></span>
2379
+ <span class="cstat-no" title="statement not covered" > // ✅ FASE 3.1: Detectar errores de autorización con detección mejorada</span>
2380
+ <span class="cstat-no" title="statement not covered" > const authError = this.detectAuthorizationError(response);</span>
2381
+ <span class="cstat-no" title="statement not covered" ></span>
2382
+ <span class="cstat-no" title="statement not covered" > if (authError.isAuthError) {</span>
2383
+ <span class="cstat-no" title="statement not covered" > this.log("🚨 Authorization error detected:", {</span>
2384
+ <span class="cstat-no" title="statement not covered" > errorType: authError.errorType,</span>
2385
+ <span class="cstat-no" title="statement not covered" > shouldLogout: authError.shouldTriggerLogout,</span>
2386
+ <span class="cstat-no" title="statement not covered" > });</span>
2387
+ <span class="cstat-no" title="statement not covered" ></span>
2388
+ <span class="cstat-no" title="statement not covered" > // ✅ CASO 1: Refresh token inválido → Logout inmediato</span>
2389
+ <span class="cstat-no" title="statement not covered" > if (authError.isRefreshTokenInvalid || authError.isTokenRefreshFailed) {</span>
2390
+ <span class="cstat-no" title="statement not covered" > this.log("Refresh token invalid, emitting TOKEN_REFRESH_FAILED event");</span>
2391
+ <span class="cstat-no" title="statement not covered" > authEventBus.emit("TOKEN_REFRESH_FAILED", {</span>
2392
+ <span class="cstat-no" title="statement not covered" > message: authError.userFriendlyMessage,</span>
2393
+ <span class="cstat-no" title="statement not covered" > error: authError.errorDetails,</span>
2394
+ <span class="cstat-no" title="statement not covered" > source: "SessionManager.setupResponseInterceptor",</span>
2395
+ <span class="cstat-no" title="statement not covered" > });</span>
2396
+ <span class="cstat-no" title="statement not covered" > return response; // ← No bloquear</span>
2397
+ <span class="cstat-no" title="statement not covered" > }</span>
2398
+ <span class="cstat-no" title="statement not covered" ></span>
2399
+ <span class="cstat-no" title="statement not covered" > // ✅ CASO 2: Access token expirado → Intentar refresh</span>
2400
+ <span class="cstat-no" title="statement not covered" > if (authError.shouldTriggerLogout) {</span>
2401
+ <span class="cstat-no" title="statement not covered" > if ((await TokenStorage.hasValidTokens()) &amp;&amp; !authError.isIrrecoverable) {</span>
2402
+ <span class="cstat-no" title="statement not covered" > // ❌ NO HACER: await this.refreshTokens()</span>
2403
+ <span class="cstat-no" title="statement not covered" > // ✅ HACER: Emitir evento para que useSession maneje</span>
2404
+ <span class="cstat-no" title="statement not covered" > this.log("Access token expired, emitting TOKEN_EXPIRED event");</span>
2405
+ <span class="cstat-no" title="statement not covered" > authEventBus.emit("TOKEN_EXPIRED", {</span>
2406
+ <span class="cstat-no" title="statement not covered" > message: "Access token expired, refresh needed",</span>
2407
+ <span class="cstat-no" title="statement not covered" > error: authError.errorDetails,</span>
2408
+ <span class="cstat-no" title="statement not covered" > source: "SessionManager.setupResponseInterceptor",</span>
2409
+ <span class="cstat-no" title="statement not covered" > });</span>
2410
+ <span class="cstat-no" title="statement not covered" > } else {</span>
2411
+ <span class="cstat-no" title="statement not covered" > // ✅ CASO 3: No hay tokens válidos o error irrecuperable</span>
2412
+ <span class="cstat-no" title="statement not covered" > this.log("No valid tokens or irrecoverable error, emitting SESSION_EXPIRED event");</span>
2413
+ <span class="cstat-no" title="statement not covered" > authEventBus.emit("SESSION_EXPIRED", {</span>
2414
+ <span class="cstat-no" title="statement not covered" > message: authError.userFriendlyMessage,</span>
2415
+ <span class="cstat-no" title="statement not covered" > error: authError.errorDetails,</span>
2416
+ <span class="cstat-no" title="statement not covered" > source: "SessionManager.setupResponseInterceptor",</span>
2417
+ <span class="cstat-no" title="statement not covered" > });</span>
2418
+ <span class="cstat-no" title="statement not covered" > }</span>
2419
+ <span class="cstat-no" title="statement not covered" > }</span>
2420
+ <span class="cstat-no" title="statement not covered" > }</span>
2421
+ <span class="cstat-no" title="statement not covered" ></span>
2422
+ <span class="cstat-no" title="statement not covered" > // ✅ IMPORTANTE: Retornar inmediatamente, NO bloquear con await</span>
2423
+ <span class="cstat-no" title="statement not covered" > return response;</span>
2424
+ <span class="cstat-no" title="statement not covered" > });</span>
2425
+ <span class="cstat-no" title="statement not covered" ></span>
2426
+ <span class="cstat-no" title="statement not covered" > this.log("Response interceptor configured (non-blocking mode)");</span>
2427
+ <span class="cstat-no" title="statement not covered" > }</span>
2428
+ &nbsp;
2429
+ /**
2430
+ * Ensure crudify SDK is initialized
2431
+ * Thread-safe: multiple calls will wait for the same initialization
2432
+ */
2433
+ <span class="fstat-no" title="function not covered" > private async ensureCrudifyInitialized(): Promise&lt;void&gt; {</span>
2434
+ <span class="cstat-no" title="statement not covered" > if (this.crudifyInitialized) return;</span>
2435
+ <span class="cstat-no" title="statement not covered" ></span>
2436
+ <span class="cstat-no" title="statement not covered" > try {</span>
2437
+ <span class="cstat-no" title="statement not covered" > this.log("Initializing crudify SDK...");</span>
2438
+ <span class="cstat-no" title="statement not covered" ></span>
2439
+ <span class="cstat-no" title="statement not covered" > // Check if already initialized by another service</span>
2440
+ <span class="cstat-no" title="statement not covered" > const tokenData = crudify.getTokenData() as CrudifyTokenData | null;</span>
2441
+ <span class="cstat-no" title="statement not covered" > if (tokenData &amp;&amp; tokenData.endpoint) {</span>
2442
+ <span class="cstat-no" title="statement not covered" > this.log("Crudify already initialized by another service");</span>
2443
+ <span class="cstat-no" title="statement not covered" > this.crudifyInitialized = true;</span>
2444
+ <span class="cstat-no" title="statement not covered" > return;</span>
2445
+ <span class="cstat-no" title="statement not covered" > }</span>
2446
+ <span class="cstat-no" title="statement not covered" ></span>
2447
+ <span class="cstat-no" title="statement not covered" > // Configure environment</span>
2448
+ <span class="cstat-no" title="statement not covered" > const env = this.config.env || "stg";</span>
2449
+ <span class="cstat-no" title="statement not covered" > crudify.config(env);</span>
2450
+ <span class="cstat-no" title="statement not covered" ></span>
2451
+ <span class="cstat-no" title="statement not covered" > // Initialize with API key</span>
2452
+ <span class="cstat-no" title="statement not covered" > const apiKey = this.config.publicApiKey!;</span>
2453
+ <span class="cstat-no" title="statement not covered" > const logLevel = this.config.enableLogging ? "debug" : "none";</span>
2454
+ <span class="cstat-no" title="statement not covered" ></span>
2455
+ <span class="cstat-no" title="statement not covered" > const response = (await crudify.init(apiKey, logLevel)) as CrudifyInitResponse;</span>
2456
+ <span class="cstat-no" title="statement not covered" ></span>
2457
+ <span class="cstat-no" title="statement not covered" > // Check if initialization failed (only check for explicit failure)</span>
2458
+ <span class="cstat-no" title="statement not covered" > // crudify.init() returns:</span>
2459
+ <span class="cstat-no" title="statement not covered" > // - { data: {...} } on first successful init</span>
2460
+ <span class="cstat-no" title="statement not covered" > // - { apiEndpointAdmin, apiKeyEndpointAdmin } when already initialized</span>
2461
+ <span class="cstat-no" title="statement not covered" > // - { success: false, errors: [...] } on error</span>
2462
+ <span class="cstat-no" title="statement not covered" > if (response &amp;&amp; response.success === false &amp;&amp; response.errors) {</span>
2463
+ <span class="cstat-no" title="statement not covered" > throw new Error(`Failed to initialize crudify: ${JSON.stringify(response.errors)}`);</span>
2464
+ <span class="cstat-no" title="statement not covered" > }</span>
2465
+ <span class="cstat-no" title="statement not covered" ></span>
2466
+ <span class="cstat-no" title="statement not covered" > this.crudifyInitialized = true;</span>
2467
+ <span class="cstat-no" title="statement not covered" > this.log("Crudify SDK initialized successfully");</span>
2468
+ <span class="cstat-no" title="statement not covered" > } catch (error: unknown) {</span>
2469
+ <span class="cstat-no" title="statement not covered" > logger.error("[SessionManager] Failed to initialize crudify", error instanceof Error ? error : { message: String(error) });</span>
2470
+ <span class="cstat-no" title="statement not covered" > throw error;</span>
2471
+ <span class="cstat-no" title="statement not covered" > }</span>
2472
+ <span class="cstat-no" title="statement not covered" > }</span>
2473
+ &nbsp;
2474
+ /**
2475
+ * ✅ FASE 3.1: Detectar errores de autorización en todos los formatos posibles (mejorado)
2476
+ *
2477
+ * JUSTIFICACIÓN PARA `any`:
2478
+ * El backend retorna respuestas en 5 formatos incompatibles:
2479
+ * 1. GraphQL Array: { errors: [{ errorType?, message?, extensions.code? }] }
2480
+ * 2. GraphQL Object: { errors: { fieldName: [messages] } }
2481
+ * 3. REST Status: { data: { response: { status } } }
2482
+ * 4. Parsed JSON: { data: { response: { data: JSON string } } }
2483
+ * 5. Error Code: { errorCode: "CODE" }
2484
+ *
2485
+ * @param response - API response with dynamic structure that varies by error format
2486
+ */
2487
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- 5 formatos API incompatibles; ver JSDoc
2488
+ private <span class="fstat-no" title="function not covered" >detectAuthorizationError(response: any): {</span>
2489
+ <span class="cstat-no" title="statement not covered" > isAuthError: boolean;</span>
2490
+ <span class="cstat-no" title="statement not covered" > isRefreshTokenInvalid: boolean;</span>
2491
+ <span class="cstat-no" title="statement not covered" > isTokenRefreshFailed: boolean;</span>
2492
+ <span class="cstat-no" title="statement not covered" > isTokenExpired: boolean;</span>
2493
+ <span class="cstat-no" title="statement not covered" > isUnauthorized: boolean;</span>
2494
+ <span class="cstat-no" title="statement not covered" > isIrrecoverable: boolean;</span>
2495
+ <span class="cstat-no" title="statement not covered" > shouldTriggerLogout: boolean;</span>
2496
+ <span class="cstat-no" title="statement not covered" > errorType: string;</span>
2497
+ <span class="cstat-no" title="statement not covered" > errorDetails: unknown;</span>
2498
+ <span class="cstat-no" title="statement not covered" > userFriendlyMessage: string;</span>
2499
+ <span class="cstat-no" title="statement not covered" > } {</span>
2500
+ <span class="cstat-no" title="statement not covered" > const result = {</span>
2501
+ <span class="cstat-no" title="statement not covered" > isAuthError: false,</span>
2502
+ <span class="cstat-no" title="statement not covered" > isRefreshTokenInvalid: false,</span>
2503
+ <span class="cstat-no" title="statement not covered" > isTokenRefreshFailed: false,</span>
2504
+ <span class="cstat-no" title="statement not covered" > isTokenExpired: false,</span>
2505
+ <span class="cstat-no" title="statement not covered" > isUnauthorized: false,</span>
2506
+ <span class="cstat-no" title="statement not covered" > isIrrecoverable: false,</span>
2507
+ <span class="cstat-no" title="statement not covered" > shouldTriggerLogout: false,</span>
2508
+ <span class="cstat-no" title="statement not covered" > errorType: "",</span>
2509
+ <span class="cstat-no" title="statement not covered" > errorDetails: null as unknown,</span>
2510
+ <span class="cstat-no" title="statement not covered" > userFriendlyMessage: "",</span>
2511
+ <span class="cstat-no" title="statement not covered" > };</span>
2512
+ <span class="cstat-no" title="statement not covered" ></span>
2513
+ <span class="cstat-no" title="statement not covered" > // FORMATO 1: response.errors (errores GraphQL Array)</span>
2514
+ <span class="cstat-no" title="statement not covered" > if (response.errors &amp;&amp; Array.isArray(response.errors)) {</span>
2515
+ <span class="cstat-no" title="statement not covered" > // eslint-disable-next-line @typescript-eslint/no-explicit-any -- GraphQL error: { errorType?, message?, extensions.code? }</span>
2516
+ <span class="cstat-no" title="statement not covered" > const authError = response.errors.find(</span>
2517
+ <span class="cstat-no" title="statement not covered" > (error: any) =&gt;</span>
2518
+ <span class="cstat-no" title="statement not covered" > error.errorType === "Unauthorized" ||</span>
2519
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("Unauthorized") ||</span>
2520
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("Not Authorized") ||</span>
2521
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("NOT_AUTHORIZED") ||</span>
2522
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("Token") ||</span>
2523
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("TOKEN") ||</span>
2524
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("Authentication") ||</span>
2525
+ <span class="cstat-no" title="statement not covered" > error.message?.includes("UNAUTHENTICATED") ||</span>
2526
+ <span class="cstat-no" title="statement not covered" > error.extensions?.code === "UNAUTHENTICATED" ||</span>
2527
+ <span class="cstat-no" title="statement not covered" > error.extensions?.code === "FORBIDDEN"</span>
2528
+ <span class="cstat-no" title="statement not covered" > );</span>
2529
+ <span class="cstat-no" title="statement not covered" ></span>
2530
+ <span class="cstat-no" title="statement not covered" > if (authError) {</span>
2531
+ <span class="cstat-no" title="statement not covered" > result.isAuthError = true;</span>
2532
+ <span class="cstat-no" title="statement not covered" > result.errorType = "GraphQL Array";</span>
2533
+ <span class="cstat-no" title="statement not covered" > result.errorDetails = authError;</span>
2534
+ <span class="cstat-no" title="statement not covered" > result.shouldTriggerLogout = true;</span>
2535
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2536
+ <span class="cstat-no" title="statement not covered" ></span>
2537
+ <span class="cstat-no" title="statement not covered" > if (authError.message?.includes("TOKEN") || authError.message?.includes("Token")) {</span>
2538
+ <span class="cstat-no" title="statement not covered" > result.isTokenExpired = true;</span>
2539
+ <span class="cstat-no" title="statement not covered" > }</span>
2540
+ <span class="cstat-no" title="statement not covered" > if (authError.extensions?.code === "UNAUTHENTICATED") {</span>
2541
+ <span class="cstat-no" title="statement not covered" > result.isUnauthorized = true;</span>
2542
+ <span class="cstat-no" title="statement not covered" > }</span>
2543
+ <span class="cstat-no" title="statement not covered" > }</span>
2544
+ <span class="cstat-no" title="statement not covered" > }</span>
2545
+ <span class="cstat-no" title="statement not covered" ></span>
2546
+ <span class="cstat-no" title="statement not covered" > // FORMATO 2: response.errors (errores GraphQL Object)</span>
2547
+ <span class="cstat-no" title="statement not covered" > if (!result.isAuthError &amp;&amp; response.errors &amp;&amp; typeof response.errors === "object" &amp;&amp; !Array.isArray(response.errors)) {</span>
2548
+ <span class="cstat-no" title="statement not covered" > const errorMessages = Object.values(response.errors).flat();</span>
2549
+ <span class="cstat-no" title="statement not covered" > // eslint-disable-next-line @typescript-eslint/no-explicit-any -- GraphQL Object format: field arrays contain mixed types</span>
2550
+ <span class="cstat-no" title="statement not covered" > const authErrorMessage = errorMessages.find(</span>
2551
+ <span class="cstat-no" title="statement not covered" > (message: any) =&gt;</span>
2552
+ <span class="cstat-no" title="statement not covered" > typeof message === "string" &amp;&amp;</span>
2553
+ <span class="cstat-no" title="statement not covered" > (message.includes("NOT_AUTHORIZED") ||</span>
2554
+ <span class="cstat-no" title="statement not covered" > message.includes("TOKEN_REFRESH_FAILED") ||</span>
2555
+ <span class="cstat-no" title="statement not covered" > message.includes("TOKEN_HAS_EXPIRED") ||</span>
2556
+ <span class="cstat-no" title="statement not covered" > message.includes("PLEASE_LOGIN") ||</span>
2557
+ <span class="cstat-no" title="statement not covered" > message.includes("Unauthorized") ||</span>
2558
+ <span class="cstat-no" title="statement not covered" > message.includes("UNAUTHENTICATED") ||</span>
2559
+ <span class="cstat-no" title="statement not covered" > message.includes("SESSION_EXPIRED") ||</span>
2560
+ <span class="cstat-no" title="statement not covered" > message.includes("INVALID_TOKEN"))</span>
2561
+ <span class="cstat-no" title="statement not covered" > );</span>
2562
+ <span class="cstat-no" title="statement not covered" ></span>
2563
+ <span class="cstat-no" title="statement not covered" > if (authErrorMessage &amp;&amp; typeof authErrorMessage === "string") {</span>
2564
+ <span class="cstat-no" title="statement not covered" > result.isAuthError = true;</span>
2565
+ <span class="cstat-no" title="statement not covered" > result.errorType = "GraphQL Object";</span>
2566
+ <span class="cstat-no" title="statement not covered" > result.errorDetails = response.errors;</span>
2567
+ <span class="cstat-no" title="statement not covered" > result.shouldTriggerLogout = true;</span>
2568
+ <span class="cstat-no" title="statement not covered" ></span>
2569
+ <span class="cstat-no" title="statement not covered" > if (authErrorMessage.includes("TOKEN_REFRESH_FAILED")) {</span>
2570
+ <span class="cstat-no" title="statement not covered" > result.isTokenRefreshFailed = true;</span>
2571
+ <span class="cstat-no" title="statement not covered" > result.isRefreshTokenInvalid = true;</span>
2572
+ <span class="cstat-no" title="statement not covered" > result.isIrrecoverable = true;</span>
2573
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha caducado. Por favor, inicia sesión nuevamente.";</span>
2574
+ <span class="cstat-no" title="statement not covered" > } else if (authErrorMessage.includes("TOKEN_HAS_EXPIRED") || authErrorMessage.includes("SESSION_EXPIRED")) {</span>
2575
+ <span class="cstat-no" title="statement not covered" > result.isTokenExpired = true;</span>
2576
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2577
+ <span class="cstat-no" title="statement not covered" > } else if (authErrorMessage.includes("INVALID_TOKEN")) {</span>
2578
+ <span class="cstat-no" title="statement not covered" > result.isTokenExpired = true;</span>
2579
+ <span class="cstat-no" title="statement not covered" > result.isIrrecoverable = true;</span>
2580
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Token inválido. Por favor, inicia sesión nuevamente.";</span>
2581
+ <span class="cstat-no" title="statement not covered" > } else {</span>
2582
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2583
+ <span class="cstat-no" title="statement not covered" > }</span>
2584
+ <span class="cstat-no" title="statement not covered" > }</span>
2585
+ <span class="cstat-no" title="statement not covered" > }</span>
2586
+ <span class="cstat-no" title="statement not covered" ></span>
2587
+ <span class="cstat-no" title="statement not covered" > // FORMATO 3: response.data.response.status</span>
2588
+ <span class="cstat-no" title="statement not covered" > if (!result.isAuthError &amp;&amp; response.data?.response?.status) {</span>
2589
+ <span class="cstat-no" title="statement not covered" > const status = response.data.response.status.toUpperCase();</span>
2590
+ <span class="cstat-no" title="statement not covered" > if (status === "UNAUTHORIZED" || status === "UNAUTHENTICATED") {</span>
2591
+ <span class="cstat-no" title="statement not covered" > result.isAuthError = true;</span>
2592
+ <span class="cstat-no" title="statement not covered" > result.errorType = "Status";</span>
2593
+ <span class="cstat-no" title="statement not covered" > result.errorDetails = response.data.response;</span>
2594
+ <span class="cstat-no" title="statement not covered" > result.isUnauthorized = true;</span>
2595
+ <span class="cstat-no" title="statement not covered" > result.shouldTriggerLogout = true;</span>
2596
+ <span class="cstat-no" title="statement not covered" > result.isIrrecoverable = true;</span>
2597
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2598
+ <span class="cstat-no" title="statement not covered" > }</span>
2599
+ <span class="cstat-no" title="statement not covered" > }</span>
2600
+ <span class="cstat-no" title="statement not covered" ></span>
2601
+ <span class="cstat-no" title="statement not covered" > // FORMATO 4: response.data.response.data (parsed)</span>
2602
+ <span class="cstat-no" title="statement not covered" > if (!result.isAuthError &amp;&amp; response.data?.response?.data) {</span>
2603
+ <span class="cstat-no" title="statement not covered" > try {</span>
2604
+ <span class="cstat-no" title="statement not covered" > const parsedData =</span>
2605
+ <span class="cstat-no" title="statement not covered" > typeof response.data.response.data === "string" ? JSON.parse(response.data.response.data) : response.data.response.data;</span>
2606
+ <span class="cstat-no" title="statement not covered" ></span>
2607
+ <span class="cstat-no" title="statement not covered" > if (parsedData.error === "REFRESH_TOKEN_INVALID" || parsedData.error === "TOKEN_EXPIRED" || parsedData.error === "INVALID_TOKEN") {</span>
2608
+ <span class="cstat-no" title="statement not covered" > result.isAuthError = true;</span>
2609
+ <span class="cstat-no" title="statement not covered" > result.errorType = "Parsed Data";</span>
2610
+ <span class="cstat-no" title="statement not covered" > result.errorDetails = parsedData;</span>
2611
+ <span class="cstat-no" title="statement not covered" > result.shouldTriggerLogout = true;</span>
2612
+ <span class="cstat-no" title="statement not covered" > result.isIrrecoverable = true;</span>
2613
+ <span class="cstat-no" title="statement not covered" ></span>
2614
+ <span class="cstat-no" title="statement not covered" > if (parsedData.error === "REFRESH_TOKEN_INVALID") {</span>
2615
+ <span class="cstat-no" title="statement not covered" > result.isRefreshTokenInvalid = true;</span>
2616
+ <span class="cstat-no" title="statement not covered" > result.isTokenRefreshFailed = true;</span>
2617
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha caducado. Por favor, inicia sesión nuevamente.";</span>
2618
+ <span class="cstat-no" title="statement not covered" > } else {</span>
2619
+ <span class="cstat-no" title="statement not covered" > result.isTokenExpired = true;</span>
2620
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2621
+ <span class="cstat-no" title="statement not covered" > }</span>
2622
+ <span class="cstat-no" title="statement not covered" > }</span>
2623
+ <span class="cstat-no" title="statement not covered" > } catch {</span>
2624
+ <span class="cstat-no" title="statement not covered" > // Si no se puede parsear, continuar</span>
2625
+ <span class="cstat-no" title="statement not covered" > }</span>
2626
+ <span class="cstat-no" title="statement not covered" > }</span>
2627
+ <span class="cstat-no" title="statement not covered" ></span>
2628
+ <span class="cstat-no" title="statement not covered" > // FORMATO 5: response.errorCode</span>
2629
+ <span class="cstat-no" title="statement not covered" > if (!result.isAuthError &amp;&amp; response.errorCode) {</span>
2630
+ <span class="cstat-no" title="statement not covered" > const errorCode = String(response.errorCode).toUpperCase();</span>
2631
+ <span class="cstat-no" title="statement not covered" > if (</span>
2632
+ <span class="cstat-no" title="statement not covered" > errorCode === "UNAUTHORIZED" ||</span>
2633
+ <span class="cstat-no" title="statement not covered" > errorCode === "UNAUTHENTICATED" ||</span>
2634
+ <span class="cstat-no" title="statement not covered" > errorCode === "TOKEN_EXPIRED" ||</span>
2635
+ <span class="cstat-no" title="statement not covered" > errorCode === "INVALID_TOKEN"</span>
2636
+ <span class="cstat-no" title="statement not covered" > ) {</span>
2637
+ <span class="cstat-no" title="statement not covered" > result.isAuthError = true;</span>
2638
+ <span class="cstat-no" title="statement not covered" > result.errorType = "Error Code";</span>
2639
+ <span class="cstat-no" title="statement not covered" > result.errorDetails = { errorCode };</span>
2640
+ <span class="cstat-no" title="statement not covered" > result.shouldTriggerLogout = true;</span>
2641
+ <span class="cstat-no" title="statement not covered" ></span>
2642
+ <span class="cstat-no" title="statement not covered" > if (errorCode === "TOKEN_EXPIRED") {</span>
2643
+ <span class="cstat-no" title="statement not covered" > result.isTokenExpired = true;</span>
2644
+ <span class="cstat-no" title="statement not covered" > } else {</span>
2645
+ <span class="cstat-no" title="statement not covered" > result.isUnauthorized = true;</span>
2646
+ <span class="cstat-no" title="statement not covered" > }</span>
2647
+ <span class="cstat-no" title="statement not covered" ></span>
2648
+ <span class="cstat-no" title="statement not covered" > result.userFriendlyMessage = "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2649
+ <span class="cstat-no" title="statement not covered" > }</span>
2650
+ <span class="cstat-no" title="statement not covered" > }</span>
2651
+ <span class="cstat-no" title="statement not covered" ></span>
2652
+ <span class="cstat-no" title="statement not covered" > return result;</span>
2653
+ <span class="cstat-no" title="statement not covered" > }</span>
2654
+ &nbsp;
2655
+ /**
2656
+ * ✅ FASE 2: Actualizar timestamp de última actividad del usuario
2657
+ * Debe llamarse cuando el usuario realiza cualquier acción (CRUD, navegación, etc.)
2658
+ */
2659
+ public <span class="fstat-no" title="function not covered" >updateLastActivity(): void {</span>
2660
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = Date.now();</span>
2661
+ <span class="cstat-no" title="statement not covered" > this.log("Last activity updated");</span>
2662
+ <span class="cstat-no" title="statement not covered" > }</span>
2663
+ &nbsp;
2664
+ /**
2665
+ * ✅ FASE 2: Obtener tiempo transcurrido desde última actividad (en milisegundos)
2666
+ */
2667
+ public <span class="fstat-no" title="function not covered" >getTimeSinceLastActivity(): number {</span>
2668
+ <span class="cstat-no" title="statement not covered" > if (this.lastActivityTime === 0) return 0;</span>
2669
+ <span class="cstat-no" title="statement not covered" > return Date.now() - this.lastActivityTime;</span>
2670
+ <span class="cstat-no" title="statement not covered" > }</span>
2671
+ &nbsp;
2672
+ /**
2673
+ * ✅ MEJORADO FASE 3: Verificar inactividad simplificada
2674
+ * Solo verifica timeout de inactividad absoluta
2675
+ * La renovación de tokens se maneja en useSession con lógica de TTL
2676
+ * @returns 'logout' si debe desloguear por inactividad, 'none' si no hace nada
2677
+ */
2678
+ public <span class="fstat-no" title="function not covered" >checkInactivity(): "logout" | "none" {</span>
2679
+ <span class="cstat-no" title="statement not covered" > const timeSinceActivity = this.getTimeSinceLastActivity();</span>
2680
+ <span class="cstat-no" title="statement not covered" ></span>
2681
+ <span class="cstat-no" title="statement not covered" > // Si no hay actividad registrada, no hacer nada (primera carga)</span>
2682
+ <span class="cstat-no" title="statement not covered" > if (this.lastActivityTime === 0) {</span>
2683
+ <span class="cstat-no" title="statement not covered" > return "none";</span>
2684
+ <span class="cstat-no" title="statement not covered" > }</span>
2685
+ <span class="cstat-no" title="statement not covered" ></span>
2686
+ <span class="cstat-no" title="statement not covered" > // Constante de tiempo de inactividad</span>
2687
+ <span class="cstat-no" title="statement not covered" > const INACTIVITY_TIMEOUT = 30 * 60 * 1000; // 30 minutos</span>
2688
+ <span class="cstat-no" title="statement not covered" ></span>
2689
+ <span class="cstat-no" title="statement not covered" > // Si última actividad fue hace más de 30 minutos → logout</span>
2690
+ <span class="cstat-no" title="statement not covered" > if (timeSinceActivity &gt; INACTIVITY_TIMEOUT) {</span>
2691
+ <span class="cstat-no" title="statement not covered" > this.log(`Inactivity timeout: ${Math.floor(timeSinceActivity / 60000)} minutes since last activity`);</span>
2692
+ <span class="cstat-no" title="statement not covered" > return "logout";</span>
2693
+ <span class="cstat-no" title="statement not covered" > }</span>
2694
+ <span class="cstat-no" title="statement not covered" ></span>
2695
+ <span class="cstat-no" title="statement not covered" > // No hacer nada (la renovación la maneja useSession con lógica de TTL)</span>
2696
+ <span class="cstat-no" title="statement not covered" > return "none";</span>
2697
+ <span class="cstat-no" title="statement not covered" > }</span>
2698
+ &nbsp;
2699
+ /**
2700
+ * Limpiar sesión completamente
2701
+ */
2702
+ <span class="fstat-no" title="function not covered" > public async clearSession(): Promise&lt;void&gt; {</span>
2703
+ <span class="cstat-no" title="statement not covered" > CookieSync.clearCrossSubdomainCookie(this.config.env);</span>
2704
+ <span class="cstat-no" title="statement not covered" > await TokenStorage.clearTokens();</span>
2705
+ <span class="cstat-no" title="statement not covered" > await crudify.logout();</span>
2706
+ <span class="cstat-no" title="statement not covered" > this.lastActivityTime = 0; // Reset última actividad</span>
2707
+ <span class="cstat-no" title="statement not covered" > this.log("Session cleared completely");</span>
2708
+ <span class="cstat-no" title="statement not covered" > }</span>
2709
+ &nbsp;
2710
+ /**
2711
+ * Obtener mensaje de sesión expirada traducido
2712
+ */
2713
+ private <span class="fstat-no" title="function not covered" >getSessionExpiredMessage(): string {</span>
2714
+ <span class="cstat-no" title="statement not covered" > if (this.config.translateFn) {</span>
2715
+ <span class="cstat-no" title="statement not covered" > return translateErrorCode("SESSION_EXPIRED", {</span>
2716
+ <span class="cstat-no" title="statement not covered" > translateFn: this.config.translateFn,</span>
2717
+ <span class="cstat-no" title="statement not covered" > enableDebug: this.config.enableLogging,</span>
2718
+ <span class="cstat-no" title="statement not covered" > });</span>
2719
+ <span class="cstat-no" title="statement not covered" > }</span>
2720
+ <span class="cstat-no" title="statement not covered" > // Fallback si no hay función de traducción</span>
2721
+ <span class="cstat-no" title="statement not covered" > return "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";</span>
2722
+ <span class="cstat-no" title="statement not covered" > }</span>
2723
+ &nbsp;
2724
+ // Métodos privados
2725
+ &nbsp;
2726
+ private <span class="fstat-no" title="function not covered" >log(message: string, context?: Record&lt;string, unknown&gt;): void {</span>
2727
+ <span class="cstat-no" title="statement not covered" > if (this.config.enableLogging) {</span>
2728
+ <span class="cstat-no" title="statement not covered" > logger.debug(`[SessionManager] ${message}`, context);</span>
2729
+ <span class="cstat-no" title="statement not covered" > }</span>
2730
+ <span class="cstat-no" title="statement not covered" > }</span>
2731
+ &nbsp;
2732
+ private <span class="fstat-no" title="function not covered" >formatError(errors: unknown): string {</span>
2733
+ <span class="cstat-no" title="statement not covered" > if (!errors) return "Unknown error";</span>
2734
+ <span class="cstat-no" title="statement not covered" ></span>
2735
+ <span class="cstat-no" title="statement not covered" > if (typeof errors === "string") return errors;</span>
2736
+ <span class="cstat-no" title="statement not covered" ></span>
2737
+ <span class="cstat-no" title="statement not covered" > if (typeof errors === "object") {</span>
2738
+ <span class="cstat-no" title="statement not covered" > const errorMessages = Object.values(errors as Record&lt;string, unknown&gt;).flat();</span>
2739
+ <span class="cstat-no" title="statement not covered" > return errorMessages.map(String).join(", ");</span>
2740
+ <span class="cstat-no" title="statement not covered" > }</span>
2741
+ <span class="cstat-no" title="statement not covered" ></span>
2742
+ <span class="cstat-no" title="statement not covered" > return "Authentication failed";</span>
2743
+ <span class="cstat-no" title="statement not covered" > }</span>
2744
+ }
2745
+ &nbsp;</pre></td></tr></table></pre>
2746
+
2747
+ <div class='push'></div><!-- for sticky footer -->
2748
+ </div><!-- /wrapper -->
2749
+ <div class='footer quiet pad2 space-top1 center small'>
2750
+ Code coverage generated by
2751
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
2752
+ at 2026-01-28T06:22:15.683Z
2753
+ </div>
2754
+ <script src="../prettify.js"></script>
2755
+ <script>
2756
+ window.onload = function () {
2757
+ prettyPrint();
2758
+ };
2759
+ </script>
2760
+ <script src="../sorter.js"></script>
2761
+ <script src="../block-navigation.js"></script>
2762
+ </body>
2763
+ </html>
2764
+