@@ -2878,6 +2878,7 @@ abstract class ModelElement extends Canonicalization
2878
2878
_rawDocs = computeDocumentationComment ?? '' ;
2879
2879
_rawDocs = stripComments (_rawDocs) ?? '' ;
2880
2880
_rawDocs = _injectExamples (_rawDocs);
2881
+ _rawDocs = _injectAnimations (_rawDocs);
2881
2882
_rawDocs = _stripMacroTemplatesAndAddToIndex (_rawDocs);
2882
2883
}
2883
2884
return _rawDocs;
@@ -3527,24 +3528,156 @@ abstract class ModelElement extends Canonicalization
3527
3528
});
3528
3529
}
3529
3530
3530
- /// Replace {@macro ...} in API comments with the contents of the macro
3531
+ /// Replace {@animation ...} in API comments with some HTML to manage an
3532
+ /// MPEG 4 video as an animation.
3531
3533
///
3532
3534
/// Syntax:
3533
3535
///
3534
- /// {@macro NAME}
3536
+ /// {@animation NAME WIDTH HEIGHT URL}
3537
+ ///
3538
+ /// Example:
3539
+ ///
3540
+ /// {@animation my_video 300 300 https://example.com/path/to/video.mp4}
3541
+ ///
3542
+ /// Which will render the HTML necessary for embedding a simple click-to-play
3543
+ /// HTML5 video player with no controls.
3544
+ ///
3545
+ /// The NAME should be a unique name that is a valid javascript identifier,
3546
+ /// and will be used as the id for the video tag.
3547
+ ///
3548
+ /// The width and height must be integers specifying the dimensions of the
3549
+ /// video file in pixels.
3550
+ String _injectAnimations (String rawDocs) {
3551
+ // Matches all animation directives (even some invalid ones). This is so
3552
+ // we can give good error messages if the directive is malformed, instead of
3553
+ // just silently emitting it as-is.
3554
+ final RegExp basicAnimationRegExp =
3555
+ new RegExp (r'''{@animation\s+([^}]+)}''' );
3556
+
3557
+ // Animations have four parameters, and the last one can be surrounded by
3558
+ // quotes (which are ignored). This RegExp is used to validate the directive
3559
+ // for the correct number of parameters.
3560
+ final RegExp animationRegExp =
3561
+ new RegExp (r'''{@animation\s+([^}\s]+)\s+([^}\s]+)\s+([^}\s]+)'''
3562
+ r'''\s+['"]?([^}]+)['"]?}''' );
3563
+
3564
+ // Matches valid javascript identifiers.
3565
+ final RegExp validNameRegExp = new RegExp (r'^[a-zA-Z_][a-zA-Z0-9_]*$' );
3566
+
3567
+ // Keeps names unique.
3568
+ final Set <String > uniqueNames = new Set <String >();
3569
+
3570
+ return rawDocs.replaceAllMapped (basicAnimationRegExp, (basicMatch) {
3571
+ final Match match = animationRegExp.firstMatch (basicMatch[0 ]);
3572
+ if (match == null ) {
3573
+ warn (PackageWarning .invalidParameter,
3574
+ message: 'Invalid @animation directive: ${basicMatch [0 ]}\n '
3575
+ 'Animation directives must be of the form: {@animation NAME '
3576
+ 'WIDTH HEIGHT URL}' );
3577
+ return '' ;
3578
+ }
3579
+ String name = match[1 ];
3580
+ if (! validNameRegExp.hasMatch (name)) {
3581
+ warn (PackageWarning .invalidParameter,
3582
+ message: 'An animation has an invalid name: $name . The name can '
3583
+ 'only contain letters, numbers and underscores.' );
3584
+ return '' ;
3585
+ } else {
3586
+ if (uniqueNames.contains (name)) {
3587
+ warn (PackageWarning .invalidParameter,
3588
+ message:
3589
+ 'An animation has a non-unique name: $name . Animation names '
3590
+ 'must be unique.' );
3591
+ return '' ;
3592
+ }
3593
+ uniqueNames.add (name);
3594
+ }
3595
+ int width;
3596
+ try {
3597
+ width = int .parse (match[2 ]);
3598
+ } on FormatException {
3599
+ warn (PackageWarning .invalidParameter,
3600
+ message: 'An animation has an invalid width ($name ): ${match [2 ]}. The '
3601
+ 'width must be an integer.' );
3602
+ return '' ;
3603
+ }
3604
+ int height;
3605
+ try {
3606
+ height = int .parse (match[3 ]);
3607
+ } on FormatException {
3608
+ warn (PackageWarning .invalidParameter,
3609
+ message: 'An animation has an invalid height ($name ): ${match [3 ]}. The '
3610
+ 'height must be an integer.' );
3611
+ return '' ;
3612
+ }
3613
+ Uri movieUrl;
3614
+ try {
3615
+ movieUrl = Uri .parse (match[4 ]);
3616
+ } on FormatException catch (e) {
3617
+ warn (PackageWarning .invalidParameter,
3618
+ message: 'An animation URL could not be parsed ($name ): ${match [4 ]}\n $e ' );
3619
+ return '' ;
3620
+ }
3621
+ final String overlayName = '${name }_play_button_' ;
3622
+
3623
+ // Blank lines before and after, and no indenting at the beginning and end
3624
+ // is needed so that Markdown doesn't confuse this with code, so be
3625
+ // careful of whitespace here.
3626
+ return '''
3627
+
3628
+ <div style="position: relative;">
3629
+ <div id="${overlayName }"
3630
+ onclick="if ($name .paused) {
3631
+ $name .play();
3632
+ this.style.display = 'none';
3633
+ } else {
3634
+ $name .pause();
3635
+ this.style.display = 'block';
3636
+ }"
3637
+ style="position:absolute;
3638
+ width:${width }px;
3639
+ height:${height }px;
3640
+ z-index:100000;
3641
+ background-position: center;
3642
+ background-repeat: no-repeat;
3643
+ background-image: url(static-assets/play_button.svg);">
3644
+ </div>
3645
+ <video id="$name "
3646
+ style="width:${width }px; height:${height }px;"
3647
+ onclick="if (this.paused) {
3648
+ this.play();
3649
+ $overlayName .style.display = 'none';
3650
+ } else {
3651
+ this.pause();
3652
+ $overlayName .style.display = 'block';
3653
+ }" loop>
3654
+ <source src="$movieUrl " type="video/mp4"/>
3655
+ </video>
3656
+ </div>
3657
+
3658
+ ''' ; // String must end at beginning of line, or following inline text will be
3659
+ // indented.
3660
+ });
3661
+ }
3662
+
3663
+ /// Replace {@macro ...} in API comments with the contents of the macro
3664
+ ///
3665
+ /// Syntax:
3666
+ ///
3667
+ /// {@macro NAME}
3535
3668
///
3536
3669
/// Example:
3537
3670
///
3538
3671
/// You define the template in any comment for a documentable entity like:
3539
3672
///
3540
- /// { @template foo}
3673
+ /// { @template foo}
3541
3674
/// Foo contents!
3542
- /// { @endtemplate}
3675
+ /// { @endtemplate}
3543
3676
///
3544
3677
/// and them somewhere use it like this:
3545
3678
///
3546
3679
/// Some comments
3547
- /// { @macro foo}
3680
+ /// { @macro foo}
3548
3681
/// More comments
3549
3682
///
3550
3683
/// Which will render
@@ -3564,13 +3697,13 @@ abstract class ModelElement extends Canonicalization
3564
3697
});
3565
3698
}
3566
3699
3567
- /// Parse { @template ...} in API comments and store them in the index on the package.
3700
+ /// Parse { @template ...} in API comments and store them in the index on the package.
3568
3701
///
3569
3702
/// Syntax:
3570
3703
///
3571
- /// { @template NAME}
3704
+ /// { @template NAME}
3572
3705
/// The contents of the macro
3573
- /// { @endtemplate}
3706
+ /// { @endtemplate}
3574
3707
///
3575
3708
String _stripMacroTemplatesAndAddToIndex (String rawDocs) {
3576
3709
final templateRegExp = new RegExp (
@@ -4133,6 +4266,9 @@ class PackageGraph extends Canonicalization
4133
4266
// so bracket with a triple quote for defense.
4134
4267
warningMessage = 'generic type handled as HTML: """${message }"""' ;
4135
4268
break ;
4269
+ case PackageWarning .invalidParameter:
4270
+ warningMessage = 'invalid parameter to dartdoc directive: ${message }' ;
4271
+ break ;
4136
4272
}
4137
4273
4138
4274
List <String > messageParts = [warningMessage];
@@ -5434,7 +5570,14 @@ class PackageBuilder {
5434
5570
// TODO(jcollins-g): explode this into detailed command line options.
5435
5571
if (config.showWarnings) {
5436
5572
for (PackageWarning kind in PackageWarning .values) {
5437
- warningOptions.warn (kind);
5573
+ switch (kind) {
5574
+ case PackageWarning .invalidParameter:
5575
+ warningOptions.error (kind);
5576
+ break ;
5577
+ default :
5578
+ warningOptions.warn (kind);
5579
+ break ;
5580
+ }
5438
5581
}
5439
5582
}
5440
5583
return warningOptions;
0 commit comments