| OLD | NEW |
| 1 <!doctype html> | 1 <!DOCTYPE html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test Exceptions from setValueCurveAtTime</title> | 4 <title> |
| 5 Test Exceptions from setValueCurveAtTime |
| 6 </title> |
| 5 <script src="../../resources/testharness.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> | 9 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audit.js"></script> | 10 <script src="../resources/audit.js"></script> |
| 9 </head> | 11 </head> |
| 10 | |
| 11 <body> | 12 <body> |
| 12 <script> | 13 <script id="layout-test-code"> |
| 13 | |
| 14 let sampleRate = 48000; | 14 let sampleRate = 48000; |
| 15 // Some short duration because we don't need to run the test for very long
. | 15 // Some short duration because we don't need to run the test for very |
| 16 // long. |
| 16 let testDurationSec = 0.125; | 17 let testDurationSec = 0.125; |
| 17 let testDurationFrames = testDurationSec * sampleRate; | 18 let testDurationFrames = testDurationSec * sampleRate; |
| 18 | 19 |
| 19 let audit = Audit.createTaskRunner(); | 20 let audit = Audit.createTaskRunner(); |
| 20 | 21 |
| 21 audit.define("setValueCurve", (task, should) => { | 22 audit.define('setValueCurve', (task, should) => { |
| 22 let success = true; | 23 let success = true; |
| 23 let context = new OfflineAudioContext(1, testDurationFrames, sampleRate)
; | 24 let context = |
| 25 new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 24 let g = context.createGain(); | 26 let g = context.createGain(); |
| 25 let curve = new Float32Array(2); | 27 let curve = new Float32Array(2); |
| 26 | 28 |
| 27 // Start time and duration for setValueCurveAtTime | 29 // Start time and duration for setValueCurveAtTime |
| 28 let curveStartTime = 0.1 * testDurationSec; | 30 let curveStartTime = 0.1 * testDurationSec; |
| 29 let duration = 0.1 * testDurationSec; | 31 let duration = 0.1 * testDurationSec; |
| 30 | 32 |
| 31 // Some time that is known to during the setValueCurveTime interval. | 33 // Some time that is known to during the setValueCurveTime interval. |
| 32 let automationTime = curveStartTime + duration / 2; | 34 let automationTime = curveStartTime + duration / 2; |
| 33 | 35 |
| 34 should(() => { | 36 should( |
| 37 () => { |
| 35 g.gain.setValueCurveAtTime(curve, curveStartTime, duration); | 38 g.gain.setValueCurveAtTime(curve, curveStartTime, duration); |
| 36 }, | 39 }, |
| 37 "setValueCurveAtTime(curve, " + curveStartTime + ", " + duration + | 40 'setValueCurveAtTime(curve, ' + curveStartTime + ', ' + duration + |
| 38 ")") | 41 ')') |
| 39 .notThrow(); | 42 .notThrow(); |
| 40 | 43 |
| 41 should(function() { | 44 should( |
| 42 g.gain.setValueAtTime(1, automationTime); | 45 function() { |
| 43 }, "setValueAtTime(1, " + automationTime + ")") | 46 g.gain.setValueAtTime(1, automationTime); |
| 44 .throw("NotSupportedError"); | 47 }, |
| 48 'setValueAtTime(1, ' + automationTime + ')') |
| 49 .throw('NotSupportedError'); |
| 45 | 50 |
| 46 should(function() { | 51 should( |
| 47 g.gain.linearRampToValueAtTime(1, automationTime); | 52 function() { |
| 48 }, "linearRampToValueAtTime(1, " + automationTime + ")") | 53 g.gain.linearRampToValueAtTime(1, automationTime); |
| 49 .throw("NotSupportedError"); | 54 }, |
| 55 'linearRampToValueAtTime(1, ' + automationTime + ')') |
| 56 .throw('NotSupportedError'); |
| 50 | 57 |
| 51 should(function() { | 58 should( |
| 52 g.gain.exponentialRampToValueAtTime(1, automationTime); | 59 function() { |
| 53 }, "exponentialRampToValueAtTime(1, " + automationTime + ")") | 60 g.gain.exponentialRampToValueAtTime(1, automationTime); |
| 54 .throw("NotSupportedError"); | 61 }, |
| 62 'exponentialRampToValueAtTime(1, ' + automationTime + ')') |
| 63 .throw('NotSupportedError'); |
| 55 | 64 |
| 56 should(function() { | 65 should( |
| 57 g.gain.setTargetAtTime(1, automationTime, 1); | 66 function() { |
| 58 }, "setTargetAtTime(1, " + automationTime + ", 1)") | 67 g.gain.setTargetAtTime(1, automationTime, 1); |
| 59 .throw("NotSupportedError"); | 68 }, |
| 69 'setTargetAtTime(1, ' + automationTime + ', 1)') |
| 70 .throw('NotSupportedError'); |
| 60 | 71 |
| 61 should(function() { | 72 should( |
| 62 g.gain.setValueAtTime(1, curveStartTime + 1.1 * duration); | 73 function() { |
| 63 }, "setValueAtTime(1, " + (curveStartTime + 1.1 * duration) + ")") | 74 g.gain.setValueAtTime(1, curveStartTime + 1.1 * duration); |
| 64 .notThrow(); | 75 }, |
| 76 'setValueAtTime(1, ' + (curveStartTime + 1.1 * duration) + ')') |
| 77 .notThrow(); |
| 65 | 78 |
| 66 task.done(); | 79 task.done(); |
| 67 }); | 80 }); |
| 68 | 81 |
| 69 audit.define("automations", (task, should) => { | 82 audit.define('automations', (task, should) => { |
| 70 let context = new OfflineAudioContext(1, testDurationFrames, sampleRate)
; | 83 let context = |
| 84 new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 71 let g = context.createGain(); | 85 let g = context.createGain(); |
| 72 | 86 |
| 73 let curve = new Float32Array(2); | 87 let curve = new Float32Array(2); |
| 74 // Start time and duration for setValueCurveAtTime | 88 // Start time and duration for setValueCurveAtTime |
| 75 let startTime = 0; | 89 let startTime = 0; |
| 76 let timeInterval = testDurationSec / 10; | 90 let timeInterval = testDurationSec / 10; |
| 77 let time; | 91 let time; |
| 78 | 92 |
| 79 startTime += timeInterval; | 93 startTime += timeInterval; |
| 80 should(() => { | 94 should(() => { |
| 81 g.gain.linearRampToValueAtTime(1, startTime); | 95 g.gain.linearRampToValueAtTime(1, startTime); |
| 82 }, "linearRampToValueAtTime(1, " + startTime + ")").notThrow(); | 96 }, 'linearRampToValueAtTime(1, ' + startTime + ')').notThrow(); |
| 83 | 97 |
| 84 startTime += timeInterval; | 98 startTime += timeInterval; |
| 85 should(() => { | 99 should(() => { |
| 86 g.gain.exponentialRampToValueAtTime(1, startTime); | 100 g.gain.exponentialRampToValueAtTime(1, startTime); |
| 87 }, "exponentialRampToValueAtTime(1, " + startTime + ")").notThrow(); | 101 }, 'exponentialRampToValueAtTime(1, ' + startTime + ')').notThrow(); |
| 88 | 102 |
| 89 startTime += timeInterval; | 103 startTime += timeInterval; |
| 90 should(() => { | 104 should(() => { |
| 91 g.gain.setTargetAtTime(1, startTime, 0.1); | 105 g.gain.setTargetAtTime(1, startTime, 0.1); |
| 92 }, "setTargetAtTime(1, " + startTime + ", 0.1)").notThrow(); | 106 }, 'setTargetAtTime(1, ' + startTime + ', 0.1)').notThrow(); |
| 93 | 107 |
| 94 startTime += timeInterval; | 108 startTime += timeInterval; |
| 95 should(() => { | 109 should(() => { |
| 96 g.gain.setValueCurveAtTime(curve, startTime, 0.1); | 110 g.gain.setValueCurveAtTime(curve, startTime, 0.1); |
| 97 }, "setValueCurveAtTime(curve, " + startTime + ", 0.1)").notThrow(); | 111 }, 'setValueCurveAtTime(curve, ' + startTime + ', 0.1)').notThrow(); |
| 98 | 112 |
| 99 // Now try to setValueCurve that overlaps each of the above automations | 113 // Now try to setValueCurve that overlaps each of the above automations |
| 100 startTime = timeInterval / 2; | 114 startTime = timeInterval / 2; |
| 101 | 115 |
| 102 for (let k = 0; k < 4; ++k) { | 116 for (let k = 0; k < 4; ++k) { |
| 103 time = startTime + timeInterval * k; | 117 time = startTime + timeInterval * k; |
| 104 should(() => { | 118 should( |
| 119 () => { |
| 105 g.gain.setValueCurveAtTime(curve, time, 0.01); | 120 g.gain.setValueCurveAtTime(curve, time, 0.01); |
| 106 }, | 121 }, |
| 107 "setValueCurveAtTime(curve, " + time + ", 0.01)") | 122 'setValueCurveAtTime(curve, ' + time + ', 0.01)') |
| 108 .throw("NotSupportedError"); | 123 .throw('NotSupportedError'); |
| 109 } | 124 } |
| 110 | 125 |
| 111 // Elements of setValueCurve should be finite. | 126 // Elements of setValueCurve should be finite. |
| 112 should(() => { | 127 should( |
| 113 g.gain.setValueCurveAtTime(Float32Array.from([NaN, NaN]), time, | 128 () => { |
| 114 0.01); | 129 g.gain.setValueCurveAtTime( |
| 130 Float32Array.from([NaN, NaN]), time, 0.01); |
| 115 }, | 131 }, |
| 116 "setValueCurveAtTime([NaN, NaN], " + time + ", 0.01)") | 132 'setValueCurveAtTime([NaN, NaN], ' + time + ', 0.01)') |
| 117 .throw("TypeError"); | 133 .throw('TypeError'); |
| 118 | 134 |
| 119 should(() => { | 135 should( |
| 120 g.gain.setValueCurveAtTime(Float32Array.from([1, Infinity]), time, | 136 () => { |
| 121 0.01); | 137 g.gain.setValueCurveAtTime( |
| 138 Float32Array.from([1, Infinity]), time, 0.01); |
| 122 }, | 139 }, |
| 123 "setValueCurveAtTime([1, Infinity], " + time + ", 0.01)") | 140 'setValueCurveAtTime([1, Infinity], ' + time + ', 0.01)') |
| 124 .throw("TypeError"); | 141 .throw('TypeError'); |
| 125 | 142 |
| 126 let d = context.createDelay(); | 143 let d = context.createDelay(); |
| 127 // Check that we get warnings for out-of-range values and also throw for | 144 // Check that we get warnings for out-of-range values and also throw for |
| 128 // non-finite values. | 145 // non-finite values. |
| 129 should(() => { | 146 should( |
| 130 d.delayTime.setValueCurveAtTime(Float32Array.from([1, 5]), time, | 147 () => { |
| 131 0.01); | 148 d.delayTime.setValueCurveAtTime( |
| 149 Float32Array.from([1, 5]), time, 0.01); |
| 132 }, | 150 }, |
| 133 "delayTime.setValueCurveAtTime([1, 5], " + time + ", 0.01)") | 151 'delayTime.setValueCurveAtTime([1, 5], ' + time + ', 0.01)') |
| 134 .notThrow(); | 152 .notThrow(); |
| 135 | 153 |
| 136 should(() => { | 154 should( |
| 137 d.delayTime.setValueCurveAtTime(Float32Array.from([1, 5, Infinity]
), | 155 () => { |
| 138 time, 0.01); | 156 d.delayTime.setValueCurveAtTime( |
| 157 Float32Array.from([1, 5, Infinity]), time, 0.01); |
| 139 }, | 158 }, |
| 140 "delayTime.setValueCurveAtTime([1, 5, Infinity], " + time + | 159 'delayTime.setValueCurveAtTime([1, 5, Infinity], ' + time + |
| 141 ", 0.01)") | 160 ', 0.01)') |
| 142 .throw("TypeError"); | 161 .throw('TypeError'); |
| 143 | 162 |
| 144 // One last test that prints out lots of digits for the time. | 163 // One last test that prints out lots of digits for the time. |
| 145 time = Math.PI / 100; | 164 time = Math.PI / 100; |
| 146 should(() => { | 165 should( |
| 147 g.gain.setValueCurveAtTime(curve, time, 0.01); | 166 () => { |
| 148 }, "setValueCurveAtTime(curve, " + time + ", 0.01)").throw("NotSupported
Error"); | 167 g.gain.setValueCurveAtTime(curve, time, 0.01); |
| 168 }, |
| 169 'setValueCurveAtTime(curve, ' + time + ', 0.01)') |
| 170 .throw('NotSupportedError'); |
| 149 | 171 |
| 150 task.done(); | 172 task.done(); |
| 151 }); | 173 }); |
| 152 | 174 |
| 153 audit.define("catch-exception", (task, should) => { | 175 audit.define('catch-exception', (task, should) => { |
| 154 // Verify that the curve isn't inserted into the time line even if we ca
tch the exception. | 176 // Verify that the curve isn't inserted into the time line even if we |
| 177 // catch the exception. |
| 155 let success = true; | 178 let success = true; |
| 156 let context = new OfflineAudioContext(1, testDurationFrames, sampleRate)
; | 179 let context = |
| 180 new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 157 let gain = context.createGain(); | 181 let gain = context.createGain(); |
| 158 let source = context.createBufferSource(); | 182 let source = context.createBufferSource(); |
| 159 let buffer = context.createBuffer(1, 1, context.sampleRate); | 183 let buffer = context.createBuffer(1, 1, context.sampleRate); |
| 160 buffer.getChannelData(0)[0] = 1; | 184 buffer.getChannelData(0)[0] = 1; |
| 161 source.buffer = buffer; | 185 source.buffer = buffer; |
| 162 source.loop = true; | 186 source.loop = true; |
| 163 | 187 |
| 164 source.connect(gain); | 188 source.connect(gain); |
| 165 gain.connect(context.destination); | 189 gain.connect(context.destination); |
| 166 | 190 |
| 167 gain.gain.setValueAtTime(1, 0); | 191 gain.gain.setValueAtTime(1, 0); |
| 168 try { | 192 try { |
| 169 // The value curve has an invalid element. This automation shouldn't b
e inserted into the | 193 // The value curve has an invalid element. This automation shouldn't |
| 170 // timeline at all. | 194 // be inserted into the timeline at all. |
| 171 gain.gain.setValueCurveAtTime(Float32Array.from([0, NaN]), 128 / conte
xt.sampleRate, .5); | 195 gain.gain.setValueCurveAtTime( |
| 196 Float32Array.from([0, NaN]), 128 / context.sampleRate, .5); |
| 172 } catch (e) { | 197 } catch (e) { |
| 173 }; | 198 }; |
| 174 source.start(); | 199 source.start(); |
| 175 | 200 |
| 176 context.startRendering().then(function (resultBuffer) { | 201 context.startRendering() |
| 177 // Since the setValueCurve wasn't inserted, the output should be exact
ly 1 for the entire | 202 .then(function(resultBuffer) { |
| 178 // duration. | 203 // Since the setValueCurve wasn't inserted, the output should be |
| 179 should(resultBuffer.getChannelData(0), | 204 // exactly 1 for the entire duration. |
| 180 "Handled setValueCurve exception so output") | 205 should( |
| 181 .beConstantValueOf(1); | 206 resultBuffer.getChannelData(0), |
| 207 'Handled setValueCurve exception so output') |
| 208 .beConstantValueOf(1); |
| 182 | 209 |
| 183 }).then(() => task.done()); | 210 }) |
| 211 .then(() => task.done()); |
| 184 }); | 212 }); |
| 185 | 213 |
| 186 audit.define("start-end", (task, should) => { | 214 audit.define('start-end', (task, should) => { |
| 187 let context = new OfflineAudioContext(1, testDurationFrames, sampleRate)
; | 215 let context = |
| 216 new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 188 let g = context.createGain(); | 217 let g = context.createGain(); |
| 189 let curve = new Float32Array(2); | 218 let curve = new Float32Array(2); |
| 190 | 219 |
| 191 // Verify that a setValueCurve can start at the end of an automation. | 220 // Verify that a setValueCurve can start at the end of an automation. |
| 192 let time = 0; | 221 let time = 0; |
| 193 let timeInterval = testDurationSec / 50; | 222 let timeInterval = testDurationSec / 50; |
| 194 should(() => { | 223 should(() => { |
| 195 g.gain.setValueAtTime(1, time); | 224 g.gain.setValueAtTime(1, time); |
| 196 }, "setValueAtTime(1, " + time + ")").notThrow(); | 225 }, 'setValueAtTime(1, ' + time + ')').notThrow(); |
| 197 | 226 |
| 198 time += timeInterval; | 227 time += timeInterval; |
| 199 should(() => { | 228 should(() => { |
| 200 g.gain.linearRampToValueAtTime(0, time); | 229 g.gain.linearRampToValueAtTime(0, time); |
| 201 }, "linearRampToValueAtTime(0, " + time + ")").notThrow(); | 230 }, 'linearRampToValueAtTime(0, ' + time + ')').notThrow(); |
| 202 | 231 |
| 203 // setValueCurve starts at the end of the linear ramp. This should be fi
ne. | 232 // setValueCurve starts at the end of the linear ramp. This should be |
| 204 should(() => { | 233 // fine. |
| 234 should( |
| 235 () => { |
| 205 g.gain.setValueCurveAtTime(curve, time, timeInterval); | 236 g.gain.setValueCurveAtTime(curve, time, timeInterval); |
| 206 }, | 237 }, |
| 207 "setValueCurveAtTime(..., " + time + ", " + timeInterval + ")") | 238 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') |
| 208 .notThrow(); | 239 .notThrow(); |
| 209 | 240 |
| 210 // exponentialRamp ending one interval past the setValueCurve should be
fine. | 241 // exponentialRamp ending one interval past the setValueCurve should be |
| 211 time += 2*timeInterval; | 242 // fine. |
| 243 time += 2 * timeInterval; |
| 212 should(() => { | 244 should(() => { |
| 213 g.gain.exponentialRampToValueAtTime(1, time); | 245 g.gain.exponentialRampToValueAtTime(1, time); |
| 214 }, "exponentialRampToValueAtTime(1, " + time + ")").notThrow(); | 246 }, 'exponentialRampToValueAtTime(1, ' + time + ')').notThrow(); |
| 215 | 247 |
| 216 // setValueCurve starts at the end of the exponential ramp. This should
be fine. | 248 // setValueCurve starts at the end of the exponential ramp. This should |
| 217 should(() => { | 249 // be fine. |
| 250 should( |
| 251 () => { |
| 218 g.gain.setValueCurveAtTime(curve, time, timeInterval); | 252 g.gain.setValueCurveAtTime(curve, time, timeInterval); |
| 219 }, | 253 }, |
| 220 "setValueCurveAtTime(..., " + time + ", " + timeInterval + ")") | 254 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') |
| 221 .notThrow(); | 255 .notThrow(); |
| 222 | 256 |
| 223 // setValueCurve at the end of the setValueCurve should be fine. | 257 // setValueCurve at the end of the setValueCurve should be fine. |
| 224 time += timeInterval; | 258 time += timeInterval; |
| 225 should(() => { | 259 should( |
| 260 () => { |
| 226 g.gain.setValueCurveAtTime(curve, time, timeInterval); | 261 g.gain.setValueCurveAtTime(curve, time, timeInterval); |
| 227 }, | 262 }, |
| 228 "setValueCurveAtTime(..., " + time + ", " + timeInterval + ")") | 263 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') |
| 229 .notThrow(); | 264 .notThrow(); |
| 230 | 265 |
| 231 // setValueAtTime at the end of setValueCurve should be fine. | 266 // setValueAtTime at the end of setValueCurve should be fine. |
| 232 time += timeInterval; | 267 time += timeInterval; |
| 233 should(() => { | 268 should(() => { |
| 234 g.gain.setValueAtTime(0, time); | 269 g.gain.setValueAtTime(0, time); |
| 235 }, "setValueAtTime(0, " + time + ")").notThrow(); | 270 }, 'setValueAtTime(0, ' + time + ')').notThrow(); |
| 236 | 271 |
| 237 // setValueCurve at the end of setValueAtTime should be fine. | 272 // setValueCurve at the end of setValueAtTime should be fine. |
| 238 should(() => { | 273 should( |
| 274 () => { |
| 239 g.gain.setValueCurveAtTime(curve, time, timeInterval); | 275 g.gain.setValueCurveAtTime(curve, time, timeInterval); |
| 240 }, | 276 }, |
| 241 "setValueCurveAtTime(..., " + time + ", " + timeInterval + ")") | 277 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') |
| 242 .notThrow(); | 278 .notThrow(); |
| 243 | 279 |
| 244 // setTarget starting at the end of setValueCurve should be fine. | 280 // setTarget starting at the end of setValueCurve should be fine. |
| 245 time += timeInterval; | 281 time += timeInterval; |
| 246 should(() => { | 282 should(() => { |
| 247 g.gain.setTargetAtTime(1, time, 1); | 283 g.gain.setTargetAtTime(1, time, 1); |
| 248 }, "setTargetAtTime(1, " + time + ", 1)").notThrow(); | 284 }, 'setTargetAtTime(1, ' + time + ', 1)').notThrow(); |
| 249 | 285 |
| 250 task.done(); | 286 task.done(); |
| 251 }); | 287 }); |
| 252 | 288 |
| 253 audit.define("curve lengths", (task, should) => { | 289 audit.define('curve lengths', (task, should) => { |
| 254 let context = new OfflineAudioContext(1, testDurationFrames, sampleRate)
; | 290 let context = |
| 291 new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 255 let g = context.createGain(); | 292 let g = context.createGain(); |
| 256 let time = 0; | 293 let time = 0; |
| 257 | 294 |
| 258 // Check for invalid curve lengths | 295 // Check for invalid curve lengths |
| 259 should(() => { | 296 should( |
| 297 () => { |
| 260 g.gain.setValueCurveAtTime(Float32Array.from([]), time, 0.01); | 298 g.gain.setValueCurveAtTime(Float32Array.from([]), time, 0.01); |
| 261 }, | 299 }, |
| 262 "setValueCurveAtTime([], " + time + ", 0.01)") | 300 'setValueCurveAtTime([], ' + time + ', 0.01)') |
| 263 .throw("InvalidStateError"); | 301 .throw('InvalidStateError'); |
| 302 |
| 303 should( |
| 304 () => { |
| 305 g.gain.setValueCurveAtTime(Float32Array.from([1]), time, 0.01); |
| 306 }, |
| 307 'setValueCurveAtTime([1], ' + time + ', 0.01)') |
| 308 .throw('InvalidStateError'); |
| 264 | 309 |
| 265 should(() => { | 310 should(() => { |
| 266 g.gain.setValueCurveAtTime(Float32Array.from([1]), time, 0.01); | 311 g.gain.setValueCurveAtTime(Float32Array.from([1, 2]), time, 0.01); |
| 267 }, | 312 }, 'setValueCurveAtTime([1,2], ' + time + ', 0.01)').notThrow(); |
| 268 "setValueCurveAtTime([1], " + time + ", 0.01)") | |
| 269 .throw("InvalidStateError"); | |
| 270 | |
| 271 should(() => { | |
| 272 g.gain.setValueCurveAtTime(Float32Array.from([1,2]), time, 0.01); | |
| 273 }, "setValueCurveAtTime([1,2], " + time + ", 0.01)").notThrow(); | |
| 274 | 313 |
| 275 task.done(); | 314 task.done(); |
| 276 }); | 315 }); |
| 277 | 316 |
| 278 audit.run(); | 317 audit.run(); |
| 279 </script> | 318 </script> |
| 280 </body> | 319 </body> |
| 281 </html> | 320 </html> |
| 282 | |
| 283 | |
| 284 | |
| 285 | |
| 286 | |
| 287 | |
| 288 | |
| 289 | |
| 290 | |
| OLD | NEW |