@spaceflow/review 4.0.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2782 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/review-spec/review-spec.service.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">src/review-spec</a> review-spec.service.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">99.73% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>377/378</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">91.3% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>189/207</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>53/53</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">99.72% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>357/358</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 high'></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>
960
+ <a name='L895'></a><a href='#L895'>895</a>
961
+ <a name='L896'></a><a href='#L896'>896</a>
962
+ <a name='L897'></a><a href='#L897'>897</a>
963
+ <a name='L898'></a><a href='#L898'>898</a>
964
+ <a name='L899'></a><a href='#L899'>899</a>
965
+ <a name='L900'></a><a href='#L900'>900</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
966
+ <span class="cline-any cline-neutral">&nbsp;</span>
967
+ <span class="cline-any cline-neutral">&nbsp;</span>
968
+ <span class="cline-any cline-neutral">&nbsp;</span>
969
+ <span class="cline-any cline-neutral">&nbsp;</span>
970
+ <span class="cline-any cline-neutral">&nbsp;</span>
971
+ <span class="cline-any cline-neutral">&nbsp;</span>
972
+ <span class="cline-any cline-neutral">&nbsp;</span>
973
+ <span class="cline-any cline-neutral">&nbsp;</span>
974
+ <span class="cline-any cline-neutral">&nbsp;</span>
975
+ <span class="cline-any cline-neutral">&nbsp;</span>
976
+ <span class="cline-any cline-neutral">&nbsp;</span>
977
+ <span class="cline-any cline-neutral">&nbsp;</span>
978
+ <span class="cline-any cline-neutral">&nbsp;</span>
979
+ <span class="cline-any cline-neutral">&nbsp;</span>
980
+ <span class="cline-any cline-neutral">&nbsp;</span>
981
+ <span class="cline-any cline-neutral">&nbsp;</span>
982
+ <span class="cline-any cline-yes">1x</span>
983
+ <span class="cline-any cline-neutral">&nbsp;</span>
984
+ <span class="cline-any cline-neutral">&nbsp;</span>
985
+ <span class="cline-any cline-yes">109x</span>
986
+ <span class="cline-any cline-neutral">&nbsp;</span>
987
+ <span class="cline-any cline-neutral">&nbsp;</span>
988
+ <span class="cline-any cline-neutral">&nbsp;</span>
989
+ <span class="cline-any cline-neutral">&nbsp;</span>
990
+ <span class="cline-any cline-neutral">&nbsp;</span>
991
+ <span class="cline-any cline-yes">32x</span>
992
+ <span class="cline-any cline-yes">2x</span>
993
+ <span class="cline-any cline-neutral">&nbsp;</span>
994
+ <span class="cline-any cline-neutral">&nbsp;</span>
995
+ <span class="cline-any cline-yes">2x</span>
996
+ <span class="cline-any cline-neutral">&nbsp;</span>
997
+ <span class="cline-any cline-yes">30x</span>
998
+ <span class="cline-any cline-neutral">&nbsp;</span>
999
+ <span class="cline-any cline-neutral">&nbsp;</span>
1000
+ <span class="cline-any cline-neutral">&nbsp;</span>
1001
+ <span class="cline-any cline-neutral">&nbsp;</span>
1002
+ <span class="cline-any cline-neutral">&nbsp;</span>
1003
+ <span class="cline-any cline-neutral">&nbsp;</span>
1004
+ <span class="cline-any cline-yes">14x</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-neutral">&nbsp;</span>
1008
+ <span class="cline-any cline-neutral">&nbsp;</span>
1009
+ <span class="cline-any cline-yes">13x</span>
1010
+ <span class="cline-any cline-yes">6x</span>
1011
+ <span class="cline-any cline-neutral">&nbsp;</span>
1012
+ <span class="cline-any cline-neutral">&nbsp;</span>
1013
+ <span class="cline-any cline-yes">7x</span>
1014
+ <span class="cline-any cline-yes">7x</span>
1015
+ <span class="cline-any cline-yes">5x</span>
1016
+ <span class="cline-any cline-neutral">&nbsp;</span>
1017
+ <span class="cline-any cline-neutral">&nbsp;</span>
1018
+ <span class="cline-any cline-yes">2x</span>
1019
+ <span class="cline-any cline-neutral">&nbsp;</span>
1020
+ <span class="cline-any cline-neutral">&nbsp;</span>
1021
+ <span class="cline-any cline-neutral">&nbsp;</span>
1022
+ <span class="cline-any cline-neutral">&nbsp;</span>
1023
+ <span class="cline-any cline-neutral">&nbsp;</span>
1024
+ <span class="cline-any cline-neutral">&nbsp;</span>
1025
+ <span class="cline-any cline-yes">4x</span>
1026
+ <span class="cline-any cline-neutral">&nbsp;</span>
1027
+ <span class="cline-any cline-yes">4x</span>
1028
+ <span class="cline-any cline-yes">6x</span>
1029
+ <span class="cline-any cline-yes">5x</span>
1030
+ <span class="cline-any cline-yes">5x</span>
1031
+ <span class="cline-any cline-yes">4x</span>
1032
+ <span class="cline-any cline-neutral">&nbsp;</span>
1033
+ <span class="cline-any cline-neutral">&nbsp;</span>
1034
+ <span class="cline-any cline-neutral">&nbsp;</span>
1035
+ <span class="cline-any cline-neutral">&nbsp;</span>
1036
+ <span class="cline-any cline-yes">12x</span>
1037
+ <span class="cline-any cline-neutral">&nbsp;</span>
1038
+ <span class="cline-any cline-neutral">&nbsp;</span>
1039
+ <span class="cline-any cline-neutral">&nbsp;</span>
1040
+ <span class="cline-any cline-yes">5x</span>
1041
+ <span class="cline-any cline-neutral">&nbsp;</span>
1042
+ <span class="cline-any cline-yes">5x</span>
1043
+ <span class="cline-any cline-yes">5x</span>
1044
+ <span class="cline-any cline-neutral">&nbsp;</span>
1045
+ <span class="cline-any cline-yes">3x</span>
1046
+ <span class="cline-any cline-yes">7x</span>
1047
+ <span class="cline-any cline-neutral">&nbsp;</span>
1048
+ <span class="cline-any cline-yes">5x</span>
1049
+ <span class="cline-any cline-yes">5x</span>
1050
+ <span class="cline-any cline-yes">5x</span>
1051
+ <span class="cline-any cline-yes">4x</span>
1052
+ <span class="cline-any cline-neutral">&nbsp;</span>
1053
+ <span class="cline-any cline-neutral">&nbsp;</span>
1054
+ <span class="cline-any cline-neutral">&nbsp;</span>
1055
+ <span class="cline-any cline-neutral">&nbsp;</span>
1056
+ <span class="cline-any cline-yes">2x</span>
1057
+ <span class="cline-any cline-yes">1x</span>
1058
+ <span class="cline-any cline-neutral">&nbsp;</span>
1059
+ <span class="cline-any cline-neutral">&nbsp;</span>
1060
+ <span class="cline-any cline-neutral">&nbsp;</span>
1061
+ <span class="cline-any cline-yes">5x</span>
1062
+ <span class="cline-any cline-neutral">&nbsp;</span>
1063
+ <span class="cline-any cline-neutral">&nbsp;</span>
1064
+ <span class="cline-any cline-neutral">&nbsp;</span>
1065
+ <span class="cline-any cline-yes">3x</span>
1066
+ <span class="cline-any cline-neutral">&nbsp;</span>
1067
+ <span class="cline-any cline-yes">3x</span>
1068
+ <span class="cline-any cline-neutral">&nbsp;</span>
1069
+ <span class="cline-any cline-yes">3x</span>
1070
+ <span class="cline-any cline-yes">3x</span>
1071
+ <span class="cline-any cline-yes">2x</span>
1072
+ <span class="cline-any cline-yes">2x</span>
1073
+ <span class="cline-any cline-yes">1x</span>
1074
+ <span class="cline-any cline-yes">1x</span>
1075
+ <span class="cline-any cline-neutral">&nbsp;</span>
1076
+ <span class="cline-any cline-neutral">&nbsp;</span>
1077
+ <span class="cline-any cline-yes">2x</span>
1078
+ <span class="cline-any cline-yes">1x</span>
1079
+ <span class="cline-any cline-yes">1x</span>
1080
+ <span class="cline-any cline-yes">1x</span>
1081
+ <span class="cline-any cline-neutral">&nbsp;</span>
1082
+ <span class="cline-any cline-neutral">&nbsp;</span>
1083
+ <span class="cline-any cline-neutral">&nbsp;</span>
1084
+ <span class="cline-any cline-yes">1x</span>
1085
+ <span class="cline-any cline-yes">1x</span>
1086
+ <span class="cline-any cline-neutral">&nbsp;</span>
1087
+ <span class="cline-any cline-neutral">&nbsp;</span>
1088
+ <span class="cline-any cline-neutral">&nbsp;</span>
1089
+ <span class="cline-any cline-yes">3x</span>
1090
+ <span class="cline-any cline-neutral">&nbsp;</span>
1091
+ <span class="cline-any cline-neutral">&nbsp;</span>
1092
+ <span class="cline-any cline-neutral">&nbsp;</span>
1093
+ <span class="cline-any cline-neutral">&nbsp;</span>
1094
+ <span class="cline-any cline-neutral">&nbsp;</span>
1095
+ <span class="cline-any cline-neutral">&nbsp;</span>
1096
+ <span class="cline-any cline-neutral">&nbsp;</span>
1097
+ <span class="cline-any cline-yes">7x</span>
1098
+ <span class="cline-any cline-yes">7x</span>
1099
+ <span class="cline-any cline-neutral">&nbsp;</span>
1100
+ <span class="cline-any cline-yes">7x</span>
1101
+ <span class="cline-any cline-yes">7x</span>
1102
+ <span class="cline-any cline-yes">7x</span>
1103
+ <span class="cline-any cline-yes">7x</span>
1104
+ <span class="cline-any cline-yes">7x</span>
1105
+ <span class="cline-any cline-yes">7x</span>
1106
+ <span class="cline-any cline-yes">7x</span>
1107
+ <span class="cline-any cline-yes">1x</span>
1108
+ <span class="cline-any cline-yes">1x</span>
1109
+ <span class="cline-any cline-yes">1x</span>
1110
+ <span class="cline-any cline-neutral">&nbsp;</span>
1111
+ <span class="cline-any cline-neutral">&nbsp;</span>
1112
+ <span class="cline-any cline-neutral">&nbsp;</span>
1113
+ <span class="cline-any cline-neutral">&nbsp;</span>
1114
+ <span class="cline-any cline-neutral">&nbsp;</span>
1115
+ <span class="cline-any cline-neutral">&nbsp;</span>
1116
+ <span class="cline-any cline-yes">6x</span>
1117
+ <span class="cline-any cline-yes">6x</span>
1118
+ <span class="cline-any cline-neutral">&nbsp;</span>
1119
+ <span class="cline-any cline-neutral">&nbsp;</span>
1120
+ <span class="cline-any cline-yes">7x</span>
1121
+ <span class="cline-any cline-neutral">&nbsp;</span>
1122
+ <span class="cline-any cline-neutral">&nbsp;</span>
1123
+ <span class="cline-any cline-neutral">&nbsp;</span>
1124
+ <span class="cline-any cline-neutral">&nbsp;</span>
1125
+ <span class="cline-any cline-neutral">&nbsp;</span>
1126
+ <span class="cline-any cline-yes">4x</span>
1127
+ <span class="cline-any cline-yes">3x</span>
1128
+ <span class="cline-any cline-neutral">&nbsp;</span>
1129
+ <span class="cline-any cline-yes">4x</span>
1130
+ <span class="cline-any cline-yes">1x</span>
1131
+ <span class="cline-any cline-yes">1x</span>
1132
+ <span class="cline-any cline-neutral">&nbsp;</span>
1133
+ <span class="cline-any cline-yes">2x</span>
1134
+ <span class="cline-any cline-yes">2x</span>
1135
+ <span class="cline-any cline-yes">2x</span>
1136
+ <span class="cline-any cline-neutral">&nbsp;</span>
1137
+ <span class="cline-any cline-neutral">&nbsp;</span>
1138
+ <span class="cline-any cline-neutral">&nbsp;</span>
1139
+ <span class="cline-any cline-neutral">&nbsp;</span>
1140
+ <span class="cline-any cline-neutral">&nbsp;</span>
1141
+ <span class="cline-any cline-yes">2x</span>
1142
+ <span class="cline-any cline-neutral">&nbsp;</span>
1143
+ <span class="cline-any cline-neutral">&nbsp;</span>
1144
+ <span class="cline-any cline-yes">2x</span>
1145
+ <span class="cline-any cline-yes">2x</span>
1146
+ <span class="cline-any cline-yes">2x</span>
1147
+ <span class="cline-any cline-neutral">&nbsp;</span>
1148
+ <span class="cline-any cline-yes">3x</span>
1149
+ <span class="cline-any cline-neutral">&nbsp;</span>
1150
+ <span class="cline-any cline-yes">3x</span>
1151
+ <span class="cline-any cline-yes">3x</span>
1152
+ <span class="cline-any cline-yes">2x</span>
1153
+ <span class="cline-any cline-yes">1x</span>
1154
+ <span class="cline-any cline-yes">1x</span>
1155
+ <span class="cline-any cline-neutral">&nbsp;</span>
1156
+ <span class="cline-any cline-neutral">&nbsp;</span>
1157
+ <span class="cline-any cline-neutral">&nbsp;</span>
1158
+ <span class="cline-any cline-neutral">&nbsp;</span>
1159
+ <span class="cline-any cline-yes">2x</span>
1160
+ <span class="cline-any cline-neutral">&nbsp;</span>
1161
+ <span class="cline-any cline-neutral">&nbsp;</span>
1162
+ <span class="cline-any cline-neutral">&nbsp;</span>
1163
+ <span class="cline-any cline-neutral">&nbsp;</span>
1164
+ <span class="cline-any cline-neutral">&nbsp;</span>
1165
+ <span class="cline-any cline-neutral">&nbsp;</span>
1166
+ <span class="cline-any cline-neutral">&nbsp;</span>
1167
+ <span class="cline-any cline-neutral">&nbsp;</span>
1168
+ <span class="cline-any cline-yes">7x</span>
1169
+ <span class="cline-any cline-neutral">&nbsp;</span>
1170
+ <span class="cline-any cline-yes">7x</span>
1171
+ <span class="cline-any cline-yes">7x</span>
1172
+ <span class="cline-any cline-neutral">&nbsp;</span>
1173
+ <span class="cline-any cline-neutral">&nbsp;</span>
1174
+ <span class="cline-any cline-yes">6x</span>
1175
+ <span class="cline-any cline-yes">6x</span>
1176
+ <span class="cline-any cline-yes">2x</span>
1177
+ <span class="cline-any cline-yes">2x</span>
1178
+ <span class="cline-any cline-neutral">&nbsp;</span>
1179
+ <span class="cline-any cline-neutral">&nbsp;</span>
1180
+ <span class="cline-any cline-neutral">&nbsp;</span>
1181
+ <span class="cline-any cline-yes">4x</span>
1182
+ <span class="cline-any cline-yes">4x</span>
1183
+ <span class="cline-any cline-yes">4x</span>
1184
+ <span class="cline-any cline-neutral">&nbsp;</span>
1185
+ <span class="cline-any cline-yes">4x</span>
1186
+ <span class="cline-any cline-yes">4x</span>
1187
+ <span class="cline-any cline-yes">4x</span>
1188
+ <span class="cline-any cline-yes">1x</span>
1189
+ <span class="cline-any cline-neutral">&nbsp;</span>
1190
+ <span class="cline-any cline-neutral">&nbsp;</span>
1191
+ <span class="cline-any cline-yes">3x</span>
1192
+ <span class="cline-any cline-yes">3x</span>
1193
+ <span class="cline-any cline-yes">2x</span>
1194
+ <span class="cline-any cline-yes">1x</span>
1195
+ <span class="cline-any cline-neutral">&nbsp;</span>
1196
+ <span class="cline-any cline-neutral">&nbsp;</span>
1197
+ <span class="cline-any cline-neutral">&nbsp;</span>
1198
+ <span class="cline-any cline-neutral">&nbsp;</span>
1199
+ <span class="cline-any cline-neutral">&nbsp;</span>
1200
+ <span class="cline-any cline-neutral">&nbsp;</span>
1201
+ <span class="cline-any cline-neutral">&nbsp;</span>
1202
+ <span class="cline-any cline-neutral">&nbsp;</span>
1203
+ <span class="cline-any cline-neutral">&nbsp;</span>
1204
+ <span class="cline-any cline-neutral">&nbsp;</span>
1205
+ <span class="cline-any cline-neutral">&nbsp;</span>
1206
+ <span class="cline-any cline-yes">5x</span>
1207
+ <span class="cline-any cline-neutral">&nbsp;</span>
1208
+ <span class="cline-any cline-neutral">&nbsp;</span>
1209
+ <span class="cline-any cline-neutral">&nbsp;</span>
1210
+ <span class="cline-any cline-yes">7x</span>
1211
+ <span class="cline-any cline-neutral">&nbsp;</span>
1212
+ <span class="cline-any cline-neutral">&nbsp;</span>
1213
+ <span class="cline-any cline-neutral">&nbsp;</span>
1214
+ <span class="cline-any cline-neutral">&nbsp;</span>
1215
+ <span class="cline-any cline-neutral">&nbsp;</span>
1216
+ <span class="cline-any cline-neutral">&nbsp;</span>
1217
+ <span class="cline-any cline-neutral">&nbsp;</span>
1218
+ <span class="cline-any cline-neutral">&nbsp;</span>
1219
+ <span class="cline-any cline-yes">6x</span>
1220
+ <span class="cline-any cline-yes">6x</span>
1221
+ <span class="cline-any cline-yes">1x</span>
1222
+ <span class="cline-any cline-yes">1x</span>
1223
+ <span class="cline-any cline-neutral">&nbsp;</span>
1224
+ <span class="cline-any cline-neutral">&nbsp;</span>
1225
+ <span class="cline-any cline-yes">5x</span>
1226
+ <span class="cline-any cline-neutral">&nbsp;</span>
1227
+ <span class="cline-any cline-yes">5x</span>
1228
+ <span class="cline-any cline-yes">5x</span>
1229
+ <span class="cline-any cline-neutral">&nbsp;</span>
1230
+ <span class="cline-any cline-yes">2x</span>
1231
+ <span class="cline-any cline-yes">2x</span>
1232
+ <span class="cline-any cline-neutral">&nbsp;</span>
1233
+ <span class="cline-any cline-neutral">&nbsp;</span>
1234
+ <span class="cline-any cline-yes">1x</span>
1235
+ <span class="cline-any cline-neutral">&nbsp;</span>
1236
+ <span class="cline-any cline-yes">2x</span>
1237
+ <span class="cline-any cline-neutral">&nbsp;</span>
1238
+ <span class="cline-any cline-neutral">&nbsp;</span>
1239
+ <span class="cline-any cline-yes">3x</span>
1240
+ <span class="cline-any cline-yes">3x</span>
1241
+ <span class="cline-any cline-yes">3x</span>
1242
+ <span class="cline-any cline-neutral">&nbsp;</span>
1243
+ <span class="cline-any cline-yes">3x</span>
1244
+ <span class="cline-any cline-neutral">&nbsp;</span>
1245
+ <span class="cline-any cline-yes">1x</span>
1246
+ <span class="cline-any cline-yes">1x</span>
1247
+ <span class="cline-any cline-neutral">&nbsp;</span>
1248
+ <span class="cline-any cline-neutral">&nbsp;</span>
1249
+ <span class="cline-any cline-neutral">&nbsp;</span>
1250
+ <span class="cline-any cline-neutral">&nbsp;</span>
1251
+ <span class="cline-any cline-neutral">&nbsp;</span>
1252
+ <span class="cline-any cline-yes">10x</span>
1253
+ <span class="cline-any cline-yes">10x</span>
1254
+ <span class="cline-any cline-yes">10x</span>
1255
+ <span class="cline-any cline-yes">10x</span>
1256
+ <span class="cline-any cline-neutral">&nbsp;</span>
1257
+ <span class="cline-any cline-yes">10x</span>
1258
+ <span class="cline-any cline-yes">10x</span>
1259
+ <span class="cline-any cline-yes">7x</span>
1260
+ <span class="cline-any cline-yes">3x</span>
1261
+ <span class="cline-any cline-yes">1x</span>
1262
+ <span class="cline-any cline-neutral">&nbsp;</span>
1263
+ <span class="cline-any cline-yes">2x</span>
1264
+ <span class="cline-any cline-neutral">&nbsp;</span>
1265
+ <span class="cline-any cline-neutral">&nbsp;</span>
1266
+ <span class="cline-any cline-neutral">&nbsp;</span>
1267
+ <span class="cline-any cline-yes">14x</span>
1268
+ <span class="cline-any cline-yes">14x</span>
1269
+ <span class="cline-any cline-neutral">&nbsp;</span>
1270
+ <span class="cline-any cline-yes">14x</span>
1271
+ <span class="cline-any cline-yes">1x</span>
1272
+ <span class="cline-any cline-yes">1x</span>
1273
+ <span class="cline-any cline-neutral">&nbsp;</span>
1274
+ <span class="cline-any cline-neutral">&nbsp;</span>
1275
+ <span class="cline-any cline-yes">13x</span>
1276
+ <span class="cline-any cline-yes">13x</span>
1277
+ <span class="cline-any cline-yes">24x</span>
1278
+ <span class="cline-any cline-neutral">&nbsp;</span>
1279
+ <span class="cline-any cline-yes">13x</span>
1280
+ <span class="cline-any cline-neutral">&nbsp;</span>
1281
+ <span class="cline-any cline-neutral">&nbsp;</span>
1282
+ <span class="cline-any cline-yes">13x</span>
1283
+ <span class="cline-any cline-neutral">&nbsp;</span>
1284
+ <span class="cline-any cline-yes">14x</span>
1285
+ <span class="cline-any cline-neutral">&nbsp;</span>
1286
+ <span class="cline-any cline-yes">14x</span>
1287
+ <span class="cline-any cline-neutral">&nbsp;</span>
1288
+ <span class="cline-any cline-yes">14x</span>
1289
+ <span class="cline-any cline-neutral">&nbsp;</span>
1290
+ <span class="cline-any cline-neutral">&nbsp;</span>
1291
+ <span class="cline-any cline-neutral">&nbsp;</span>
1292
+ <span class="cline-any cline-neutral">&nbsp;</span>
1293
+ <span class="cline-any cline-neutral">&nbsp;</span>
1294
+ <span class="cline-any cline-neutral">&nbsp;</span>
1295
+ <span class="cline-any cline-neutral">&nbsp;</span>
1296
+ <span class="cline-any cline-neutral">&nbsp;</span>
1297
+ <span class="cline-any cline-neutral">&nbsp;</span>
1298
+ <span class="cline-any cline-neutral">&nbsp;</span>
1299
+ <span class="cline-any cline-neutral">&nbsp;</span>
1300
+ <span class="cline-any cline-neutral">&nbsp;</span>
1301
+ <span class="cline-any cline-yes">13x</span>
1302
+ <span class="cline-any cline-yes">13x</span>
1303
+ <span class="cline-any cline-neutral">&nbsp;</span>
1304
+ <span class="cline-any cline-yes">13x</span>
1305
+ <span class="cline-any cline-neutral">&nbsp;</span>
1306
+ <span class="cline-any cline-yes">13x</span>
1307
+ <span class="cline-any cline-yes">26x</span>
1308
+ <span class="cline-any cline-neutral">&nbsp;</span>
1309
+ <span class="cline-any cline-neutral">&nbsp;</span>
1310
+ <span class="cline-any cline-neutral">&nbsp;</span>
1311
+ <span class="cline-any cline-neutral">&nbsp;</span>
1312
+ <span class="cline-any cline-neutral">&nbsp;</span>
1313
+ <span class="cline-any cline-neutral">&nbsp;</span>
1314
+ <span class="cline-any cline-neutral">&nbsp;</span>
1315
+ <span class="cline-any cline-yes">13x</span>
1316
+ <span class="cline-any cline-yes">26x</span>
1317
+ <span class="cline-any cline-yes">26x</span>
1318
+ <span class="cline-any cline-yes">26x</span>
1319
+ <span class="cline-any cline-neutral">&nbsp;</span>
1320
+ <span class="cline-any cline-yes">26x</span>
1321
+ <span class="cline-any cline-yes">26x</span>
1322
+ <span class="cline-any cline-yes">26x</span>
1323
+ <span class="cline-any cline-neutral">&nbsp;</span>
1324
+ <span class="cline-any cline-neutral">&nbsp;</span>
1325
+ <span class="cline-any cline-yes">26x</span>
1326
+ <span class="cline-any cline-yes">26x</span>
1327
+ <span class="cline-any cline-yes">26x</span>
1328
+ <span class="cline-any cline-yes">2x</span>
1329
+ <span class="cline-any cline-neutral">&nbsp;</span>
1330
+ <span class="cline-any cline-neutral">&nbsp;</span>
1331
+ <span class="cline-any cline-yes">24x</span>
1332
+ <span class="cline-any cline-neutral">&nbsp;</span>
1333
+ <span class="cline-any cline-neutral">&nbsp;</span>
1334
+ <span class="cline-any cline-yes">26x</span>
1335
+ <span class="cline-any cline-yes">26x</span>
1336
+ <span class="cline-any cline-neutral">&nbsp;</span>
1337
+ <span class="cline-any cline-yes">26x</span>
1338
+ <span class="cline-any cline-neutral">&nbsp;</span>
1339
+ <span class="cline-any cline-neutral">&nbsp;</span>
1340
+ <span class="cline-any cline-neutral">&nbsp;</span>
1341
+ <span class="cline-any cline-neutral">&nbsp;</span>
1342
+ <span class="cline-any cline-neutral">&nbsp;</span>
1343
+ <span class="cline-any cline-neutral">&nbsp;</span>
1344
+ <span class="cline-any cline-neutral">&nbsp;</span>
1345
+ <span class="cline-any cline-neutral">&nbsp;</span>
1346
+ <span class="cline-any cline-neutral">&nbsp;</span>
1347
+ <span class="cline-any cline-neutral">&nbsp;</span>
1348
+ <span class="cline-any cline-yes">13x</span>
1349
+ <span class="cline-any cline-neutral">&nbsp;</span>
1350
+ <span class="cline-any cline-neutral">&nbsp;</span>
1351
+ <span class="cline-any cline-neutral">&nbsp;</span>
1352
+ <span class="cline-any cline-neutral">&nbsp;</span>
1353
+ <span class="cline-any cline-neutral">&nbsp;</span>
1354
+ <span class="cline-any cline-neutral">&nbsp;</span>
1355
+ <span class="cline-any cline-neutral">&nbsp;</span>
1356
+ <span class="cline-any cline-neutral">&nbsp;</span>
1357
+ <span class="cline-any cline-yes">109x</span>
1358
+ <span class="cline-any cline-yes">109x</span>
1359
+ <span class="cline-any cline-neutral">&nbsp;</span>
1360
+ <span class="cline-any cline-neutral">&nbsp;</span>
1361
+ <span class="cline-any cline-yes">109x</span>
1362
+ <span class="cline-any cline-yes">32x</span>
1363
+ <span class="cline-any cline-yes">32x</span>
1364
+ <span class="cline-any cline-neutral">&nbsp;</span>
1365
+ <span class="cline-any cline-yes">32x</span>
1366
+ <span class="cline-any cline-yes">32x</span>
1367
+ <span class="cline-any cline-yes">48x</span>
1368
+ <span class="cline-any cline-neutral">&nbsp;</span>
1369
+ <span class="cline-any cline-neutral">&nbsp;</span>
1370
+ <span class="cline-any cline-yes">32x</span>
1371
+ <span class="cline-any cline-neutral">&nbsp;</span>
1372
+ <span class="cline-any cline-neutral">&nbsp;</span>
1373
+ <span class="cline-any cline-yes">109x</span>
1374
+ <span class="cline-any cline-neutral">&nbsp;</span>
1375
+ <span class="cline-any cline-neutral">&nbsp;</span>
1376
+ <span class="cline-any cline-neutral">&nbsp;</span>
1377
+ <span class="cline-any cline-neutral">&nbsp;</span>
1378
+ <span class="cline-any cline-yes">30x</span>
1379
+ <span class="cline-any cline-yes">8x</span>
1380
+ <span class="cline-any cline-neutral">&nbsp;</span>
1381
+ <span class="cline-any cline-neutral">&nbsp;</span>
1382
+ <span class="cline-any cline-neutral">&nbsp;</span>
1383
+ <span class="cline-any cline-neutral">&nbsp;</span>
1384
+ <span class="cline-any cline-yes">32x</span>
1385
+ <span class="cline-any cline-yes">32x</span>
1386
+ <span class="cline-any cline-yes">10x</span>
1387
+ <span class="cline-any cline-yes">10x</span>
1388
+ <span class="cline-any cline-yes">9x</span>
1389
+ <span class="cline-any cline-neutral">&nbsp;</span>
1390
+ <span class="cline-any cline-neutral">&nbsp;</span>
1391
+ <span class="cline-any cline-yes">23x</span>
1392
+ <span class="cline-any cline-neutral">&nbsp;</span>
1393
+ <span class="cline-any cline-neutral">&nbsp;</span>
1394
+ <span class="cline-any cline-neutral">&nbsp;</span>
1395
+ <span class="cline-any cline-neutral">&nbsp;</span>
1396
+ <span class="cline-any cline-neutral">&nbsp;</span>
1397
+ <span class="cline-any cline-yes">16x</span>
1398
+ <span class="cline-any cline-yes">16x</span>
1399
+ <span class="cline-any cline-yes">16x</span>
1400
+ <span class="cline-any cline-neutral">&nbsp;</span>
1401
+ <span class="cline-any cline-neutral">&nbsp;</span>
1402
+ <span class="cline-any cline-neutral">&nbsp;</span>
1403
+ <span class="cline-any cline-yes">27x</span>
1404
+ <span class="cline-any cline-yes">27x</span>
1405
+ <span class="cline-any cline-neutral">&nbsp;</span>
1406
+ <span class="cline-any cline-yes">27x</span>
1407
+ <span class="cline-any cline-yes">30x</span>
1408
+ <span class="cline-any cline-yes">30x</span>
1409
+ <span class="cline-any cline-neutral">&nbsp;</span>
1410
+ <span class="cline-any cline-yes">18x</span>
1411
+ <span class="cline-any cline-yes">18x</span>
1412
+ <span class="cline-any cline-yes">2x</span>
1413
+ <span class="cline-any cline-yes">16x</span>
1414
+ <span class="cline-any cline-yes">1x</span>
1415
+ <span class="cline-any cline-neutral">&nbsp;</span>
1416
+ <span class="cline-any cline-neutral">&nbsp;</span>
1417
+ <span class="cline-any cline-yes">18x</span>
1418
+ <span class="cline-any cline-neutral">&nbsp;</span>
1419
+ <span class="cline-any cline-yes">3x</span>
1420
+ <span class="cline-any cline-neutral">&nbsp;</span>
1421
+ <span class="cline-any cline-yes">3x</span>
1422
+ <span class="cline-any cline-yes">3x</span>
1423
+ <span class="cline-any cline-yes">3x</span>
1424
+ <span class="cline-any cline-yes">3x</span>
1425
+ <span class="cline-any cline-neutral">&nbsp;</span>
1426
+ <span class="cline-any cline-neutral">&nbsp;</span>
1427
+ <span class="cline-any cline-neutral">&nbsp;</span>
1428
+ <span class="cline-any cline-yes">27x</span>
1429
+ <span class="cline-any cline-neutral">&nbsp;</span>
1430
+ <span class="cline-any cline-neutral">&nbsp;</span>
1431
+ <span class="cline-any cline-neutral">&nbsp;</span>
1432
+ <span class="cline-any cline-neutral">&nbsp;</span>
1433
+ <span class="cline-any cline-neutral">&nbsp;</span>
1434
+ <span class="cline-any cline-neutral">&nbsp;</span>
1435
+ <span class="cline-any cline-neutral">&nbsp;</span>
1436
+ <span class="cline-any cline-neutral">&nbsp;</span>
1437
+ <span class="cline-any cline-yes">2x</span>
1438
+ <span class="cline-any cline-yes">2x</span>
1439
+ <span class="cline-any cline-yes">4x</span>
1440
+ <span class="cline-any cline-yes">4x</span>
1441
+ <span class="cline-any cline-yes">5x</span>
1442
+ <span class="cline-any cline-neutral">&nbsp;</span>
1443
+ <span class="cline-any cline-neutral">&nbsp;</span>
1444
+ <span class="cline-any cline-neutral">&nbsp;</span>
1445
+ <span class="cline-any cline-yes">2x</span>
1446
+ <span class="cline-any cline-no">&nbsp;</span>
1447
+ <span class="cline-any cline-neutral">&nbsp;</span>
1448
+ <span class="cline-any cline-neutral">&nbsp;</span>
1449
+ <span class="cline-any cline-neutral">&nbsp;</span>
1450
+ <span class="cline-any cline-yes">2x</span>
1451
+ <span class="cline-any cline-yes">4x</span>
1452
+ <span class="cline-any cline-neutral">&nbsp;</span>
1453
+ <span class="cline-any cline-neutral">&nbsp;</span>
1454
+ <span class="cline-any cline-yes">5x</span>
1455
+ <span class="cline-any cline-yes">5x</span>
1456
+ <span class="cline-any cline-yes">1x</span>
1457
+ <span class="cline-any cline-neutral">&nbsp;</span>
1458
+ <span class="cline-any cline-yes">5x</span>
1459
+ <span class="cline-any cline-neutral">&nbsp;</span>
1460
+ <span class="cline-any cline-neutral">&nbsp;</span>
1461
+ <span class="cline-any cline-yes">4x</span>
1462
+ <span class="cline-any cline-neutral">&nbsp;</span>
1463
+ <span class="cline-any cline-neutral">&nbsp;</span>
1464
+ <span class="cline-any cline-neutral">&nbsp;</span>
1465
+ <span class="cline-any cline-neutral">&nbsp;</span>
1466
+ <span class="cline-any cline-neutral">&nbsp;</span>
1467
+ <span class="cline-any cline-neutral">&nbsp;</span>
1468
+ <span class="cline-any cline-neutral">&nbsp;</span>
1469
+ <span class="cline-any cline-neutral">&nbsp;</span>
1470
+ <span class="cline-any cline-neutral">&nbsp;</span>
1471
+ <span class="cline-any cline-neutral">&nbsp;</span>
1472
+ <span class="cline-any cline-neutral">&nbsp;</span>
1473
+ <span class="cline-any cline-neutral">&nbsp;</span>
1474
+ <span class="cline-any cline-yes">3x</span>
1475
+ <span class="cline-any cline-yes">3x</span>
1476
+ <span class="cline-any cline-neutral">&nbsp;</span>
1477
+ <span class="cline-any cline-yes">3x</span>
1478
+ <span class="cline-any cline-yes">4x</span>
1479
+ <span class="cline-any cline-neutral">&nbsp;</span>
1480
+ <span class="cline-any cline-neutral">&nbsp;</span>
1481
+ <span class="cline-any cline-neutral">&nbsp;</span>
1482
+ <span class="cline-any cline-yes">3x</span>
1483
+ <span class="cline-any cline-neutral">&nbsp;</span>
1484
+ <span class="cline-any cline-yes">6x</span>
1485
+ <span class="cline-any cline-neutral">&nbsp;</span>
1486
+ <span class="cline-any cline-neutral">&nbsp;</span>
1487
+ <span class="cline-any cline-yes">6x</span>
1488
+ <span class="cline-any cline-yes">2x</span>
1489
+ <span class="cline-any cline-neutral">&nbsp;</span>
1490
+ <span class="cline-any cline-neutral">&nbsp;</span>
1491
+ <span class="cline-any cline-neutral">&nbsp;</span>
1492
+ <span class="cline-any cline-yes">4x</span>
1493
+ <span class="cline-any cline-yes">4x</span>
1494
+ <span class="cline-any cline-neutral">&nbsp;</span>
1495
+ <span class="cline-any cline-neutral">&nbsp;</span>
1496
+ <span class="cline-any cline-yes">4x</span>
1497
+ <span class="cline-any cline-neutral">&nbsp;</span>
1498
+ <span class="cline-any cline-neutral">&nbsp;</span>
1499
+ <span class="cline-any cline-neutral">&nbsp;</span>
1500
+ <span class="cline-any cline-neutral">&nbsp;</span>
1501
+ <span class="cline-any cline-neutral">&nbsp;</span>
1502
+ <span class="cline-any cline-neutral">&nbsp;</span>
1503
+ <span class="cline-any cline-neutral">&nbsp;</span>
1504
+ <span class="cline-any cline-neutral">&nbsp;</span>
1505
+ <span class="cline-any cline-neutral">&nbsp;</span>
1506
+ <span class="cline-any cline-neutral">&nbsp;</span>
1507
+ <span class="cline-any cline-neutral">&nbsp;</span>
1508
+ <span class="cline-any cline-neutral">&nbsp;</span>
1509
+ <span class="cline-any cline-neutral">&nbsp;</span>
1510
+ <span class="cline-any cline-neutral">&nbsp;</span>
1511
+ <span class="cline-any cline-neutral">&nbsp;</span>
1512
+ <span class="cline-any cline-neutral">&nbsp;</span>
1513
+ <span class="cline-any cline-neutral">&nbsp;</span>
1514
+ <span class="cline-any cline-neutral">&nbsp;</span>
1515
+ <span class="cline-any cline-neutral">&nbsp;</span>
1516
+ <span class="cline-any cline-neutral">&nbsp;</span>
1517
+ <span class="cline-any cline-neutral">&nbsp;</span>
1518
+ <span class="cline-any cline-neutral">&nbsp;</span>
1519
+ <span class="cline-any cline-neutral">&nbsp;</span>
1520
+ <span class="cline-any cline-neutral">&nbsp;</span>
1521
+ <span class="cline-any cline-neutral">&nbsp;</span>
1522
+ <span class="cline-any cline-neutral">&nbsp;</span>
1523
+ <span class="cline-any cline-neutral">&nbsp;</span>
1524
+ <span class="cline-any cline-neutral">&nbsp;</span>
1525
+ <span class="cline-any cline-neutral">&nbsp;</span>
1526
+ <span class="cline-any cline-neutral">&nbsp;</span>
1527
+ <span class="cline-any cline-neutral">&nbsp;</span>
1528
+ <span class="cline-any cline-neutral">&nbsp;</span>
1529
+ <span class="cline-any cline-neutral">&nbsp;</span>
1530
+ <span class="cline-any cline-neutral">&nbsp;</span>
1531
+ <span class="cline-any cline-neutral">&nbsp;</span>
1532
+ <span class="cline-any cline-neutral">&nbsp;</span>
1533
+ <span class="cline-any cline-neutral">&nbsp;</span>
1534
+ <span class="cline-any cline-neutral">&nbsp;</span>
1535
+ <span class="cline-any cline-neutral">&nbsp;</span>
1536
+ <span class="cline-any cline-neutral">&nbsp;</span>
1537
+ <span class="cline-any cline-neutral">&nbsp;</span>
1538
+ <span class="cline-any cline-neutral">&nbsp;</span>
1539
+ <span class="cline-any cline-neutral">&nbsp;</span>
1540
+ <span class="cline-any cline-neutral">&nbsp;</span>
1541
+ <span class="cline-any cline-neutral">&nbsp;</span>
1542
+ <span class="cline-any cline-yes">7x</span>
1543
+ <span class="cline-any cline-neutral">&nbsp;</span>
1544
+ <span class="cline-any cline-yes">7x</span>
1545
+ <span class="cline-any cline-neutral">&nbsp;</span>
1546
+ <span class="cline-any cline-yes">8x</span>
1547
+ <span class="cline-any cline-yes">1x</span>
1548
+ <span class="cline-any cline-neutral">&nbsp;</span>
1549
+ <span class="cline-any cline-yes">8x</span>
1550
+ <span class="cline-any cline-yes">7x</span>
1551
+ <span class="cline-any cline-neutral">&nbsp;</span>
1552
+ <span class="cline-any cline-neutral">&nbsp;</span>
1553
+ <span class="cline-any cline-neutral">&nbsp;</span>
1554
+ <span class="cline-any cline-neutral">&nbsp;</span>
1555
+ <span class="cline-any cline-neutral">&nbsp;</span>
1556
+ <span class="cline-any cline-neutral">&nbsp;</span>
1557
+ <span class="cline-any cline-neutral">&nbsp;</span>
1558
+ <span class="cline-any cline-yes">8x</span>
1559
+ <span class="cline-any cline-yes">1x</span>
1560
+ <span class="cline-any cline-yes">1x</span>
1561
+ <span class="cline-any cline-neutral">&nbsp;</span>
1562
+ <span class="cline-any cline-neutral">&nbsp;</span>
1563
+ <span class="cline-any cline-neutral">&nbsp;</span>
1564
+ <span class="cline-any cline-yes">1x</span>
1565
+ <span class="cline-any cline-yes">1x</span>
1566
+ <span class="cline-any cline-neutral">&nbsp;</span>
1567
+ <span class="cline-any cline-neutral">&nbsp;</span>
1568
+ <span class="cline-any cline-neutral">&nbsp;</span>
1569
+ <span class="cline-any cline-neutral">&nbsp;</span>
1570
+ <span class="cline-any cline-neutral">&nbsp;</span>
1571
+ <span class="cline-any cline-neutral">&nbsp;</span>
1572
+ <span class="cline-any cline-neutral">&nbsp;</span>
1573
+ <span class="cline-any cline-neutral">&nbsp;</span>
1574
+ <span class="cline-any cline-neutral">&nbsp;</span>
1575
+ <span class="cline-any cline-yes">7x</span>
1576
+ <span class="cline-any cline-yes">2x</span>
1577
+ <span class="cline-any cline-yes">1x</span>
1578
+ <span class="cline-any cline-neutral">&nbsp;</span>
1579
+ <span class="cline-any cline-neutral">&nbsp;</span>
1580
+ <span class="cline-any cline-neutral">&nbsp;</span>
1581
+ <span class="cline-any cline-neutral">&nbsp;</span>
1582
+ <span class="cline-any cline-neutral">&nbsp;</span>
1583
+ <span class="cline-any cline-yes">7x</span>
1584
+ <span class="cline-any cline-yes">1x</span>
1585
+ <span class="cline-any cline-neutral">&nbsp;</span>
1586
+ <span class="cline-any cline-neutral">&nbsp;</span>
1587
+ <span class="cline-any cline-neutral">&nbsp;</span>
1588
+ <span class="cline-any cline-neutral">&nbsp;</span>
1589
+ <span class="cline-any cline-yes">6x</span>
1590
+ <span class="cline-any cline-yes">6x</span>
1591
+ <span class="cline-any cline-yes">6x</span>
1592
+ <span class="cline-any cline-yes">13x</span>
1593
+ <span class="cline-any cline-neutral">&nbsp;</span>
1594
+ <span class="cline-any cline-neutral">&nbsp;</span>
1595
+ <span class="cline-any cline-yes">13x</span>
1596
+ <span class="cline-any cline-neutral">&nbsp;</span>
1597
+ <span class="cline-any cline-yes">17x</span>
1598
+ <span class="cline-any cline-yes">7x</span>
1599
+ <span class="cline-any cline-neutral">&nbsp;</span>
1600
+ <span class="cline-any cline-neutral">&nbsp;</span>
1601
+ <span class="cline-any cline-neutral">&nbsp;</span>
1602
+ <span class="cline-any cline-yes">10x</span>
1603
+ <span class="cline-any cline-yes">4x</span>
1604
+ <span class="cline-any cline-neutral">&nbsp;</span>
1605
+ <span class="cline-any cline-neutral">&nbsp;</span>
1606
+ <span class="cline-any cline-yes">6x</span>
1607
+ <span class="cline-any cline-neutral">&nbsp;</span>
1608
+ <span class="cline-any cline-neutral">&nbsp;</span>
1609
+ <span class="cline-any cline-yes">13x</span>
1610
+ <span class="cline-any cline-yes">7x</span>
1611
+ <span class="cline-any cline-yes">7x</span>
1612
+ <span class="cline-any cline-neutral">&nbsp;</span>
1613
+ <span class="cline-any cline-yes">6x</span>
1614
+ <span class="cline-any cline-neutral">&nbsp;</span>
1615
+ <span class="cline-any cline-neutral">&nbsp;</span>
1616
+ <span class="cline-any cline-neutral">&nbsp;</span>
1617
+ <span class="cline-any cline-yes">6x</span>
1618
+ <span class="cline-any cline-yes">1x</span>
1619
+ <span class="cline-any cline-yes">1x</span>
1620
+ <span class="cline-any cline-yes">1x</span>
1621
+ <span class="cline-any cline-yes">1x</span>
1622
+ <span class="cline-any cline-yes">1x</span>
1623
+ <span class="cline-any cline-neutral">&nbsp;</span>
1624
+ <span class="cline-any cline-neutral">&nbsp;</span>
1625
+ <span class="cline-any cline-neutral">&nbsp;</span>
1626
+ <span class="cline-any cline-neutral">&nbsp;</span>
1627
+ <span class="cline-any cline-yes">6x</span>
1628
+ <span class="cline-any cline-neutral">&nbsp;</span>
1629
+ <span class="cline-any cline-neutral">&nbsp;</span>
1630
+ <span class="cline-any cline-neutral">&nbsp;</span>
1631
+ <span class="cline-any cline-neutral">&nbsp;</span>
1632
+ <span class="cline-any cline-neutral">&nbsp;</span>
1633
+ <span class="cline-any cline-neutral">&nbsp;</span>
1634
+ <span class="cline-any cline-neutral">&nbsp;</span>
1635
+ <span class="cline-any cline-neutral">&nbsp;</span>
1636
+ <span class="cline-any cline-neutral">&nbsp;</span>
1637
+ <span class="cline-any cline-neutral">&nbsp;</span>
1638
+ <span class="cline-any cline-neutral">&nbsp;</span>
1639
+ <span class="cline-any cline-yes">4x</span>
1640
+ <span class="cline-any cline-neutral">&nbsp;</span>
1641
+ <span class="cline-any cline-yes">4x</span>
1642
+ <span class="cline-any cline-yes">4x</span>
1643
+ <span class="cline-any cline-yes">2x</span>
1644
+ <span class="cline-any cline-yes">2x</span>
1645
+ <span class="cline-any cline-neutral">&nbsp;</span>
1646
+ <span class="cline-any cline-neutral">&nbsp;</span>
1647
+ <span class="cline-any cline-yes">4x</span>
1648
+ <span class="cline-any cline-yes">5x</span>
1649
+ <span class="cline-any cline-neutral">&nbsp;</span>
1650
+ <span class="cline-any cline-neutral">&nbsp;</span>
1651
+ <span class="cline-any cline-yes">5x</span>
1652
+ <span class="cline-any cline-yes">2x</span>
1653
+ <span class="cline-any cline-neutral">&nbsp;</span>
1654
+ <span class="cline-any cline-neutral">&nbsp;</span>
1655
+ <span class="cline-any cline-neutral">&nbsp;</span>
1656
+ <span class="cline-any cline-yes">3x</span>
1657
+ <span class="cline-any cline-neutral">&nbsp;</span>
1658
+ <span class="cline-any cline-neutral">&nbsp;</span>
1659
+ <span class="cline-any cline-yes">3x</span>
1660
+ <span class="cline-any cline-yes">3x</span>
1661
+ <span class="cline-any cline-neutral">&nbsp;</span>
1662
+ <span class="cline-any cline-neutral">&nbsp;</span>
1663
+ <span class="cline-any cline-yes">3x</span>
1664
+ <span class="cline-any cline-neutral">&nbsp;</span>
1665
+ <span class="cline-any cline-neutral">&nbsp;</span>
1666
+ <span class="cline-any cline-neutral">&nbsp;</span>
1667
+ <span class="cline-any cline-neutral">&nbsp;</span>
1668
+ <span class="cline-any cline-neutral">&nbsp;</span>
1669
+ <span class="cline-any cline-neutral">&nbsp;</span>
1670
+ <span class="cline-any cline-neutral">&nbsp;</span>
1671
+ <span class="cline-any cline-yes">5x</span>
1672
+ <span class="cline-any cline-yes">5x</span>
1673
+ <span class="cline-any cline-neutral">&nbsp;</span>
1674
+ <span class="cline-any cline-yes">5x</span>
1675
+ <span class="cline-any cline-neutral">&nbsp;</span>
1676
+ <span class="cline-any cline-yes">5x</span>
1677
+ <span class="cline-any cline-neutral">&nbsp;</span>
1678
+ <span class="cline-any cline-yes">26x</span>
1679
+ <span class="cline-any cline-yes">26x</span>
1680
+ <span class="cline-any cline-yes">6x</span>
1681
+ <span class="cline-any cline-yes">6x</span>
1682
+ <span class="cline-any cline-neutral">&nbsp;</span>
1683
+ <span class="cline-any cline-neutral">&nbsp;</span>
1684
+ <span class="cline-any cline-yes">20x</span>
1685
+ <span class="cline-any cline-neutral">&nbsp;</span>
1686
+ <span class="cline-any cline-yes">7x</span>
1687
+ <span class="cline-any cline-yes">7x</span>
1688
+ <span class="cline-any cline-yes">13x</span>
1689
+ <span class="cline-any cline-neutral">&nbsp;</span>
1690
+ <span class="cline-any cline-neutral">&nbsp;</span>
1691
+ <span class="cline-any cline-neutral">&nbsp;</span>
1692
+ <span class="cline-any cline-yes">12x</span>
1693
+ <span class="cline-any cline-neutral">&nbsp;</span>
1694
+ <span class="cline-any cline-neutral">&nbsp;</span>
1695
+ <span class="cline-any cline-neutral">&nbsp;</span>
1696
+ <span class="cline-any cline-yes">5x</span>
1697
+ <span class="cline-any cline-neutral">&nbsp;</span>
1698
+ <span class="cline-any cline-neutral">&nbsp;</span>
1699
+ <span class="cline-any cline-neutral">&nbsp;</span>
1700
+ <span class="cline-any cline-neutral">&nbsp;</span>
1701
+ <span class="cline-any cline-neutral">&nbsp;</span>
1702
+ <span class="cline-any cline-neutral">&nbsp;</span>
1703
+ <span class="cline-any cline-neutral">&nbsp;</span>
1704
+ <span class="cline-any cline-yes">6x</span>
1705
+ <span class="cline-any cline-yes">6x</span>
1706
+ <span class="cline-any cline-neutral">&nbsp;</span>
1707
+ <span class="cline-any cline-yes">6x</span>
1708
+ <span class="cline-any cline-yes">2x</span>
1709
+ <span class="cline-any cline-yes">2x</span>
1710
+ <span class="cline-any cline-yes">2x</span>
1711
+ <span class="cline-any cline-yes">5x</span>
1712
+ <span class="cline-any cline-neutral">&nbsp;</span>
1713
+ <span class="cline-any cline-neutral">&nbsp;</span>
1714
+ <span class="cline-any cline-yes">4x</span>
1715
+ <span class="cline-any cline-yes">4x</span>
1716
+ <span class="cline-any cline-yes">3x</span>
1717
+ <span class="cline-any cline-neutral">&nbsp;</span>
1718
+ <span class="cline-any cline-neutral">&nbsp;</span>
1719
+ <span class="cline-any cline-neutral">&nbsp;</span>
1720
+ <span class="cline-any cline-yes">6x</span>
1721
+ <span class="cline-any cline-neutral">&nbsp;</span>
1722
+ <span class="cline-any cline-neutral">&nbsp;</span>
1723
+ <span class="cline-any cline-neutral">&nbsp;</span>
1724
+ <span class="cline-any cline-neutral">&nbsp;</span>
1725
+ <span class="cline-any cline-neutral">&nbsp;</span>
1726
+ <span class="cline-any cline-neutral">&nbsp;</span>
1727
+ <span class="cline-any cline-yes">2x</span>
1728
+ <span class="cline-any cline-neutral">&nbsp;</span>
1729
+ <span class="cline-any cline-yes">2x</span>
1730
+ <span class="cline-any cline-yes">2x</span>
1731
+ <span class="cline-any cline-neutral">&nbsp;</span>
1732
+ <span class="cline-any cline-neutral">&nbsp;</span>
1733
+ <span class="cline-any cline-yes">2x</span>
1734
+ <span class="cline-any cline-yes">2x</span>
1735
+ <span class="cline-any cline-yes">2x</span>
1736
+ <span class="cline-any cline-neutral">&nbsp;</span>
1737
+ <span class="cline-any cline-yes">2x</span>
1738
+ <span class="cline-any cline-yes">1x</span>
1739
+ <span class="cline-any cline-yes">1x</span>
1740
+ <span class="cline-any cline-yes">1x</span>
1741
+ <span class="cline-any cline-neutral">&nbsp;</span>
1742
+ <span class="cline-any cline-neutral">&nbsp;</span>
1743
+ <span class="cline-any cline-yes">2x</span>
1744
+ <span class="cline-any cline-neutral">&nbsp;</span>
1745
+ <span class="cline-any cline-neutral">&nbsp;</span>
1746
+ <span class="cline-any cline-neutral">&nbsp;</span>
1747
+ <span class="cline-any cline-yes">2x</span>
1748
+ <span class="cline-any cline-neutral">&nbsp;</span>
1749
+ <span class="cline-any cline-neutral">&nbsp;</span>
1750
+ <span class="cline-any cline-neutral">&nbsp;</span>
1751
+ <span class="cline-any cline-neutral">&nbsp;</span>
1752
+ <span class="cline-any cline-neutral">&nbsp;</span>
1753
+ <span class="cline-any cline-neutral">&nbsp;</span>
1754
+ <span class="cline-any cline-neutral">&nbsp;</span>
1755
+ <span class="cline-any cline-neutral">&nbsp;</span>
1756
+ <span class="cline-any cline-neutral">&nbsp;</span>
1757
+ <span class="cline-any cline-yes">4x</span>
1758
+ <span class="cline-any cline-yes">4x</span>
1759
+ <span class="cline-any cline-yes">5x</span>
1760
+ <span class="cline-any cline-yes">2x</span>
1761
+ <span class="cline-any cline-neutral">&nbsp;</span>
1762
+ <span class="cline-any cline-neutral">&nbsp;</span>
1763
+ <span class="cline-any cline-neutral">&nbsp;</span>
1764
+ <span class="cline-any cline-yes">2x</span>
1765
+ <span class="cline-any cline-neutral">&nbsp;</span>
1766
+ <span class="cline-any cline-neutral">&nbsp;</span>
1767
+ <span class="cline-any cline-neutral">&nbsp;</span>
1768
+ <span class="cline-any cline-neutral">&nbsp;</span>
1769
+ <span class="cline-any cline-neutral">&nbsp;</span>
1770
+ <span class="cline-any cline-neutral">&nbsp;</span>
1771
+ <span class="cline-any cline-yes">2x</span>
1772
+ <span class="cline-any cline-yes">2x</span>
1773
+ <span class="cline-any cline-yes">2x</span>
1774
+ <span class="cline-any cline-neutral">&nbsp;</span>
1775
+ <span class="cline-any cline-yes">1x</span>
1776
+ <span class="cline-any cline-neutral">&nbsp;</span>
1777
+ <span class="cline-any cline-yes">1x</span>
1778
+ <span class="cline-any cline-neutral">&nbsp;</span>
1779
+ <span class="cline-any cline-neutral">&nbsp;</span>
1780
+ <span class="cline-any cline-neutral">&nbsp;</span>
1781
+ <span class="cline-any cline-neutral">&nbsp;</span>
1782
+ <span class="cline-any cline-neutral">&nbsp;</span>
1783
+ <span class="cline-any cline-neutral">&nbsp;</span>
1784
+ <span class="cline-any cline-neutral">&nbsp;</span>
1785
+ <span class="cline-any cline-neutral">&nbsp;</span>
1786
+ <span class="cline-any cline-neutral">&nbsp;</span>
1787
+ <span class="cline-any cline-neutral">&nbsp;</span>
1788
+ <span class="cline-any cline-yes">3x</span>
1789
+ <span class="cline-any cline-neutral">&nbsp;</span>
1790
+ <span class="cline-any cline-yes">3x</span>
1791
+ <span class="cline-any cline-neutral">&nbsp;</span>
1792
+ <span class="cline-any cline-yes">3x</span>
1793
+ <span class="cline-any cline-yes">5x</span>
1794
+ <span class="cline-any cline-yes">5x</span>
1795
+ <span class="cline-any cline-yes">6x</span>
1796
+ <span class="cline-any cline-yes">6x</span>
1797
+ <span class="cline-any cline-neutral">&nbsp;</span>
1798
+ <span class="cline-any cline-yes">6x</span>
1799
+ <span class="cline-any cline-neutral">&nbsp;</span>
1800
+ <span class="cline-any cline-yes">2x</span>
1801
+ <span class="cline-any cline-yes">2x</span>
1802
+ <span class="cline-any cline-neutral">&nbsp;</span>
1803
+ <span class="cline-any cline-yes">2x</span>
1804
+ <span class="cline-any cline-neutral">&nbsp;</span>
1805
+ <span class="cline-any cline-neutral">&nbsp;</span>
1806
+ <span class="cline-any cline-neutral">&nbsp;</span>
1807
+ <span class="cline-any cline-yes">6x</span>
1808
+ <span class="cline-any cline-neutral">&nbsp;</span>
1809
+ <span class="cline-any cline-neutral">&nbsp;</span>
1810
+ <span class="cline-any cline-neutral">&nbsp;</span>
1811
+ <span class="cline-any cline-neutral">&nbsp;</span>
1812
+ <span class="cline-any cline-yes">3x</span>
1813
+ <span class="cline-any cline-yes">1x</span>
1814
+ <span class="cline-any cline-neutral">&nbsp;</span>
1815
+ <span class="cline-any cline-neutral">&nbsp;</span>
1816
+ <span class="cline-any cline-neutral">&nbsp;</span>
1817
+ <span class="cline-any cline-yes">2x</span>
1818
+ <span class="cline-any cline-yes">2x</span>
1819
+ <span class="cline-any cline-yes">4x</span>
1820
+ <span class="cline-any cline-yes">4x</span>
1821
+ <span class="cline-any cline-neutral">&nbsp;</span>
1822
+ <span class="cline-any cline-yes">4x</span>
1823
+ <span class="cline-any cline-yes">2x</span>
1824
+ <span class="cline-any cline-neutral">&nbsp;</span>
1825
+ <span class="cline-any cline-yes">3x</span>
1826
+ <span class="cline-any cline-yes">2x</span>
1827
+ <span class="cline-any cline-yes">1x</span>
1828
+ <span class="cline-any cline-neutral">&nbsp;</span>
1829
+ <span class="cline-any cline-neutral">&nbsp;</span>
1830
+ <span class="cline-any cline-neutral">&nbsp;</span>
1831
+ <span class="cline-any cline-neutral">&nbsp;</span>
1832
+ <span class="cline-any cline-yes">4x</span>
1833
+ <span class="cline-any cline-neutral">&nbsp;</span>
1834
+ <span class="cline-any cline-neutral">&nbsp;</span>
1835
+ <span class="cline-any cline-neutral">&nbsp;</span>
1836
+ <span class="cline-any cline-neutral">&nbsp;</span>
1837
+ <span class="cline-any cline-neutral">&nbsp;</span>
1838
+ <span class="cline-any cline-neutral">&nbsp;</span>
1839
+ <span class="cline-any cline-neutral">&nbsp;</span>
1840
+ <span class="cline-any cline-neutral">&nbsp;</span>
1841
+ <span class="cline-any cline-neutral">&nbsp;</span>
1842
+ <span class="cline-any cline-neutral">&nbsp;</span>
1843
+ <span class="cline-any cline-yes">4x</span>
1844
+ <span class="cline-any cline-neutral">&nbsp;</span>
1845
+ <span class="cline-any cline-yes">4x</span>
1846
+ <span class="cline-any cline-yes">4x</span>
1847
+ <span class="cline-any cline-neutral">&nbsp;</span>
1848
+ <span class="cline-any cline-yes">4x</span>
1849
+ <span class="cline-any cline-yes">4x</span>
1850
+ <span class="cline-any cline-neutral">&nbsp;</span>
1851
+ <span class="cline-any cline-neutral">&nbsp;</span>
1852
+ <span class="cline-any cline-neutral">&nbsp;</span>
1853
+ <span class="cline-any cline-yes">4x</span>
1854
+ <span class="cline-any cline-yes">4x</span>
1855
+ <span class="cline-any cline-neutral">&nbsp;</span>
1856
+ <span class="cline-any cline-yes">4x</span>
1857
+ <span class="cline-any cline-yes">2x</span>
1858
+ <span class="cline-any cline-neutral">&nbsp;</span>
1859
+ <span class="cline-any cline-neutral">&nbsp;</span>
1860
+ <span class="cline-any cline-yes">2x</span>
1861
+ <span class="cline-any cline-neutral">&nbsp;</span>
1862
+ <span class="cline-any cline-neutral">&nbsp;</span>
1863
+ <span class="cline-any cline-neutral">&nbsp;</span>
1864
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import {
1865
+ type ChangedFile,
1866
+ type VerboseLevel,
1867
+ shouldLog,
1868
+ GitProviderService,
1869
+ parseRepoUrl,
1870
+ type RemoteRepoRef,
1871
+ type RepositoryContent,
1872
+ } from "@spaceflow/core";
1873
+ import { readdir, readFile, mkdir, access, writeFile } from "fs/promises";
1874
+ import { join, basename, extname } from "path";
1875
+ import { homedir } from "os";
1876
+ import { execSync } from "child_process";
1877
+ import micromatch from "micromatch";
1878
+ import { ReviewSpec, ReviewRule, RuleExample, Severity } from "./types";
1879
+ &nbsp;
1880
+ /** 远程规则缓存 TTL(毫秒),默认 5 分钟 */
1881
+ const REMOTE_SPEC_CACHE_TTL = 5 * 60 * 1000;
1882
+ &nbsp;
1883
+ export class ReviewSpecService {
1884
+ constructor(protected readonly gitProvider?: GitProviderService) {}
1885
+ /**
1886
+ * 检查规则 ID 是否匹配(精确匹配或前缀匹配)
1887
+ * 例如: "JsTs.FileName" 匹配 "JsTs.FileName" 和 "JsTs.FileName.UpperCamel"
1888
+ */
1889
+ protected matchRuleId(ruleId: string, pattern: string): boolean {
1890
+ if (!ruleId || !pattern) {
1891
+ console.warn(
1892
+ `matchRuleId: 参数为空 (ruleId=${JSON.stringify(ruleId)}, pattern=${JSON.stringify(pattern)})`,
1893
+ );
1894
+ return false;
1895
+ }
1896
+ return ruleId === pattern || ruleId.startsWith(pattern + ".");
1897
+ }
1898
+ &nbsp;
1899
+ /**
1900
+ * 从 Map 中查找匹配的规则值(精确匹配优先,然后前缀匹配)
1901
+ */
1902
+ protected findByRuleId&lt;T&gt;(ruleId: string, map: Map&lt;string, T&gt;): T | undefined {
1903
+ if (!ruleId) {
1904
+ console.warn(`findByRuleId: ruleId 为空 (ruleId=${JSON.stringify(ruleId)})`);
1905
+ return undefined;
1906
+ }
1907
+ // 精确匹配
1908
+ if (map.has(ruleId)) {
1909
+ return map.get(ruleId);
1910
+ }
1911
+ // 前缀匹配
1912
+ for (const [key, value] of map) {
1913
+ if (ruleId.startsWith(key + ".")) {
1914
+ return value;
1915
+ }
1916
+ }
1917
+ return undefined;
1918
+ }
1919
+ /**
1920
+ * 根据变更文件的扩展名过滤适用的规则文件
1921
+ * 只按扩展名过滤,includes 和 override 在 LLM 审查后处理
1922
+ */
1923
+ filterApplicableSpecs(specs: ReviewSpec[], changedFiles: { filename?: string }[]): ReviewSpec[] {
1924
+ const changedExtensions = new Set&lt;string&gt;();
1925
+ &nbsp;
1926
+ for (const file of changedFiles) {
1927
+ if (file.filename) {
1928
+ const ext = extname(file.filename).slice(1).toLowerCase();
1929
+ if (ext) {
1930
+ changedExtensions.add(ext);
1931
+ }
1932
+ }
1933
+ }
1934
+ &nbsp;
1935
+ return specs.filter((spec) =&gt; spec.extensions.some((ext) =&gt; changedExtensions.has(ext)));
1936
+ }
1937
+ &nbsp;
1938
+ async loadReviewSpecs(specDir: string): Promise&lt;ReviewSpec[]&gt; {
1939
+ const specs: ReviewSpec[] = [];
1940
+ &nbsp;
1941
+ try {
1942
+ const files = await readdir(specDir);
1943
+ &nbsp;
1944
+ for (const file of files) {
1945
+ if (!file.endsWith(".md")) continue;
1946
+ &nbsp;
1947
+ const content = await readFile(join(specDir, file), "utf-8");
1948
+ const spec = this.parseSpecFile(file, content);
1949
+ if (spec) {
1950
+ specs.push(spec);
1951
+ }
1952
+ }
1953
+ } catch (error) {
1954
+ // 目录不存在时静默跳过(这些是可选的配置目录)
1955
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
1956
+ console.warn(`警告: 无法读取规则目录 ${specDir}:`, error);
1957
+ }
1958
+ }
1959
+ &nbsp;
1960
+ return specs;
1961
+ }
1962
+ &nbsp;
1963
+ async resolveSpecSources(sources: string[]): Promise&lt;string[]&gt; {
1964
+ const dirs: string[] = [];
1965
+ &nbsp;
1966
+ for (const source of sources) {
1967
+ // 优先尝试解析为远程仓库 URL(浏览器复制的链接)
1968
+ const repoRef = parseRepoUrl(source);
1969
+ if (repoRef &amp;&amp; this.gitProvider) {
1970
+ const dir = await this.fetchRemoteSpecs(repoRef);
1971
+ if (dir) {
1972
+ dirs.push(dir);
1973
+ continue;
1974
+ }
1975
+ }
1976
+ if (this.isRepoUrl(source)) {
1977
+ const dir = await this.cloneSpecRepo(source);
1978
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (dir) {
1979
+ dirs.push(dir);
1980
+ }
1981
+ } else {
1982
+ // 检查是否是 deps 目录,如果是则扫描子目录的 references
1983
+ const resolvedDirs = await this.resolveDepsDir(source);
1984
+ dirs.push(...resolvedDirs);
1985
+ }
1986
+ }
1987
+ &nbsp;
1988
+ return dirs;
1989
+ }
1990
+ &nbsp;
1991
+ /**
1992
+ * 通过 Git API 从远程仓库拉取规则文件
1993
+ * 缓存到 ~/.spaceflow/review-spec-cache/ 目录,带 TTL
1994
+ */
1995
+ protected async fetchRemoteSpecs(ref: RemoteRepoRef): Promise&lt;string | null&gt; {
1996
+ const cacheKey = `${ref.owner}__${ref.repo}${ref.path ? `__${ref.path.replace(/\//g, "_")}` : ""}${ref.ref ? `@${ref.ref}` : ""}`;
1997
+ const cacheDir = join(homedir(), ".spaceflow", "review-spec-cache", cacheKey);
1998
+ // 检查缓存是否有效(非 CI 环境下使用 TTL)
1999
+ const isCI = !!process.env.CI;
2000
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!isCI) {
2001
+ try {
2002
+ const timestampFile = join(cacheDir, ".timestamp");
2003
+ const timestamp = await readFile(timestampFile, "utf-8");
2004
+ const age = Date.now() - Number(timestamp);
2005
+ if (age &lt; REMOTE_SPEC_CACHE_TTL) {
2006
+ const entries = await readdir(cacheDir);
2007
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (entries.some((f) =&gt; f.endsWith(".md"))) {
2008
+ return cacheDir;
2009
+ }
2010
+ }
2011
+ } catch {
2012
+ // 缓存不存在或无效,继续拉取
2013
+ }
2014
+ }
2015
+ try {
2016
+ console.log(
2017
+ ` 📡 从远程仓库拉取规则: ${ref.owner}/${ref.repo}${ref.path ? `/${ref.path}` : ""}${ref.ref ? `@${ref.ref}` : ""}`,
2018
+ );
2019
+ const contents = await this.gitProvider!.listRepositoryContents(
2020
+ ref.owner,
2021
+ ref.repo,
2022
+ ref.path || undefined,
2023
+ ref.ref,
2024
+ );
2025
+ const mdFiles = contents.filter(
2026
+ (f: RepositoryContent) =&gt; f.type === "file" &amp;&amp; f.name.endsWith(".md"),
2027
+ );
2028
+ if (mdFiles.length === 0) {
2029
+ console.warn(` ⚠️ 远程目录中未找到 .md 规则文件`);
2030
+ return null;
2031
+ }
2032
+ await mkdir(cacheDir, { recursive: true });
2033
+ for (const file of mdFiles) {
2034
+ const content = await this.gitProvider!.getFileContent(
2035
+ ref.owner,
2036
+ ref.repo,
2037
+ file.path,
2038
+ ref.ref,
2039
+ );
2040
+ await writeFile(join(cacheDir, file.name), content, "utf-8");
2041
+ }
2042
+ // 写入时间戳
2043
+ await writeFile(join(cacheDir, ".timestamp"), String(Date.now()), "utf-8");
2044
+ console.log(` ✅ 已拉取 ${mdFiles.length} 个规则文件到缓存`);
2045
+ return cacheDir;
2046
+ } catch (error) {
2047
+ console.warn(` ⚠️ 远程规则拉取失败:`, error instanceof Error ? error.message : <span class="branch-1 cbranch-no" title="branch not covered" >error);</span>
2048
+ // 尝试使用过期缓存
2049
+ try {
2050
+ const entries = await readdir(cacheDir);
2051
+ if (entries.some((f) =&gt; f.endsWith(".md"))) {
2052
+ console.log(` 📦 使用过期缓存`);
2053
+ return cacheDir;
2054
+ }
2055
+ } catch {
2056
+ // 无缓存可用
2057
+ }
2058
+ return null;
2059
+ }
2060
+ }
2061
+ &nbsp;
2062
+ /**
2063
+ * 解析 deps 目录,扫描子目录中的 references 文件夹
2064
+ * 如果目录本身包含 .md 文件则直接返回,否则扫描子目录
2065
+ */
2066
+ protected async resolveDepsDir(dir: string): Promise&lt;string[]&gt; {
2067
+ const dirs: string[] = [];
2068
+ &nbsp;
2069
+ try {
2070
+ const entries = await readdir(dir, { withFileTypes: true });
2071
+ &nbsp;
2072
+ // 检查目录本身是否包含 .md 文件
2073
+ const hasMdFiles = entries.some((e) =&gt; e.isFile() &amp;&amp; e.name.endsWith(".md"));
2074
+ if (hasMdFiles) {
2075
+ dirs.push(dir);
2076
+ return dirs;
2077
+ }
2078
+ &nbsp;
2079
+ // 扫描子目录
2080
+ for (const entry of entries) {
2081
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (entry.isDirectory()) {
2082
+ const subDir = join(dir, entry.name);
2083
+ // 优先检查 references 子目录
2084
+ const referencesDir = join(subDir, "references");
2085
+ try {
2086
+ await access(referencesDir);
2087
+ dirs.push(referencesDir);
2088
+ } catch {
2089
+ // 没有 references 子目录,检查子目录本身是否有 .md 文件
2090
+ try {
2091
+ const subEntries = await readdir(subDir);
2092
+ if (subEntries.some((f) =&gt; f.endsWith(".md"))) {
2093
+ dirs.push(subDir);
2094
+ }
2095
+ } catch {
2096
+ // 忽略无法读取的子目录
2097
+ }
2098
+ }
2099
+ }
2100
+ }
2101
+ } catch {
2102
+ // 目录不存在时静默跳过
2103
+ }
2104
+ &nbsp;
2105
+ return dirs;
2106
+ }
2107
+ &nbsp;
2108
+ protected isRepoUrl(source: string): boolean {
2109
+ return (
2110
+ source.startsWith("http://") ||
2111
+ source.startsWith("https://") ||
2112
+ source.startsWith("git@") ||
2113
+ source.includes("://")
2114
+ );
2115
+ }
2116
+ &nbsp;
2117
+ protected async cloneSpecRepo(repoUrl: string): Promise&lt;string | null&gt; {
2118
+ const repoName = this.extractRepoName(repoUrl);
2119
+ if (!repoName) {
2120
+ console.warn(`警告: 无法解析仓库名称: ${repoUrl}`);
2121
+ return null;
2122
+ }
2123
+ &nbsp;
2124
+ const cacheDir = join(homedir(), ".spaceflow", "review-spec", repoName);
2125
+ &nbsp;
2126
+ try {
2127
+ await access(cacheDir);
2128
+ // console.log(` 使用缓存的规则仓库: ${cacheDir}`);
2129
+ try {
2130
+ execSync("git pull --ff-only", { cwd: cacheDir, stdio: "pipe" });
2131
+ // console.log(` 已更新规则仓库`);
2132
+ } catch {
2133
+ console.warn(` 警告: 无法更新规则仓库,使用现有版本`);
2134
+ }
2135
+ return cacheDir;
2136
+ } catch {
2137
+ // console.log(` 克隆规则仓库: ${repoUrl}`);
2138
+ try {
2139
+ await mkdir(join(homedir(), ".spaceflow", "review-spec"), { recursive: true });
2140
+ execSync(`git clone --depth 1 "${repoUrl}" "${cacheDir}"`, { stdio: "pipe" });
2141
+ // console.log(` 克隆完成: ${cacheDir}`);
2142
+ return cacheDir;
2143
+ } catch (error) {
2144
+ console.warn(`警告: 无法克隆仓库 ${repoUrl}:`, error);
2145
+ return null;
2146
+ }
2147
+ }
2148
+ }
2149
+ &nbsp;
2150
+ protected extractRepoName(repoUrl: string): string | null {
2151
+ let path = repoUrl;
2152
+ path = path.replace(/\.git$/, "");
2153
+ path = path.replace(/^git@[^:]+:/, "");
2154
+ path = path.replace(/^https?:\/\/[^/]+\//, "");
2155
+ &nbsp;
2156
+ const parts = path.split("/").filter(Boolean);
2157
+ if (parts.length &gt;= 2) {
2158
+ return `${parts[parts.length - 2]}__${parts[parts.length - 1]}`;
2159
+ } else if (parts.length === 1) {
2160
+ return parts[0];
2161
+ }
2162
+ return null;
2163
+ }
2164
+ &nbsp;
2165
+ parseSpecFile(filename: string, content: string): ReviewSpec | null {
2166
+ const nameWithoutExt = basename(filename, ".md");
2167
+ const parts = nameWithoutExt.split(".");
2168
+ &nbsp;
2169
+ if (parts.length &lt; 2) {
2170
+ console.warn(`警告: 规则文件名格式不正确: ${filename}`);
2171
+ return null;
2172
+ }
2173
+ &nbsp;
2174
+ const extensionsPart = parts[0];
2175
+ const type = parts.slice(1).join(".");
2176
+ const extensions = extensionsPart.split("&amp;").map((ext) =&gt; ext.toLowerCase());
2177
+ &nbsp;
2178
+ const rules = this.extractRules(content);
2179
+ &nbsp;
2180
+ // 文件级别的 override 来自第一个规则(标题规则)的 overrides
2181
+ const fileOverrides = rules.length &gt; 0 ? rules[0].overrides : <span class="branch-1 cbranch-no" title="branch not covered" >[];</span>
2182
+ // 文件级别的 severity 来自第一个规则(标题规则)的 severity,默认为 error
2183
+ const fileSeverity = (rules.length &gt; 0 ? rules[0].severity : <span class="branch-1 cbranch-no" title="branch not covered" >undefined</span>) || "error";
2184
+ // 文件级别的 includes 从内容中提取
2185
+ const fileIncludes = this.extractIncludes(content);
2186
+ &nbsp;
2187
+ return {
2188
+ filename,
2189
+ extensions,
2190
+ type,
2191
+ content,
2192
+ rules,
2193
+ overrides: fileOverrides,
2194
+ severity: fileSeverity,
2195
+ includes: fileIncludes,
2196
+ };
2197
+ }
2198
+ &nbsp;
2199
+ protected extractRules(content: string): ReviewRule[] {
2200
+ const rules: ReviewRule[] = [];
2201
+ const ruleRegex = /^(#{1,3})\s+(.+?)\s+`\[([^\]]+)\]`/gm;
2202
+ &nbsp;
2203
+ const matches: { index: number; length: number; title: string; id: string }[] = [];
2204
+ let match;
2205
+ while ((match = ruleRegex.exec(content)) !== null) {
2206
+ matches.push({
2207
+ index: match.index,
2208
+ length: match[0].length,
2209
+ title: match[2].trim(),
2210
+ id: match[3],
2211
+ });
2212
+ }
2213
+ &nbsp;
2214
+ for (let i = 0; i &lt; matches.length; i++) {
2215
+ const current = matches[i];
2216
+ const startIndex = current.index + current.length;
2217
+ const endIndex = i + 1 &lt; matches.length ? matches[i + 1].index : content.length;
2218
+ &nbsp;
2219
+ const ruleContent = content.slice(startIndex, endIndex).trim();
2220
+ const examples = this.extractExamples(ruleContent);
2221
+ const overrides = this.extractOverrides(ruleContent);
2222
+ &nbsp;
2223
+ // 提取描述:在第一个例子之前的文本
2224
+ let description = ruleContent;
2225
+ const firstExampleIndex = ruleContent.search(/(?:^|\n)###\s+(?:good|bad)/i);
2226
+ if (firstExampleIndex !== -1) {
2227
+ description = ruleContent.slice(0, firstExampleIndex).trim();
2228
+ } else {
2229
+ // 如果没有例子,则整个 ruleContent 都是描述
2230
+ description = ruleContent;
2231
+ }
2232
+ &nbsp;
2233
+ const severity = this.extractSeverity(ruleContent);
2234
+ const includes = this.extractConfigValues(ruleContent, "includes");
2235
+ &nbsp;
2236
+ rules.push({
2237
+ id: current.id,
2238
+ title: current.title,
2239
+ description,
2240
+ examples,
2241
+ overrides,
2242
+ severity,
2243
+ includes: includes.length &gt; 0 ? includes : undefined,
2244
+ });
2245
+ }
2246
+ &nbsp;
2247
+ return rules;
2248
+ }
2249
+ &nbsp;
2250
+ /**
2251
+ * 通用配置解析方法
2252
+ * 格式: &gt; - &lt;configName&gt; `value1` `value2` ...
2253
+ * 同名配置项后面的覆盖前面的
2254
+ */
2255
+ protected extractConfigValues(content: string, configName: string): string[] {
2256
+ const configRegex = new RegExp(`^&gt;\\s*-\\s*${configName}\\s+(.+)$`, "gm");
2257
+ let values: string[] = [];
2258
+ let match;
2259
+ &nbsp;
2260
+ while ((match = configRegex.exec(content)) !== null) {
2261
+ const valuesStr = match[1];
2262
+ const valueRegex = /`([^`]+)`/g;
2263
+ let valueMatch;
2264
+ const lineValues: string[] = [];
2265
+ while ((valueMatch = valueRegex.exec(valuesStr)) !== null) {
2266
+ lineValues.push(valueMatch[1]);
2267
+ }
2268
+ // 同名配置项覆盖
2269
+ values = lineValues;
2270
+ }
2271
+ &nbsp;
2272
+ return values;
2273
+ }
2274
+ &nbsp;
2275
+ protected extractOverrides(content: string): string[] {
2276
+ // override 的值格式是 `[RuleId]`,需要去掉方括号
2277
+ return this.extractConfigValues(content, "override").map((v) =&gt;
2278
+ v.startsWith("[") &amp;&amp; v.endsWith("]") ? v.slice(1, -1) : <span class="branch-1 cbranch-no" title="branch not covered" >v,</span>
2279
+ );
2280
+ }
2281
+ &nbsp;
2282
+ protected extractSeverity(content: string): Severity | undefined {
2283
+ const values = this.extractConfigValues(content, "severity");
2284
+ if (values.length &gt; 0) {
2285
+ const value = values[values.length - 1];
2286
+ if (value === "off" || value === "warn" || value === "error") {
2287
+ return value;
2288
+ }
2289
+ }
2290
+ return undefined;
2291
+ }
2292
+ &nbsp;
2293
+ protected extractIncludes(content: string): string[] {
2294
+ // 只提取文件开头(第一个 ## 规则标题之前)的 includes 配置
2295
+ // 避免规则级的 includes 覆盖文件级的 includes
2296
+ const firstRuleIndex = content.indexOf("\n## ");
2297
+ const headerContent = firstRuleIndex &gt; 0 ? content.slice(0, firstRuleIndex) : content;
2298
+ return this.extractConfigValues(headerContent, "includes");
2299
+ }
2300
+ &nbsp;
2301
+ protected extractExamples(content: string): RuleExample[] {
2302
+ const examples: RuleExample[] = [];
2303
+ const sections = content.split(/(?:^|\n)###\s+/);
2304
+ &nbsp;
2305
+ for (const section of sections) {
2306
+ const trimmedSection = section.trim();
2307
+ if (!trimmedSection) continue;
2308
+ &nbsp;
2309
+ let type: "good" | "bad" | null = null;
2310
+ if (/^good\b/i.test(trimmedSection)) {
2311
+ type = "good";
2312
+ } else if (/^bad\b/i.test(trimmedSection)) {
2313
+ type = "bad";
2314
+ }
2315
+ &nbsp;
2316
+ if (!type) continue;
2317
+ &nbsp;
2318
+ const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
2319
+ let codeMatch;
2320
+ while ((codeMatch = codeBlockRegex.exec(trimmedSection)) !== null) {
2321
+ const lang = codeMatch[1] || <span class="branch-1 cbranch-no" title="branch not covered" >"text";</span>
2322
+ const code = codeMatch[2].trim();
2323
+ examples.push({ lang, code, type });
2324
+ }
2325
+ }
2326
+ &nbsp;
2327
+ return examples;
2328
+ }
2329
+ &nbsp;
2330
+ /**
2331
+ * 收集所有 override 声明并排除被覆盖的规则
2332
+ * override 使用前缀匹配:如果规则 ID 以 override 值开头,则被排除
2333
+ */
2334
+ applyOverrides(specs: ReviewSpec[], verbose?: VerboseLevel): ReviewSpec[] {
2335
+ // 收集所有 override 声明(文件级别 + 规则级别)
2336
+ const allOverrides: string[] = [];
2337
+ for (const spec of specs) {
2338
+ allOverrides.push(...spec.overrides);
2339
+ for (const rule of spec.rules) {
2340
+ allOverrides.push(...rule.overrides);
2341
+ }
2342
+ }
2343
+ &nbsp;
2344
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (allOverrides.length === 0) {
2345
+ <span class="cstat-no" title="statement not covered" > return specs;</span>
2346
+ }
2347
+ &nbsp;
2348
+ // 过滤规则:排除 ID 以任意 override 值开头的规则
2349
+ return specs
2350
+ .map((spec) =&gt; ({
2351
+ ...spec,
2352
+ rules: spec.rules.filter((rule) =&gt; {
2353
+ const isOverridden = allOverrides.some((override) =&gt; this.matchRuleId(rule.id, override));
2354
+ if (isOverridden &amp;&amp; shouldLog(verbose, 2)) {
2355
+ console.error(` 规则 [${rule.id}] 被 override 排除`);
2356
+ }
2357
+ return !isOverridden;
2358
+ }),
2359
+ }))
2360
+ .filter((spec) =&gt; spec.rules.length &gt; 0);
2361
+ }
2362
+ &nbsp;
2363
+ /**
2364
+ * 根据 spec 的 includes 配置过滤 issues
2365
+ * 只保留文件名匹配对应 spec includes 模式的 issues
2366
+ * 如果 spec 没有 includes 配置,则保留该 spec 的所有 issues
2367
+ */
2368
+ filterIssuesByIncludes&lt;T extends { file: string; ruleId: string }&gt;(
2369
+ issues: T[],
2370
+ specs: ReviewSpec[],
2371
+ ): T[] {
2372
+ // 构建 spec filename -&gt; includes 的映射
2373
+ const specIncludesMap = new Map&lt;string, string[]&gt;();
2374
+ for (const spec of specs) {
2375
+ // 从规则 ID 前缀推断 spec filename
2376
+ for (const rule of spec.rules) {
2377
+ specIncludesMap.set(rule.id, spec.includes);
2378
+ }
2379
+ }
2380
+ &nbsp;
2381
+ return issues.filter((issue) =&gt; {
2382
+ // 找到该 issue 对应的 spec includes
2383
+ const includes = this.findByRuleId(issue.ruleId, specIncludesMap) ?? <span class="branch-1 cbranch-no" title="branch not covered" >[];</span>
2384
+ &nbsp;
2385
+ // 如果没有 includes 配置,保留该 issue
2386
+ if (includes.length === 0) {
2387
+ return true;
2388
+ }
2389
+ &nbsp;
2390
+ // 检查文件是否匹配 includes 模式
2391
+ const matches = micromatch.isMatch(issue.file, includes, { matchBase: true });
2392
+ if (!matches) {
2393
+ // console.log(` Issue [${issue.ruleId}] 在文件 ${issue.file} 不匹配 includes 模式,跳过`);
2394
+ }
2395
+ return matches;
2396
+ });
2397
+ }
2398
+ &nbsp;
2399
+ /**
2400
+ * 根据 override 配置过滤 issues,排除被覆盖规则产生的 issues
2401
+ *
2402
+ * ## Override 机制说明
2403
+ * Override 允许高优先级规则"覆盖"低优先级规则。当规则 A 声明 `overrides: ["B"]` 时,
2404
+ * 规则 B 产生的 issues 会被过滤掉,避免重复报告或低优先级噪音。
2405
+ *
2406
+ * ## 作用域规则
2407
+ * Override 是**作用域感知**的:只有当 issue 的文件匹配 override 所在 spec 的 includes 时,
2408
+ * 该 override 才会生效。这允许不同目录/文件类型使用不同的规则优先级。
2409
+ *
2410
+ * 示例:
2411
+ * ```yaml
2412
+ * # controller-spec.yaml (includes: ["*.controller.ts"])
2413
+ * overrides: ["JsTs.Base.Rule1"] # 只在 controller 文件中覆盖 Rule1
2414
+ * ```
2415
+ * 上述 override 不会影响 `*.service.ts` 文件中的 `Rule1` issues。
2416
+ *
2417
+ * ## 处理流程
2418
+ * 1. **收集阶段**:遍历所有 specs,收集 overrides 并保留其作用域(includes)信息
2419
+ * - 文件级 overrides (`spec.overrides`) - 继承 spec 的 includes 作用域
2420
+ * - 规则级 overrides (`rule.overrides`) - 同样继承 spec 的 includes 作用域
2421
+ * 2. **过滤阶段**:对每个 issue,检查是否存在匹配的 override
2422
+ * - 需同时满足:ruleId 匹配 AND issue 文件在 override 的 includes 作用域内
2423
+ * - 如果 includes 为空,表示全局作用域(匹配所有文件)
2424
+ *
2425
+ * @param issues - 待过滤的 issues 列表,每个 issue 必须包含 ruleId 字段,可选 file 字段
2426
+ * @param specs - 已加载的 ReviewSpec 列表
2427
+ * @param verbose - 日志详细级别:1=基础统计,3=详细收集过程
2428
+ * @returns 过滤后的 issues 列表(排除了被 override 的规则产生的 issues)
2429
+ */
2430
+ filterIssuesByOverrides&lt;T extends { ruleId: string; file?: string }&gt;(
2431
+ issues: T[],
2432
+ specs: ReviewSpec[],
2433
+ verbose?: VerboseLevel,
2434
+ ): T[] {
2435
+ // ========== 阶段1: 收集 spec -&gt; overrides 的映射(保留作用域信息) ==========
2436
+ // 每个 override 需要记录其来源 spec 的 includes,用于作用域判断
2437
+ const scopedOverrides: Array&lt;{
2438
+ override: string;
2439
+ includes: string[];
2440
+ source: string; // 用于日志:spec filename 或 rule id
2441
+ }&gt; = [];
2442
+ &nbsp;
2443
+ for (const spec of specs) {
2444
+ // 文件级 overrides:作用域为该 spec 的 includes
2445
+ if (shouldLog(verbose, 3) &amp;&amp; spec.overrides.length &gt; 0) {
2446
+ console.error(` 📋 ${spec.filename} 文件级 overrides: ${spec.overrides.join(", ")}`);
2447
+ }
2448
+ for (const override of spec.overrides) {
2449
+ scopedOverrides.push({
2450
+ override,
2451
+ includes: spec.includes,
2452
+ source: spec.filename,
2453
+ });
2454
+ }
2455
+ &nbsp;
2456
+ // 规则级 overrides:继承该 spec 的 includes 作用域
2457
+ for (const rule of spec.rules) {
2458
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (shouldLog(verbose, 3) &amp;&amp; rule.overrides.length &gt; 0) {
2459
+ console.error(
2460
+ ` 📋 ${spec.filename} 规则 [${rule.id}] overrides: ${rule.overrides.join(", ")}`,
2461
+ );
2462
+ }
2463
+ for (const override of rule.overrides) {
2464
+ scopedOverrides.push({
2465
+ override,
2466
+ includes: spec.includes,
2467
+ source: `${spec.filename}#${rule.id}`,
2468
+ });
2469
+ }
2470
+ }
2471
+ }
2472
+ &nbsp;
2473
+ // 输出收集结果汇总(verbose=3 时)
2474
+ if (shouldLog(verbose, 3)) {
2475
+ const overrideList = scopedOverrides.map((o) =&gt; o.override);
2476
+ console.error(
2477
+ ` 🔍 收集到的 overrides 总计: ${overrideList.length &gt; 0 ? overrideList.join(", ") : <span class="branch-1 cbranch-no" title="branch not covered" >"(无)"}`,</span>
2478
+ );
2479
+ }
2480
+ &nbsp;
2481
+ // 快速路径:无 override 声明时直接返回原列表
2482
+ if (scopedOverrides.length === 0) {
2483
+ return issues;
2484
+ }
2485
+ &nbsp;
2486
+ // ========== 阶段2: 过滤 issues(作用域感知) ==========
2487
+ // 对每个 issue,只检查其文件匹配的 spec 中声明的 overrides
2488
+ const beforeCount = issues.length;
2489
+ const skipped: Array&lt;{ issue: T; override: string; source: string }&gt; = [];
2490
+ const filtered = issues.filter((issue) =&gt; {
2491
+ const issueFile = "file" in issue ? (issue as { file: string }).file : "";
2492
+ &nbsp;
2493
+ // 查找第一个匹配的 override(需同时满足:ruleId 匹配 AND 文件在 includes 作用域内)
2494
+ const matched = scopedOverrides.find((scoped) =&gt; {
2495
+ // 检查 ruleId 是否匹配 override 模式
2496
+ if (!this.matchRuleId(issue.ruleId, scoped.override)) {
2497
+ return false;
2498
+ }
2499
+ // 检查 issue 文件是否在该 override 的作用域内
2500
+ // 如果 includes 为空,表示全局作用域(匹配所有文件)
2501
+ if (scoped.includes.length === 0) {
2502
+ return true;
2503
+ }
2504
+ // 使用 micromatch 检查文件是否匹配 includes 模式
2505
+ return issueFile &amp;&amp; micromatch.isMatch(issueFile, scoped.includes, { matchBase: true });
2506
+ });
2507
+ &nbsp;
2508
+ if (matched) {
2509
+ skipped.push({ issue, override: matched.override, source: matched.source });
2510
+ return false;
2511
+ }
2512
+ return true;
2513
+ });
2514
+ &nbsp;
2515
+ // ========== 阶段3: 输出过滤结果日志 ==========
2516
+ if (skipped.length &gt; 0 &amp;&amp; shouldLog(verbose, 1)) {
2517
+ console.error(` Override 过滤: ${beforeCount} -&gt; ${filtered.length} 个问题`);
2518
+ for (const { issue, override, source } of skipped) {
2519
+ const file = "file" in issue ? (issue as { file: string }).file : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
2520
+ const line = "line" in issue ? (issue as { line: string }).line : <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
2521
+ console.error(
2522
+ ` ❌ [${issue.ruleId}] ${file}:${line} (override: ${override} from ${source})`,
2523
+ );
2524
+ }
2525
+ }
2526
+ return filtered;
2527
+ }
2528
+ &nbsp;
2529
+ /**
2530
+ * 根据变更文件的 patch 信息过滤 issues
2531
+ * 只保留 issue 的行号在实际变更行范围内的问题
2532
+ */
2533
+ filterIssuesByCommits&lt;T extends { file: string; line: string }&gt;(
2534
+ issues: T[],
2535
+ changedFiles: { filename?: string; patch?: string }[],
2536
+ ): T[] {
2537
+ // 构建文件 -&gt; 变更行集合的映射
2538
+ const fileChangedLines = new Map&lt;string, Set&lt;number&gt;&gt;();
2539
+ &nbsp;
2540
+ for (const file of changedFiles) {
2541
+ if (!file.filename || !file.patch) continue;
2542
+ const lines = this.parseChangedLinesFromPatch(file.patch);
2543
+ fileChangedLines.set(file.filename, lines);
2544
+ }
2545
+ &nbsp;
2546
+ return issues.filter((issue) =&gt; {
2547
+ const changedLines = fileChangedLines.get(issue.file);
2548
+ &nbsp;
2549
+ // 如果没有该文件的 patch 信息,保留 issue
2550
+ if (!changedLines || changedLines.size === 0) {
2551
+ return true;
2552
+ }
2553
+ &nbsp;
2554
+ // 解析 issue 的行号(支持单行或范围如 "123" 或 "123-125")
2555
+ const issueLines = this.parseLineRange(issue.line);
2556
+ &nbsp;
2557
+ // 检查 issue 的任意行是否在变更行范围内
2558
+ const matches = issueLines.some((line) =&gt; changedLines.has(line));
2559
+ if (!matches) {
2560
+ // console.log(` Issue ${issue.file}:${issue.line} 不在变更行范围内,跳过`);
2561
+ }
2562
+ return matches;
2563
+ });
2564
+ }
2565
+ &nbsp;
2566
+ /**
2567
+ * 从 unified diff patch 中解析变更的行号(新文件中的行号)
2568
+ */
2569
+ protected parseChangedLinesFromPatch(patch: string): Set&lt;number&gt; {
2570
+ const changedLines = new Set&lt;number&gt;();
2571
+ const lines = patch.split("\n");
2572
+ &nbsp;
2573
+ let currentLine = 0;
2574
+ &nbsp;
2575
+ for (const line of lines) {
2576
+ // 解析 hunk header: @@ -oldStart,oldCount +newStart,newCount @@
2577
+ const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
2578
+ if (hunkMatch) {
2579
+ currentLine = parseInt(hunkMatch[1], 10);
2580
+ continue;
2581
+ }
2582
+ &nbsp;
2583
+ if (line.startsWith("+") &amp;&amp; !line.startsWith("+++")) {
2584
+ // 新增行
2585
+ changedLines.add(currentLine);
2586
+ currentLine++;
2587
+ } else if (line.startsWith("-") &amp;&amp; !line.startsWith("---")) {
2588
+ // 删除行不增加行号
2589
+ } else {
2590
+ // 上下文行
2591
+ currentLine++;
2592
+ }
2593
+ }
2594
+ &nbsp;
2595
+ return changedLines;
2596
+ }
2597
+ &nbsp;
2598
+ /**
2599
+ * 解析行号字符串,支持单行 "123" 或范围 "123-125"
2600
+ * 返回行号数组,如果解析失败返回空数组
2601
+ */
2602
+ parseLineRange(lineStr: string): number[] {
2603
+ const lines: number[] = [];
2604
+ const rangeMatch = lineStr.match(/^(\d+)-(\d+)$/);
2605
+ &nbsp;
2606
+ if (rangeMatch) {
2607
+ const start = parseInt(rangeMatch[1], 10);
2608
+ const end = parseInt(rangeMatch[2], 10);
2609
+ for (let i = start; i &lt;= end; i++) {
2610
+ lines.push(i);
2611
+ }
2612
+ } else {
2613
+ const line = parseInt(lineStr, 10);
2614
+ if (!isNaN(line)) {
2615
+ lines.push(line);
2616
+ }
2617
+ }
2618
+ &nbsp;
2619
+ return lines;
2620
+ }
2621
+ &nbsp;
2622
+ /**
2623
+ * 构建 specs 的 prompt 部分
2624
+ */
2625
+ buildSpecsSection(specs: ReviewSpec[]): string {
2626
+ return specs
2627
+ .map((spec) =&gt; {
2628
+ const firstRule = spec.rules[0];
2629
+ const rulesText = spec.rules
2630
+ .slice(1)
2631
+ .map((rule) =&gt; {
2632
+ let text = `#### [${rule.id}] ${rule.title}\n`;
2633
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (rule.description) {
2634
+ text += `${rule.description}\n`;
2635
+ }
2636
+ if (rule.examples.length &gt; 0) {
2637
+ for (const example of rule.examples) {
2638
+ text += `##### ${example.type === "good" ? "推荐做法 (Good)" : <span class="branch-1 cbranch-no" title="branch not covered" >"不推荐做法 (Bad)"}\</span>n`;
2639
+ text += `\`\`\`${example.lang}\n${example.code}\n\`\`\`\n`;
2640
+ }
2641
+ }
2642
+ return text;
2643
+ })
2644
+ .join("\n");
2645
+ &nbsp;
2646
+ return `### ${firstRule.title}\n- 规范文件: ${spec.filename}\n- 适用扩展名: ${spec.extensions.join(", ")}\n\n${rulesText}`;
2647
+ })
2648
+ .join("\n\n-------------------\n\n");
2649
+ }
2650
+ &nbsp;
2651
+ /**
2652
+ * 根据 ruleId 查找规则定义
2653
+ * 支持精确匹配和前缀匹配
2654
+ */
2655
+ findRuleById(ruleId: string, specs: ReviewSpec[]): { rule: ReviewRule; spec: ReviewSpec } | null {
2656
+ for (const spec of specs) {
2657
+ for (const rule of spec.rules) {
2658
+ if (this.matchRuleId(ruleId, rule.id)) {
2659
+ return { rule, spec };
2660
+ }
2661
+ }
2662
+ }
2663
+ return null;
2664
+ }
2665
+ &nbsp;
2666
+ /**
2667
+ * 过滤 issues,只保留 ruleId 存在于 specs 中的问题
2668
+ */
2669
+ filterIssuesByRuleExistence&lt;T extends { ruleId: string }&gt;(issues: T[], specs: ReviewSpec[]): T[] {
2670
+ return issues.filter((issue) =&gt; {
2671
+ const ruleInfo = this.findRuleById(issue.ruleId, specs);
2672
+ if (!ruleInfo) {
2673
+ // console.log(` Issue [${issue.ruleId}] 规则不存在,跳过`);
2674
+ return false;
2675
+ }
2676
+ return true;
2677
+ });
2678
+ }
2679
+ &nbsp;
2680
+ /**
2681
+ * 去重规范文件中的重复 ruleId
2682
+ * 后加载的规则覆盖先加载的(符合配置优先级:命令行 &gt; 配置文件 &gt; 默认路径)
2683
+ * @returns 去重后的 specs 数组
2684
+ */
2685
+ deduplicateSpecs(specs: ReviewSpec[]): ReviewSpec[] {
2686
+ // 记录 ruleId -&gt; { specIndex, ruleIndex } 的映射,用于检测重复
2687
+ const ruleIdMap = new Map&lt;string, { specIndex: number; ruleIndex: number }&gt;();
2688
+ // 记录需要从每个 spec 中移除的 rule 索引
2689
+ const rulesToRemove = new Map&lt;number, Set&lt;number&gt;&gt;();
2690
+ &nbsp;
2691
+ for (let specIndex = 0; specIndex &lt; specs.length; specIndex++) {
2692
+ const spec = specs[specIndex];
2693
+ for (let ruleIndex = 0; ruleIndex &lt; spec.rules.length; ruleIndex++) {
2694
+ const rule = spec.rules[ruleIndex];
2695
+ const existing = ruleIdMap.get(rule.id);
2696
+ &nbsp;
2697
+ if (existing) {
2698
+ // 标记先前的规则为待移除(后加载的覆盖先加载的)
2699
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!rulesToRemove.has(existing.specIndex)) {
2700
+ rulesToRemove.set(existing.specIndex, new Set());
2701
+ }
2702
+ rulesToRemove.get(existing.specIndex)!.add(existing.ruleIndex);
2703
+ }
2704
+ &nbsp;
2705
+ // 更新映射为当前规则
2706
+ ruleIdMap.set(rule.id, { specIndex, ruleIndex });
2707
+ }
2708
+ }
2709
+ &nbsp;
2710
+ // 如果没有重复,直接返回原数组
2711
+ if (rulesToRemove.size === 0) {
2712
+ return specs;
2713
+ }
2714
+ &nbsp;
2715
+ // 构建去重后的 specs
2716
+ const result: ReviewSpec[] = [];
2717
+ for (let specIndex = 0; specIndex &lt; specs.length; specIndex++) {
2718
+ const spec = specs[specIndex];
2719
+ const removeSet = rulesToRemove.get(specIndex);
2720
+ &nbsp;
2721
+ if (!removeSet || removeSet.size === 0) {
2722
+ result.push(spec);
2723
+ } else {
2724
+ const filteredRules = spec.rules.filter((_, ruleIndex) =&gt; !removeSet.has(ruleIndex));
2725
+ if (filteredRules.length &gt; 0) {
2726
+ result.push({ ...spec, rules: filteredRules });
2727
+ }
2728
+ }
2729
+ }
2730
+ &nbsp;
2731
+ return result;
2732
+ }
2733
+ &nbsp;
2734
+ /**
2735
+ * 格式化 issues,用规则定义的 severity 覆盖 AI 返回的值
2736
+ */
2737
+ formatIssues&lt;T extends { ruleId: string; severity?: Severity }&gt;(
2738
+ issues: T[],
2739
+ { specs, changedFiles }: { specs: ReviewSpec[]; changedFiles: ChangedFile[] },
2740
+ ): T[] {
2741
+ // 构建 ruleId -&gt; severity 的映射
2742
+ const ruleSeverityMap = new Map&lt;string, Severity&gt;();
2743
+ &nbsp;
2744
+ for (const spec of specs) {
2745
+ for (const rule of spec.rules) {
2746
+ // 规则级别的 severity 优先,否则使用文件级别的 severity
2747
+ const severity = rule.severity ?? spec.severity;
2748
+ ruleSeverityMap.set(rule.id, severity);
2749
+ }
2750
+ }
2751
+ &nbsp;
2752
+ return issues.map((issue) =&gt; {
2753
+ const ruleSeverity = this.findByRuleId(issue.ruleId, ruleSeverityMap);
2754
+ &nbsp;
2755
+ if (ruleSeverity &amp;&amp; ruleSeverity !== issue.severity) {
2756
+ return { ...issue, severity: ruleSeverity };
2757
+ }
2758
+ &nbsp;
2759
+ return issue;
2760
+ });
2761
+ }
2762
+ }
2763
+ &nbsp;</pre></td></tr></table></pre>
2764
+
2765
+ <div class='push'></div><!-- for sticky footer -->
2766
+ </div><!-- /wrapper -->
2767
+ <div class='footer quiet pad2 space-top1 center small'>
2768
+ Code coverage generated by
2769
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
2770
+ at 2026-03-03T08:37:01.464Z
2771
+ </div>
2772
+ <script src="../../prettify.js"></script>
2773
+ <script>
2774
+ window.onload = function () {
2775
+ prettyPrint();
2776
+ };
2777
+ </script>
2778
+ <script src="../../sorter.js"></script>
2779
+ <script src="../../block-navigation.js"></script>
2780
+ </body>
2781
+ </html>
2782
+