| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> |
| 3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org> |
| 4 * Copyright (C) 2008 Apple Inc. All rights reserved. | 4 * Copyright (C) 2008 Apple Inc. All rights reserved. |
| 5 * Copyright (C) Research In Motion Limited 2011. All rights reserved. | 5 * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| 6 * | 6 * |
| 7 * This library is free software; you can redistribute it and/or | 7 * This library is free software; you can redistribute it and/or |
| 8 * modify it under the terms of the GNU Library General Public | 8 * modify it under the terms of the GNU Library General Public |
| 9 * License as published by the Free Software Foundation; either | 9 * License as published by the Free Software Foundation; either |
| 10 * version 2 of the License, or (at your option) any later version. | 10 * version 2 of the License, or (at your option) any later version. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 */ | 21 */ |
| 22 | 22 |
| 23 #include "config.h" | 23 #include "config.h" |
| 24 | 24 |
| 25 #include "core/svg/SVGAnimateElement.h" | 25 #include "core/svg/SVGAnimateElement.h" |
| 26 | 26 |
| 27 #include "CSSPropertyNames.h" | 27 #include "CSSPropertyNames.h" |
| 28 #include "core/css/parser/BisonCSSParser.h" | 28 #include "core/css/parser/BisonCSSParser.h" |
| 29 #include "core/css/StylePropertySet.h" | 29 #include "core/css/StylePropertySet.h" |
| 30 #include "core/dom/QualifiedName.h" | 30 #include "core/dom/QualifiedName.h" |
| 31 #include "core/svg/SVGAnimatedType.h" | |
| 32 #include "core/svg/SVGAnimatedTypeAnimator.h" | 31 #include "core/svg/SVGAnimatedTypeAnimator.h" |
| 33 #include "core/svg/SVGAnimatorFactory.h" | 32 #include "core/svg/SVGAnimatorFactory.h" |
| 34 #include "core/svg/SVGDocumentExtensions.h" | 33 #include "core/svg/SVGDocumentExtensions.h" |
| 35 | 34 |
| 36 namespace WebCore { | 35 namespace WebCore { |
| 37 | 36 |
| 38 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& doc
ument) | 37 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& doc
ument) |
| 39 : SVGAnimationElement(tagName, document) | 38 : SVGAnimationElement(tagName, document) |
| 40 , m_animatedPropertyType(AnimatedString) | 39 , m_animatedPropertyType(AnimatedString) |
| 41 { | 40 { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 if (!targetElement || !isSVGAnimateElement(*resultElement)) | 92 if (!targetElement || !isSVGAnimateElement(*resultElement)) |
| 94 return; | 93 return; |
| 95 | 94 |
| 96 ASSERT(m_animatedPropertyType == determineAnimatedPropertyType(targetElement
)); | 95 ASSERT(m_animatedPropertyType == determineAnimatedPropertyType(targetElement
)); |
| 97 | 96 |
| 98 ASSERT(percentage >= 0 && percentage <= 1); | 97 ASSERT(percentage >= 0 && percentage <= 1); |
| 99 ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGName
s::animateTransformTag)); | 98 ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGName
s::animateTransformTag)); |
| 100 ASSERT(m_animatedPropertyType != AnimatedUnknown); | 99 ASSERT(m_animatedPropertyType != AnimatedUnknown); |
| 101 ASSERT(m_animator); | 100 ASSERT(m_animator); |
| 102 ASSERT(m_animator->type() == m_animatedPropertyType); | 101 ASSERT(m_animator->type() == m_animatedPropertyType); |
| 103 ASSERT(m_fromType); | 102 ASSERT(m_fromProperty); |
| 104 ASSERT(m_fromType->type() == m_animatedPropertyType); | 103 ASSERT(m_fromProperty->type() == m_animatedPropertyType); |
| 105 ASSERT(m_toType); | 104 ASSERT(m_toProperty); |
| 106 | 105 |
| 107 SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElemen
t); | 106 SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElemen
t); |
| 108 ASSERT(resultAnimationElement->m_animatedType); | 107 ASSERT(resultAnimationElement->m_animatedProperty); |
| 109 ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyT
ype); | 108 ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyT
ype); |
| 110 | 109 |
| 111 if (hasTagName(SVGNames::setTag)) | 110 if (hasTagName(SVGNames::setTag)) |
| 112 percentage = 1; | 111 percentage = 1; |
| 113 | 112 |
| 114 if (calcMode() == CalcModeDiscrete) | 113 if (calcMode() == CalcModeDiscrete) |
| 115 percentage = percentage < 0.5 ? 0 : 1; | 114 percentage = percentage < 0.5 ? 0 : 1; |
| 116 | 115 |
| 117 // Target element might have changed. | 116 // Target element might have changed. |
| 118 m_animator->setContextElement(targetElement); | 117 m_animator->setContextElement(targetElement); |
| 119 | 118 |
| 120 // Be sure to detach list wrappers before we modfiy their underlying value.
If we'd do | 119 // Be sure to detach list wrappers before we modfiy their underlying value.
If we'd do |
| 121 // if after calculateAnimatedValue() ran the cached pointers in the list pro
pery tear | 120 // if after calculateAnimatedValue() ran the cached pointers in the list pro
pery tear |
| 122 // offs would point nowhere, and we couldn't create copies of those values a
nymore, | 121 // offs would point nowhere, and we couldn't create copies of those values a
nymore, |
| 123 // while detaching. This is covered by assertions, moving this down would fi
re them. | 122 // while detaching. This is covered by assertions, moving this down would fi
re them. |
| 124 if (!m_animatedProperties.isEmpty()) | 123 if (!m_animatedProperties.isEmpty()) |
| 125 m_animator->animValWillChange(m_animatedProperties); | 124 m_animator->animValWillChange(m_animatedProperties); |
| 126 | 125 |
| 127 // Values-animation accumulates using the last values entry corresponding to
the end of duration time. | 126 // Values-animation accumulates using the last values entry corresponding to
the end of duration time. |
| 128 SVGAnimatedType* toAtEndOfDurationType = m_toAtEndOfDurationType ? m_toAtEnd
OfDurationType.get() : m_toType.get(); | 127 NewSVGPropertyBase* toAtEndOfDurationProperty = m_toAtEndOfDurationProperty
? m_toAtEndOfDurationProperty.get() : m_toProperty.get(); |
| 129 m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromType.get()
, m_toType.get(), toAtEndOfDurationType, resultAnimationElement->m_animatedType.
get()); | 128 m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromProperty.g
et(), m_toProperty.get(), toAtEndOfDurationProperty, resultAnimationElement->m_a
nimatedProperty.get()); |
| 130 } | 129 } |
| 131 | 130 |
| 132 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfD
urationString) | 131 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfD
urationString) |
| 133 { | 132 { |
| 134 if (toAtEndOfDurationString.isEmpty()) | 133 if (toAtEndOfDurationString.isEmpty()) |
| 135 return false; | 134 return false; |
| 136 m_toAtEndOfDurationType = ensureAnimator()->constructFromString(toAtEndOfDur
ationString); | 135 m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndO
fDurationString); |
| 137 return true; | 136 return true; |
| 138 } | 137 } |
| 139 | 138 |
| 140 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const
String& toString) | 139 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const
String& toString) |
| 141 { | 140 { |
| 142 SVGElement* targetElement = this->targetElement(); | 141 SVGElement* targetElement = this->targetElement(); |
| 143 if (!targetElement) | 142 if (!targetElement) |
| 144 return false; | 143 return false; |
| 145 | 144 |
| 146 determinePropertyValueTypes(fromString, toString); | 145 determinePropertyValueTypes(fromString, toString); |
| 147 ensureAnimator()->calculateFromAndToValues(m_fromType, m_toType, fromString,
toString); | 146 ensureAnimator()->calculateFromAndToValues(m_fromProperty, m_toProperty, fro
mString, toString); |
| 148 ASSERT(m_animatedPropertyType == m_animator->type()); | 147 ASSERT(m_animatedPropertyType == m_animator->type()); |
| 149 return true; | 148 return true; |
| 150 } | 149 } |
| 151 | 150 |
| 152 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const
String& byString) | 151 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const
String& byString) |
| 153 { | 152 { |
| 154 SVGElement* targetElement = this->targetElement(); | 153 SVGElement* targetElement = this->targetElement(); |
| 155 if (!targetElement) | 154 if (!targetElement) |
| 156 return false; | 155 return false; |
| 157 | 156 |
| 158 if (animationMode() == ByAnimation && !isAdditive()) | 157 if (animationMode() == ByAnimation && !isAdditive()) |
| 159 return false; | 158 return false; |
| 160 | 159 |
| 161 // from-by animation may only be used with attributes that support addition
(e.g. most numeric attributes). | 160 // from-by animation may only be used with attributes that support addition
(e.g. most numeric attributes). |
| 162 if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddit
ion()) | 161 if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddit
ion()) |
| 163 return false; | 162 return false; |
| 164 | 163 |
| 165 ASSERT(!hasTagName(SVGNames::setTag)); | 164 ASSERT(!hasTagName(SVGNames::setTag)); |
| 166 | 165 |
| 167 determinePropertyValueTypes(fromString, byString); | 166 determinePropertyValueTypes(fromString, byString); |
| 168 ensureAnimator()->calculateFromAndByValues(m_fromType, m_toType, fromString,
byString); | 167 ensureAnimator()->calculateFromAndByValues(m_fromProperty, m_toProperty, fro
mString, byString); |
| 169 ASSERT(m_animatedPropertyType == m_animator->type()); | 168 ASSERT(m_animatedPropertyType == m_animator->type()); |
| 170 return true; | 169 return true; |
| 171 } | 170 } |
| 172 | 171 |
| 173 #ifndef NDEBUG | 172 #ifndef NDEBUG |
| 174 static inline bool propertyTypesAreConsistent(AnimatedPropertyType expectedPrope
rtyType, const SVGElementAnimatedPropertyList& animatedTypes) | 173 static inline bool propertyTypesAreConsistent(AnimatedPropertyType expectedPrope
rtyType, const SVGElementAnimatedPropertyList& animatedTypes) |
| 175 { | 174 { |
| 176 SVGElementAnimatedPropertyList::const_iterator end = animatedTypes.end(); | 175 SVGElementAnimatedPropertyList::const_iterator end = animatedTypes.end(); |
| 177 for (SVGElementAnimatedPropertyList::const_iterator it = animatedTypes.begin
(); it != end; ++it) { | 176 for (SVGElementAnimatedPropertyList::const_iterator it = animatedTypes.begin
(); it != end; ++it) { |
| 178 for (size_t i = 0; i < it->properties.size(); ++i) { | 177 for (size_t i = 0; i < it->properties.size(); ++i) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 204 if (shouldApply == ApplyXMLAnimation) { | 203 if (shouldApply == ApplyXMLAnimation) { |
| 205 // SVG DOM animVal animation code-path. | 204 // SVG DOM animVal animation code-path. |
| 206 m_animatedProperties = animator->findAnimatedPropertiesForAttributeName(
targetElement, attributeName); | 205 m_animatedProperties = animator->findAnimatedPropertiesForAttributeName(
targetElement, attributeName); |
| 207 SVGElementAnimatedPropertyList::const_iterator end = m_animatedPropertie
s.end(); | 206 SVGElementAnimatedPropertyList::const_iterator end = m_animatedPropertie
s.end(); |
| 208 for (SVGElementAnimatedPropertyList::const_iterator it = m_animatedPrope
rties.begin(); it != end; ++it) | 207 for (SVGElementAnimatedPropertyList::const_iterator it = m_animatedPrope
rties.begin(); it != end; ++it) |
| 209 document().accessSVGExtensions().addElementReferencingTarget(this, i
t->element); | 208 document().accessSVGExtensions().addElementReferencingTarget(this, i
t->element); |
| 210 | 209 |
| 211 ASSERT(!m_animatedProperties.isEmpty()); | 210 ASSERT(!m_animatedProperties.isEmpty()); |
| 212 | 211 |
| 213 ASSERT(propertyTypesAreConsistent(m_animatedPropertyType, m_animatedProp
erties)); | 212 ASSERT(propertyTypesAreConsistent(m_animatedPropertyType, m_animatedProp
erties)); |
| 214 if (!m_animatedType) | 213 if (!m_animatedProperty) |
| 215 m_animatedType = animator->startAnimValAnimation(m_animatedPropertie
s); | 214 m_animatedProperty = animator->startAnimValAnimation(m_animatedPrope
rties); |
| 216 else { | 215 else { |
| 217 animator->resetAnimValToBaseVal(m_animatedProperties, m_animatedType
.get()); | 216 m_animatedProperty = animator->resetAnimValToBaseVal(m_animatedPrope
rties); |
| 218 animator->animValDidChange(m_animatedProperties); | 217 animator->animValDidChange(m_animatedProperties); |
| 219 } | 218 } |
| 220 return; | 219 return; |
| 221 } | 220 } |
| 222 | 221 |
| 223 // CSS properties animation code-path. | 222 // CSS properties animation code-path. |
| 224 ASSERT(m_animatedProperties.isEmpty()); | 223 ASSERT(m_animatedProperties.isEmpty()); |
| 225 String baseValue; | 224 String baseValue; |
| 226 | 225 |
| 227 if (shouldApply == ApplyCSSAnimation) { | 226 if (shouldApply == ApplyCSSAnimation) { |
| 228 ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement,
attributeName)); | 227 ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement,
attributeName)); |
| 229 computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.local
Name()), baseValue); | 228 computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.local
Name()), baseValue); |
| 230 } | 229 } |
| 231 | 230 |
| 232 if (!m_animatedType || !m_animatedType->setValueAsString(attributeName, base
Value)) | 231 if (!m_animatedProperty) |
| 233 m_animatedType = animator->constructFromString(baseValue); | 232 m_animatedProperty = animator->constructFromString(baseValue); |
| 234 } | 233 } |
| 235 | 234 |
| 236 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSProper
tyID id, const String& value) | 235 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSProper
tyID id, const String& value) |
| 237 { | 236 { |
| 238 ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun); | 237 ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun); |
| 239 | 238 |
| 240 MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyl
eProperties(); | 239 MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyl
eProperties(); |
| 241 if (!propertySet->setProperty(id, value, false, 0)) | 240 if (!propertySet->setProperty(id, value, false, 0)) |
| 242 return; | 241 return; |
| 243 | 242 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForE
lement(); | 310 const HashSet<SVGElementInstance*>& instances = targetElement->instancesForE
lement(); |
| 312 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); | 311 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); |
| 313 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it
!= end; ++it) { | 312 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it
!= end; ++it) { |
| 314 if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement()) | 313 if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement()) |
| 315 notifyTargetAboutAnimValChange(shadowTreeElement, attributeName); | 314 notifyTargetAboutAnimValChange(shadowTreeElement, attributeName); |
| 316 } | 315 } |
| 317 } | 316 } |
| 318 | 317 |
| 319 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement) | 318 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement) |
| 320 { | 319 { |
| 321 if (!m_animatedType) | 320 if (!m_animatedProperty) |
| 322 return; | 321 return; |
| 323 | 322 |
| 324 if (!targetElement) { | 323 if (!targetElement) { |
| 325 m_animatedType.clear(); | 324 m_animatedProperty.clear(); |
| 326 return; | 325 return; |
| 327 } | 326 } |
| 328 | 327 |
| 329 if (m_animatedProperties.isEmpty()) { | 328 if (m_animatedProperties.isEmpty()) { |
| 330 // CSS properties animation code-path. | 329 // CSS properties animation code-path. |
| 331 removeCSSPropertyFromTargetAndInstances(targetElement, attributeName()); | 330 removeCSSPropertyFromTargetAndInstances(targetElement, attributeName()); |
| 332 m_animatedType.clear(); | 331 m_animatedProperty.clear(); |
| 333 return; | 332 return; |
| 334 } | 333 } |
| 335 | 334 |
| 336 // SVG DOM animVal animation code-path. | 335 // SVG DOM animVal animation code-path. |
| 337 if (m_animator) { | 336 if (m_animator) { |
| 338 m_animator->stopAnimValAnimation(m_animatedProperties); | 337 m_animator->stopAnimValAnimation(m_animatedProperties); |
| 339 notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName(
)); | 338 notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName(
)); |
| 340 } | 339 } |
| 341 | 340 |
| 342 m_animatedProperties.clear(); | 341 m_animatedProperties.clear(); |
| 343 m_animatedType.clear(); | 342 m_animatedProperty.clear(); |
| 344 } | 343 } |
| 345 | 344 |
| 346 void SVGAnimateElement::applyResultsToTarget() | 345 void SVGAnimateElement::applyResultsToTarget() |
| 347 { | 346 { |
| 348 ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGName
s::animateTransformTag)); | 347 ASSERT(m_animatedPropertyType != AnimatedTransformList || hasTagName(SVGName
s::animateTransformTag)); |
| 349 ASSERT(m_animatedPropertyType != AnimatedUnknown); | 348 ASSERT(m_animatedPropertyType != AnimatedUnknown); |
| 350 ASSERT(m_animator); | 349 ASSERT(m_animator); |
| 351 | 350 |
| 352 // Early exit if our animated type got destructed by a previous endedActiveI
nterval(). | 351 // Early exit if our animated type got destructed by a previous endedActiveI
nterval(). |
| 353 if (!m_animatedType) | 352 if (!m_animatedProperty) |
| 354 return; | 353 return; |
| 355 | 354 |
| 356 if (m_animatedProperties.isEmpty()) { | 355 if (m_animatedProperties.isEmpty()) { |
| 357 // CSS properties animation code-path. | 356 // CSS properties animation code-path. |
| 358 // Convert the result of the animation to a String and apply it as CSS p
roperty on the target & all instances. | 357 // Convert the result of the animation to a String and apply it as CSS p
roperty on the target & all instances. |
| 359 applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m
_animatedType->valueAsString()); | 358 applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m
_animatedProperty->valueAsString()); |
| 360 return; | 359 return; |
| 361 } | 360 } |
| 362 | 361 |
| 363 // SVG DOM animVal animation code-path. | 362 // SVG DOM animVal animation code-path. |
| 364 // At this point the SVG DOM values are already changed, unlike for CSS. | 363 // At this point the SVG DOM values are already changed, unlike for CSS. |
| 365 // We only have to trigger update notifications here. | 364 // We only have to trigger update notifications here. |
| 366 m_animator->animValDidChange(m_animatedProperties); | 365 m_animator->animValDidChange(m_animatedProperties); |
| 367 notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName())
; | 366 notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName())
; |
| 368 } | 367 } |
| 369 | 368 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 408 } | 407 } |
| 409 | 408 |
| 410 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName) | 409 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName) |
| 411 { | 410 { |
| 412 SVGAnimationElement::setAttributeName(attributeName); | 411 SVGAnimationElement::setAttributeName(attributeName); |
| 413 resetAnimatedPropertyType(); | 412 resetAnimatedPropertyType(); |
| 414 } | 413 } |
| 415 | 414 |
| 416 void SVGAnimateElement::resetAnimatedPropertyType() | 415 void SVGAnimateElement::resetAnimatedPropertyType() |
| 417 { | 416 { |
| 418 ASSERT(!m_animatedType); | 417 ASSERT(!m_animatedProperty); |
| 419 m_fromType.clear(); | 418 m_fromProperty.clear(); |
| 420 m_toType.clear(); | 419 m_toProperty.clear(); |
| 421 m_toAtEndOfDurationType.clear(); | 420 m_toAtEndOfDurationProperty.clear(); |
| 422 m_animator.clear(); | 421 m_animator.clear(); |
| 423 m_animatedPropertyType = targetElement() ? determineAnimatedPropertyType(tar
getElement()) : AnimatedString; | 422 m_animatedPropertyType = targetElement() ? determineAnimatedPropertyType(tar
getElement()) : AnimatedString; |
| 424 } | 423 } |
| 425 | 424 |
| 426 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator() | 425 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator() |
| 427 { | 426 { |
| 428 if (!m_animator) | 427 if (!m_animator) |
| 429 m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animate
dPropertyType); | 428 m_animator = SVGAnimatorFactory::create(this, targetElement(), m_animate
dPropertyType); |
| 430 ASSERT(m_animatedPropertyType == m_animator->type()); | 429 ASSERT(m_animatedPropertyType == m_animator->type()); |
| 431 return m_animator.get(); | 430 return m_animator.get(); |
| 432 } | 431 } |
| 433 | 432 |
| 434 } | 433 } |
| OLD | NEW |