@angular/animations 6.0.3 → 6.0.7
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.
- package/browser/browser.metadata.json +1 -1
- package/browser/src/dsl/animation_transition_factory.d.ts +1 -1
- package/browser/src/render/animation_engine_next.d.ts +2 -1
- package/browser/src/render/shared.d.ts +2 -0
- package/browser/src/render/timeline_animation_engine.d.ts +2 -1
- package/browser/src/render/transition_animation_engine.d.ts +3 -3
- package/browser/testing.d.ts +0 -5
- package/browser/testing.metadata.json +1 -1
- package/browser.d.ts +0 -5
- package/browser.metadata.json +1 -1
- package/bundles/animations-browser-testing.umd.js +28 -10
- package/bundles/animations-browser-testing.umd.js.map +1 -1
- package/bundles/animations-browser-testing.umd.min.js +16 -2
- package/bundles/animations-browser-testing.umd.min.js.map +1 -1
- package/bundles/animations-browser.umd.js +200 -83
- package/bundles/animations-browser.umd.js.map +1 -1
- package/bundles/animations-browser.umd.min.js +55 -6
- package/bundles/animations-browser.umd.min.js.map +1 -1
- package/bundles/animations.umd.js +470 -577
- package/bundles/animations.umd.js.map +1 -1
- package/bundles/animations.umd.min.js +9 -2
- package/bundles/animations.umd.min.js.map +1 -1
- package/esm2015/animations.externs.js +1 -1
- package/esm2015/browser/browser.externs.js +3 -3
- package/esm2015/browser/src/dsl/animation_transition_factory.js +4 -3
- package/esm2015/browser/src/render/animation_driver.js +1 -12
- package/esm2015/browser/src/render/animation_engine_next.js +8 -4
- package/esm2015/browser/src/render/shared.js +18 -3
- package/esm2015/browser/src/render/timeline_animation_engine.js +6 -2
- package/esm2015/browser/src/render/transition_animation_engine.js +23 -19
- package/esm2015/browser/src/render/web_animations/web_animations_driver.js +3 -3
- package/esm2015/browser/src/util.js +57 -1
- package/esm2015/src/animation_metadata.js +626 -659
- package/esm2015/src/version.js +1 -1
- package/esm5/browser/index.js +5 -1
- package/esm5/browser/public_api.js +6 -1
- package/esm5/browser/src/browser.js +6 -1
- package/esm5/browser/src/dsl/animation.js +1 -1
- package/esm5/browser/src/dsl/animation_ast_builder.js +11 -4
- package/esm5/browser/src/dsl/animation_timeline_builder.js +27 -21
- package/esm5/browser/src/dsl/animation_timeline_instruction.js +1 -1
- package/esm5/browser/src/dsl/animation_transition_expr.js +1 -1
- package/esm5/browser/src/dsl/animation_transition_factory.js +3 -3
- package/esm5/browser/src/dsl/animation_transition_instruction.js +1 -1
- package/esm5/browser/src/dsl/animation_trigger.js +2 -8
- package/esm5/browser/src/dsl/element_instruction_map.js +1 -1
- package/esm5/browser/src/dsl/style_normalization/animation_style_normalizer.js +3 -15
- package/esm5/browser/src/dsl/style_normalization/web_animations_style_normalizer.js +8 -1
- package/esm5/browser/src/private_export.js +8 -1
- package/esm5/browser/src/render/animation_driver.js +8 -3
- package/esm5/browser/src/render/animation_engine_next.js +5 -4
- package/esm5/browser/src/render/css_keyframes/css_keyframes_driver.js +1 -1
- package/esm5/browser/src/render/css_keyframes/css_keyframes_player.js +2 -4
- package/esm5/browser/src/render/css_keyframes/direct_style_player.js +8 -1
- package/esm5/browser/src/render/css_keyframes/element_animation_style_handler.js +1 -1
- package/esm5/browser/src/render/shared.js +19 -3
- package/esm5/browser/src/render/timeline_animation_engine.js +10 -2
- package/esm5/browser/src/render/transition_animation_engine.js +36 -39
- package/esm5/browser/src/render/web_animations/dom_animation.js +1 -1
- package/esm5/browser/src/render/web_animations/web_animations_driver.js +3 -3
- package/esm5/browser/src/render/web_animations/web_animations_player.js +3 -7
- package/esm5/browser/src/util.js +51 -3
- package/esm5/browser/testing/index.js +5 -1
- package/esm5/browser/testing/public_api.js +6 -1
- package/esm5/browser/testing/src/mock_animation_driver.js +12 -17
- package/esm5/browser/testing/src/testing.js +8 -1
- package/esm5/index.js +5 -1
- package/esm5/public_api.js +6 -1
- package/esm5/src/animation_builder.js +3 -93
- package/esm5/src/animation_event.js +1 -1
- package/esm5/src/animation_metadata.js +439 -568
- package/esm5/src/animations.js +6 -1
- package/esm5/src/players/animation_group_player.js +2 -4
- package/esm5/src/players/animation_player.js +11 -14
- package/esm5/src/private_export.js +8 -1
- package/esm5/src/util.js +1 -1
- package/esm5/src/version.js +3 -23
- package/fesm2015/animations.js +446 -631
- package/fesm2015/animations.js.map +1 -1
- package/fesm2015/browser/testing.js +1 -1
- package/fesm2015/browser/testing.js.map +1 -1
- package/fesm2015/browser.js +104 -29
- package/fesm2015/browser.js.map +1 -1
- package/fesm5/animations.js +470 -577
- package/fesm5/animations.js.map +1 -1
- package/fesm5/browser/testing.js +28 -10
- package/fesm5/browser/testing.js.map +1 -1
- package/fesm5/browser.js +200 -83
- package/fesm5/browser.js.map +1 -1
- package/package.json +3 -3
- package/src/animation_metadata.d.ts +673 -650
package/fesm2015/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v6.0.
|
|
2
|
+
* @license Angular v6.0.7
|
|
3
3
|
* (c) 2010-2018 Google, Inc. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -11,6 +11,18 @@ import { Injectable } from '@angular/core';
|
|
|
11
11
|
* @fileoverview added by tsickle
|
|
12
12
|
* @suppress {checkTypes} checked by tsc
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* @return {?}
|
|
16
|
+
*/
|
|
17
|
+
function isBrowser() {
|
|
18
|
+
return (typeof window !== 'undefined' && typeof window.document !== 'undefined');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @return {?}
|
|
22
|
+
*/
|
|
23
|
+
function isNode() {
|
|
24
|
+
return (typeof process !== 'undefined');
|
|
25
|
+
}
|
|
14
26
|
/**
|
|
15
27
|
* @param {?} players
|
|
16
28
|
* @return {?}
|
|
@@ -161,10 +173,13 @@ let _matches = (element, selector) => false;
|
|
|
161
173
|
let _query = (element, selector, multi) => {
|
|
162
174
|
return [];
|
|
163
175
|
};
|
|
164
|
-
|
|
176
|
+
// Define utility methods for browsers and platform-server(domino) where Element
|
|
177
|
+
// and utility methods exist.
|
|
178
|
+
const _isNode = isNode();
|
|
179
|
+
if (_isNode || typeof Element !== 'undefined') {
|
|
165
180
|
// this is well supported in all browsers
|
|
166
181
|
_contains = (elm1, elm2) => { return /** @type {?} */ (elm1.contains(elm2)); };
|
|
167
|
-
if (Element.prototype.matches) {
|
|
182
|
+
if (_isNode || Element.prototype.matches) {
|
|
168
183
|
_matches = (element, selector) => element.matches(selector);
|
|
169
184
|
}
|
|
170
185
|
else {
|
|
@@ -306,8 +321,6 @@ class NoopAnimationDriver {
|
|
|
306
321
|
NoopAnimationDriver.decorators = [
|
|
307
322
|
{ type: Injectable }
|
|
308
323
|
];
|
|
309
|
-
/** @nocollapse */
|
|
310
|
-
NoopAnimationDriver.ctorParameters = () => [];
|
|
311
324
|
/**
|
|
312
325
|
* \@experimental
|
|
313
326
|
* @abstract
|
|
@@ -457,6 +470,46 @@ function copyStyles(styles, readPrototype, destination = {}) {
|
|
|
457
470
|
}
|
|
458
471
|
return destination;
|
|
459
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* @param {?} element
|
|
475
|
+
* @param {?} key
|
|
476
|
+
* @param {?} value
|
|
477
|
+
* @return {?}
|
|
478
|
+
*/
|
|
479
|
+
function getStyleAttributeString(element, key, value) {
|
|
480
|
+
// Return the key-value pair string to be added to the style attribute for the
|
|
481
|
+
// given CSS style key.
|
|
482
|
+
if (value) {
|
|
483
|
+
return key + ':' + value + ';';
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
return '';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* @param {?} element
|
|
491
|
+
* @return {?}
|
|
492
|
+
*/
|
|
493
|
+
function writeStyleAttribute(element) {
|
|
494
|
+
// Read the style property of the element and manually reflect it to the
|
|
495
|
+
// style attribute. This is needed because Domino on platform-server doesn't
|
|
496
|
+
// understand the full set of allowed CSS properties and doesn't reflect some
|
|
497
|
+
// of them automatically.
|
|
498
|
+
let /** @type {?} */ styleAttrValue = '';
|
|
499
|
+
for (let /** @type {?} */ i = 0; i < element.style.length; i++) {
|
|
500
|
+
const /** @type {?} */ key = element.style.item(i);
|
|
501
|
+
styleAttrValue += getStyleAttributeString(element, key, element.style.getPropertyValue(key));
|
|
502
|
+
}
|
|
503
|
+
for (const /** @type {?} */ key in element.style) {
|
|
504
|
+
// Skip internal Domino properties that don't need to be reflected.
|
|
505
|
+
if (!element.style.hasOwnProperty(key) || key.startsWith('_')) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const /** @type {?} */ dashKey = camelCaseToDashCase(key);
|
|
509
|
+
styleAttrValue += getStyleAttributeString(element, dashKey, element.style[key]);
|
|
510
|
+
}
|
|
511
|
+
element.setAttribute('style', styleAttrValue);
|
|
512
|
+
}
|
|
460
513
|
/**
|
|
461
514
|
* @param {?} element
|
|
462
515
|
* @param {?} styles
|
|
@@ -468,6 +521,10 @@ function setStyles(element, styles) {
|
|
|
468
521
|
const /** @type {?} */ camelProp = dashCaseToCamelCase(prop);
|
|
469
522
|
element.style[camelProp] = styles[prop];
|
|
470
523
|
});
|
|
524
|
+
// On the server set the 'style' attribute since it's not automatically reflected.
|
|
525
|
+
if (isNode()) {
|
|
526
|
+
writeStyleAttribute(element);
|
|
527
|
+
}
|
|
471
528
|
}
|
|
472
529
|
}
|
|
473
530
|
/**
|
|
@@ -481,6 +538,10 @@ function eraseStyles(element, styles) {
|
|
|
481
538
|
const /** @type {?} */ camelProp = dashCaseToCamelCase(prop);
|
|
482
539
|
element.style[camelProp] = '';
|
|
483
540
|
});
|
|
541
|
+
// On the server set the 'style' attribute since it's not automatically reflected.
|
|
542
|
+
if (isNode()) {
|
|
543
|
+
writeStyleAttribute(element);
|
|
544
|
+
}
|
|
484
545
|
}
|
|
485
546
|
}
|
|
486
547
|
/**
|
|
@@ -576,6 +637,13 @@ const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
|
|
576
637
|
function dashCaseToCamelCase(input) {
|
|
577
638
|
return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
|
|
578
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* @param {?} input
|
|
642
|
+
* @return {?}
|
|
643
|
+
*/
|
|
644
|
+
function camelCaseToDashCase(input) {
|
|
645
|
+
return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
646
|
+
}
|
|
579
647
|
/**
|
|
580
648
|
* @param {?} duration
|
|
581
649
|
* @param {?} delay
|
|
@@ -2543,9 +2611,10 @@ class AnimationTransitionFactory {
|
|
|
2543
2611
|
* @param {?=} currentOptions
|
|
2544
2612
|
* @param {?=} nextOptions
|
|
2545
2613
|
* @param {?=} subInstructions
|
|
2614
|
+
* @param {?=} skipAstBuild
|
|
2546
2615
|
* @return {?}
|
|
2547
2616
|
*/
|
|
2548
|
-
build(driver, element, currentState, nextState, enterClassName, leaveClassName, currentOptions, nextOptions, subInstructions) {
|
|
2617
|
+
build(driver, element, currentState, nextState, enterClassName, leaveClassName, currentOptions, nextOptions, subInstructions, skipAstBuild) {
|
|
2549
2618
|
const /** @type {?} */ errors = [];
|
|
2550
2619
|
const /** @type {?} */ transitionAnimationParams = this.ast.options && this.ast.options.params || EMPTY_OBJECT;
|
|
2551
2620
|
const /** @type {?} */ currentAnimationParams = currentOptions && currentOptions.params || EMPTY_OBJECT;
|
|
@@ -2557,7 +2626,7 @@ class AnimationTransitionFactory {
|
|
|
2557
2626
|
const /** @type {?} */ postStyleMap = new Map();
|
|
2558
2627
|
const /** @type {?} */ isRemoval = nextState === 'void';
|
|
2559
2628
|
const /** @type {?} */ animationOptions = { params: Object.assign({}, transitionAnimationParams, nextAnimationParams) };
|
|
2560
|
-
const /** @type {?} */ timelines = buildAnimationTimelines(driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles, nextStateStyles, animationOptions, subInstructions, errors);
|
|
2629
|
+
const /** @type {?} */ timelines = skipAstBuild ? [] : buildAnimationTimelines(driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles, nextStateStyles, animationOptions, subInstructions, errors);
|
|
2561
2630
|
let /** @type {?} */ totalTime = 0;
|
|
2562
2631
|
timelines.forEach(tl => { totalTime = Math.max(tl.duration + tl.delay, totalTime); });
|
|
2563
2632
|
if (errors.length) {
|
|
@@ -2731,10 +2800,12 @@ function balanceProperties(obj, key1, key2) {
|
|
|
2731
2800
|
const EMPTY_INSTRUCTION_MAP = new ElementInstructionMap();
|
|
2732
2801
|
class TimelineAnimationEngine {
|
|
2733
2802
|
/**
|
|
2803
|
+
* @param {?} bodyNode
|
|
2734
2804
|
* @param {?} _driver
|
|
2735
2805
|
* @param {?} _normalizer
|
|
2736
2806
|
*/
|
|
2737
|
-
constructor(_driver, _normalizer) {
|
|
2807
|
+
constructor(bodyNode, _driver, _normalizer) {
|
|
2808
|
+
this.bodyNode = bodyNode;
|
|
2738
2809
|
this._driver = _driver;
|
|
2739
2810
|
this._normalizer = _normalizer;
|
|
2740
2811
|
this._animations = {};
|
|
@@ -2967,7 +3038,6 @@ class StateValue {
|
|
|
2967
3038
|
}
|
|
2968
3039
|
const VOID_VALUE = 'void';
|
|
2969
3040
|
const DEFAULT_STATE_VALUE = new StateValue(VOID_VALUE);
|
|
2970
|
-
const DELETED_STATE_VALUE = new StateValue('DELETED');
|
|
2971
3041
|
class AnimationTransitionNamespace {
|
|
2972
3042
|
/**
|
|
2973
3043
|
* @param {?} id
|
|
@@ -3078,9 +3148,6 @@ class AnimationTransitionNamespace {
|
|
|
3078
3148
|
if (!fromState) {
|
|
3079
3149
|
fromState = DEFAULT_STATE_VALUE;
|
|
3080
3150
|
}
|
|
3081
|
-
else if (fromState === DELETED_STATE_VALUE) {
|
|
3082
|
-
return player;
|
|
3083
|
-
}
|
|
3084
3151
|
const /** @type {?} */ isRemoval = toState.value === VOID_VALUE;
|
|
3085
3152
|
// normally this isn't reached by here, however, if an object expression
|
|
3086
3153
|
// is passed in then it may be a new object each time. Comparing the value
|
|
@@ -3390,10 +3457,12 @@ class AnimationTransitionNamespace {
|
|
|
3390
3457
|
|
|
3391
3458
|
class TransitionAnimationEngine {
|
|
3392
3459
|
/**
|
|
3460
|
+
* @param {?} bodyNode
|
|
3393
3461
|
* @param {?} driver
|
|
3394
3462
|
* @param {?} _normalizer
|
|
3395
3463
|
*/
|
|
3396
|
-
constructor(driver, _normalizer) {
|
|
3464
|
+
constructor(bodyNode, driver, _normalizer) {
|
|
3465
|
+
this.bodyNode = bodyNode;
|
|
3397
3466
|
this.driver = driver;
|
|
3398
3467
|
this._normalizer = _normalizer;
|
|
3399
3468
|
this.players = [];
|
|
@@ -3692,10 +3761,11 @@ class TransitionAnimationEngine {
|
|
|
3692
3761
|
* @param {?} subTimelines
|
|
3693
3762
|
* @param {?} enterClassName
|
|
3694
3763
|
* @param {?} leaveClassName
|
|
3764
|
+
* @param {?=} skipBuildAst
|
|
3695
3765
|
* @return {?}
|
|
3696
3766
|
*/
|
|
3697
|
-
_buildInstruction(entry, subTimelines, enterClassName, leaveClassName) {
|
|
3698
|
-
return entry.transition.build(this.driver, entry.element, entry.fromState.value, entry.toState.value, enterClassName, leaveClassName, entry.fromState.options, entry.toState.options, subTimelines);
|
|
3767
|
+
_buildInstruction(entry, subTimelines, enterClassName, leaveClassName, skipBuildAst) {
|
|
3768
|
+
return entry.transition.build(this.driver, entry.element, entry.fromState.value, entry.toState.value, enterClassName, leaveClassName, entry.fromState.options, entry.toState.options, subTimelines, skipBuildAst);
|
|
3699
3769
|
}
|
|
3700
3770
|
/**
|
|
3701
3771
|
* @param {?} containerElement
|
|
@@ -3728,10 +3798,6 @@ class TransitionAnimationEngine {
|
|
|
3728
3798
|
}
|
|
3729
3799
|
});
|
|
3730
3800
|
}
|
|
3731
|
-
const /** @type {?} */ stateMap = this.statesByElement.get(element);
|
|
3732
|
-
if (stateMap) {
|
|
3733
|
-
Object.keys(stateMap).forEach(triggerName => stateMap[triggerName] = DELETED_STATE_VALUE);
|
|
3734
|
-
}
|
|
3735
3801
|
}
|
|
3736
3802
|
/**
|
|
3737
3803
|
* @param {?} element
|
|
@@ -3862,7 +3928,7 @@ class TransitionAnimationEngine {
|
|
|
3862
3928
|
disabledElementsSet.add(nodesThatAreDisabled[i]);
|
|
3863
3929
|
}
|
|
3864
3930
|
});
|
|
3865
|
-
const /** @type {?} */ bodyNode =
|
|
3931
|
+
const /** @type {?} */ bodyNode = this.bodyNode;
|
|
3866
3932
|
const /** @type {?} */ allTriggerElements = Array.from(this.statesByElement.keys());
|
|
3867
3933
|
const /** @type {?} */ enterNodeMap = buildRootMap(allTriggerElements, this.collectedEnterElements);
|
|
3868
3934
|
// this must occur before the instructions are built below such that
|
|
@@ -3926,17 +3992,24 @@ class TransitionAnimationEngine {
|
|
|
3926
3992
|
return;
|
|
3927
3993
|
}
|
|
3928
3994
|
}
|
|
3929
|
-
|
|
3930
|
-
player.destroy();
|
|
3931
|
-
return;
|
|
3932
|
-
}
|
|
3995
|
+
const /** @type {?} */ nodeIsOrphaned = !bodyNode || !this.driver.containsElement(bodyNode, element);
|
|
3933
3996
|
const /** @type {?} */ leaveClassName = /** @type {?} */ ((leaveNodeMapIds.get(element)));
|
|
3934
3997
|
const /** @type {?} */ enterClassName = /** @type {?} */ ((enterNodeMapIds.get(element)));
|
|
3935
|
-
const /** @type {?} */ instruction = /** @type {?} */ ((this._buildInstruction(entry, subTimelines, enterClassName, leaveClassName)));
|
|
3998
|
+
const /** @type {?} */ instruction = /** @type {?} */ ((this._buildInstruction(entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned)));
|
|
3936
3999
|
if (instruction.errors && instruction.errors.length) {
|
|
3937
4000
|
erroneousTransitions.push(instruction);
|
|
3938
4001
|
return;
|
|
3939
4002
|
}
|
|
4003
|
+
// even though the element may not be apart of the DOM, it may
|
|
4004
|
+
// still be added at a later point (due to the mechanics of content
|
|
4005
|
+
// projection and/or dynamic component insertion) therefore it's
|
|
4006
|
+
// important we still style the element.
|
|
4007
|
+
if (nodeIsOrphaned) {
|
|
4008
|
+
player.onStart(() => eraseStyles(element, instruction.fromStyles));
|
|
4009
|
+
player.onDestroy(() => setStyles(element, instruction.toStyles));
|
|
4010
|
+
skippedPlayers.push(player);
|
|
4011
|
+
return;
|
|
4012
|
+
}
|
|
3940
4013
|
// if a unmatched transition is queued to go then it SHOULD NOT render
|
|
3941
4014
|
// an animation and cancel the previously running animations.
|
|
3942
4015
|
if (entry.isFallbackTransition) {
|
|
@@ -4755,15 +4828,17 @@ function replacePostStylesAsPre(element, allPreStyleElements, allPostStyleElemen
|
|
|
4755
4828
|
*/
|
|
4756
4829
|
class AnimationEngine {
|
|
4757
4830
|
/**
|
|
4831
|
+
* @param {?} bodyNode
|
|
4758
4832
|
* @param {?} _driver
|
|
4759
4833
|
* @param {?} normalizer
|
|
4760
4834
|
*/
|
|
4761
|
-
constructor(_driver, normalizer) {
|
|
4835
|
+
constructor(bodyNode, _driver, normalizer) {
|
|
4836
|
+
this.bodyNode = bodyNode;
|
|
4762
4837
|
this._driver = _driver;
|
|
4763
4838
|
this._triggerCache = {};
|
|
4764
4839
|
this.onRemovalComplete = (element, context) => { };
|
|
4765
|
-
this._transitionEngine = new TransitionAnimationEngine(_driver, normalizer);
|
|
4766
|
-
this._timelineEngine = new TimelineAnimationEngine(_driver, normalizer);
|
|
4840
|
+
this._transitionEngine = new TransitionAnimationEngine(bodyNode, _driver, normalizer);
|
|
4841
|
+
this._timelineEngine = new TimelineAnimationEngine(bodyNode, _driver, normalizer);
|
|
4767
4842
|
this._transitionEngine.onRemovalComplete = (element, context) => this.onRemovalComplete(element, context);
|
|
4768
4843
|
}
|
|
4769
4844
|
/**
|
|
@@ -5810,7 +5885,7 @@ function supportsWebAnimations() {
|
|
|
5810
5885
|
* @return {?}
|
|
5811
5886
|
*/
|
|
5812
5887
|
function getElementAnimateFn() {
|
|
5813
|
-
return (
|
|
5888
|
+
return (isBrowser() && (/** @type {?} */ (Element)).prototype['animate']) || {};
|
|
5814
5889
|
}
|
|
5815
5890
|
|
|
5816
5891
|
/**
|