Commit graph

658 commits

Author SHA1 Message Date
Kristiyan Kostadinov
7c068861b7 refactor(compiler): output arrow functions for deferred dependencies functions (#51650)
Reworks the compiler to generate arrow functions for the deferred dependencies which reduces the number of bytes we generate.

PR Close #51650
2023-09-05 18:16:59 +00:00
Kristiyan Kostadinov
05a16b973d refactor(compiler): add support for advanced tracking expressions (#51618)
These changes build on top of #51514 to add support for advanced expressions inside the `track` parameter of `for` loop blocks. There are two different outputs that the compiler can generate:

1. If the tracking function only references the item or `$index`, the compiler generates a pure arrow function as a constant references in the `repeaterCreate` instruction.
2. If the tracking function has references to properties outside of the `for` loop block, the compiler will rewrite those references to go through `this` and generate a function declaration. The runtime will `bind` the declaration to the current component instance so that the rewritten `this` references are resolved correctly.

Advanced tracking expression come with the following limitations to ensure the best possible performance:
1. They can only reference the item, `$index` and properties directly on the component instance. This means that there'll be an error when accessing this like local template variables and references. While we could get this to work, we would have to traverse the context tree at runtime which will degrade the performance of the loop, because it's a linear time operation that is performed on each comparison. Furthermore, allowing local references would require us re-evaluate the list when any one of them has changed.
2. Pipes aren't allowed inside the tracking function.
3. Object literals and pipes used inside the tracking expression will be recreated on each invocation.

PR Close #51618
2023-09-05 14:19:18 +00:00
Kristiyan Kostadinov
f36cdd6d1a refactor(core): add instruction to reference component instance (#51618)
Adds an instruction that allows us to access the containing component instance directly instead of having to traverse the context tree. This will be necessary for the tracking function of `for` loop blocks.

PR Close #51618
2023-09-05 14:19:18 +00:00
Kristiyan Kostadinov
6ecafa3305 refactor(compiler): computed for loop variables exposed on the wrong scope (#51618)
Fixes that the computed for loop variables (e.g. $first and $last) were exposed on the parent scope instead of the for loop scope.

PR Close #51618
2023-09-05 14:19:18 +00:00
Kristiyan Kostadinov
98d98f2c94 refactor(compiler): incorrect validation of switch blocks when preserveWhitespaces is enabled (#51570)
When `preserveWhitespaces` is enabled, `switch` blocks can end up with content inside their main block due to the indentation that is usually used for the nested cases. This was tripping up the validation that doesn't allow content inside the main block of `switch`.

These changes update the validation to ignore empty text nodes.

PR Close #51570
2023-09-05 14:18:44 +00:00
Kristiyan Kostadinov
685d01e106 perf(core): chain template instructions (#51546)
With the new control flow and defer blocks it'll be common for several template instructions to be declare one after another. These changes add support for chaining to the `template` instruction which will allow us to save some bytes.

PR Close #51546
2023-08-29 16:38:52 +00:00
Kristiyan Kostadinov
d83dfaa8ea refactor(compiler): generate for loop block instructions (#51514)
Adds the initial implementation to generate the instructions for the `for` loop block.

**Note:** the expressions we support in the `track` paramateter are currently limited to tracking by identity or index, or a specific property of the item. Supporting more advanced expression will require additional work that I'll do in a follow-up PR.

PR Close #51514
2023-08-29 16:38:22 +00:00
Dylan Hunn
6669bea544 refactor(compiler): Share the same phase list for components and hosts (#51498)
Component compilation and host binding compilation previously used separate compilation emit functions. This was a bit messy, because we had to manage the relative orders of both phase lists. Indeed, there was already some inconsistency between the precise orders!

This commit refactors the emit functions to share the same phase list, and thus guarantees they will always be in the same order.

PR Close #51498
2023-08-28 21:51:04 +00:00
Dylan Hunn
cae01dae1b refactor(compiler): Support class and style attrs in host bindings (#51498)
The template pipeline is already capable of parsing and processing class and style attributes on templates. We now extend that functionality to host bindings.

The parser, for some reason, splits out class and style attributes into a `specialAttributes` field. We merge them back into the main attributes map, and allow the template pipeline to process them normally.

PR Close #51498
2023-08-28 21:51:04 +00:00
Dylan Hunn
0df7828637 refactor(compiler): Extract host binding static attributes to hostAttrs (#51498)
Host bindings can apply static attributes. These will be extracted to a `hostAttrs` field on the host binding function's metadata.

In order to achieve this, we add an `attributes` field to the host binding job. Then, we peform attribute exraction on host bindings. We finally populate the `attributes` field directly, instead of relying on a `consts` array.

PR Close #51498
2023-08-28 21:51:04 +00:00
Miles Malerba
b064b29776 refactor(compiler): use correct closure variable naming (#51353)
Updates the closure variable naming for i18n variables to match those
generated by TemplateDefinitionBuilder

PR Close #51353
2023-08-28 18:57:08 +00:00
Miles Malerba
d13a2ee064 refactor(compiler): support i18n messages in the consts array (#51353)
Creates a new `ExtractedMessageOp` which is consumed by the const
collection pahse to serialize the i18n message into the consts array.
Also adds support to the consts array for initialization statements.

PR Close #51353
2023-08-28 18:57:08 +00:00
Andrew Kushnir
c4deaac5b0 refactor(core): initial implementation of {#defer} block runtime (#51347)
This commit adds an initial implementation of the `{#defer}` block runtime, which supports the `when` conditions. More conditions and basic prefetching support will be added in followup PRs.

PR Close #51347
2023-08-28 17:09:52 +00:00
Kristiyan Kostadinov
36663e6ef6 refactor(compiler): parse let parameters in for loop (#51398)
Adds the logic to parse `let` parameters of a `for` loop block which was missed in #51299.

PR Close #51398
2023-08-22 10:40:17 -07:00
Kristiyan Kostadinov
9f6b565abd refactor(compiler): parse for loop track as an expression (#51398)
Adds some logic to store the `track` parameter of a `for` loop block as an expression AST instead of a string.

PR Close #51398
2023-08-22 10:40:17 -07:00
Kristiyan Kostadinov
eb1faa8f87 refactor(compiler): don't allow as expressions in else if blocks (#51398)
Based on some discussions, these changes remove the ability to have an `as` expression on an `else if` block.

PR Close #51398
2023-08-22 10:40:17 -07:00
Kristiyan Kostadinov
9cc0cbed0c refactor(compiler): generate if block instructions (#51380)
Adds the logic to generate the instructions for `if` blocks. There are two primary use cases we need to account for:

A conditional that doesn't use the `as` parameter of the `if` block. To support it we generate a nested ternary expression that evaluates to the index of the template whose condition is truthy. If the block doesn't have an `else` branch, we pass in a special `-1` value which means that no view will be rendered.

Example with an `else`:
```ts
// {#if expr}
//   ...
//   {:else if otherExpr} ...
//   {:else} ...
// {/if}

if (rf & 1) {
  ɵɵtemplate(0, App_Conditional_0_Template, 0, 0);
  ɵɵtemplate(1, App_Conditional_1_Template, 0, 0);
  ɵɵtemplate(2, App_Conditional_2_Template, 0, 0);
}
if (rf & 2) {
  ɵɵconditional(0, ctx.expr ? 0 : ctx.otherExpr ? 1 : 2);
}
```

Example without an `else`:
```ts
// {#if expr}
//   ...
//   {:else if otherExpr} ...
// {/if}

if (rf & 1) {
  ɵɵtemplate(0, App_Conditional_0_Template, 0, 0);
  ɵɵtemplate(1, App_Conditional_1_Template, 0, 0);
}
if (rf & 2) {
  ɵɵconditional(0, ctx.expr ? 0 : ctx.otherExpr ? 1 : -1);
}
```

If a conditional captures it's value in an alias (e.g. `{#if expr; as foo}`) we need to assign the value to a temporary variable before passing it along to `conditional`.

```ts
// {#if expr; as alias}...{/if}
if (rf & 1) {
  ɵɵtemplate(0, App_Conditional_0_Template, 1, 0);
}
if (rf & 2) {
  let App_contFlowTmp;
  ɵɵconditional(0, (App_contFlowTmp = ctx.expr) ? 0 : -1, App_contFlowTmp);
}
```

PR Close #51380
2023-08-18 10:01:02 -07:00
Kristiyan Kostadinov
0c4c773fca refactor(compiler): generate switch block instructions (#51380)
Adds the logic to generate the instructions for `switch` instructions. For the following block:

```html
{#switch value()}
  {:case 0} case 0
  {:case 1} case 1
  {:case 2} case 2
  {:default} default
{/switch}
```

The compiler will produce the following output:

```ts
function App_Template(rf, ctx) {
  if (rf & 1) {
    ɵɵtemplate(0, App_Case_0_Template, 1, 0);
    ɵɵtemplate(1, App_Case_1_Template, 1, 0);
    ɵɵtemplate(2, App_Case_2_Template, 1, 0);
    ɵɵtemplate(3, App_Case_3_Template, 1, 0);
  }
  if (rf & 2) {
    let App_contFlowTmp;
    ɵɵconditional(0, (App_contFlowTmp = ctx.value()) === 0 ? 0 : App_contFlowTmp === 1 ? 1 : App_contFlowTmp === 2 ? 2 : 3);
  }
}
```

PR Close #51380
2023-08-18 10:01:02 -07:00
JoostK
5bd9fbd2c3 fix(compiler-cli): enforce a minimum version to be used when a library uses input transform (#51413)
Angular 16.1 introduced the input transform feature, requiring the partial compilation output to be extended
with a reference to the input transform function. This has resulted in a subtle breaking change, where older
versions of the Angular linker can no longer consume libraries that have started to use this feature.

We do try to support using a 16.1 library from an Angular 16.0 application, but if a library actually
adopts a new feature then this is no longer possible. In such cases, it is desirable to report a message
telling the user that their version of the Angular compiler is too old, as determined by the `"minVersion"`
property that is present in each partial declaration. This version would still indicate that the declaration
required at least Angular 14.0 to be compiled, but this is not accurate once input transforms are being
used. Consequently, this error would not be reported, causing a less informative error once the input transform
was being observed.

Fixes #51411

PR Close #51413
2023-08-18 07:58:53 -07:00
Andrew Kushnir
bcc3c43fca refactor(core): update TestBed to handle async component metadata (#51182)
This commit updates TestBed to wait for async component metadata resolution before compiling components.
Async metadata is added by the compiler in case a component uses defer blocks, which contain deferrable
symbols.

PR Close #51182
2023-08-15 11:32:09 -07:00
Andrew Kushnir
c41a1950fd refactor(compiler): apply component metadata asynchronously when defer blocks are present (#51182)
This commit updates compiler logic to generate the `setClassMetadataAsync` calls for components that used defer blocks. The `setClassMetadataAsync` function loads deferrable dependencies and invokes the `setClassMetadata` synchronously once everything is loaded. This change is needed to avoid eager references to deferrable symbols in component metadata in generated code.

PR Close #51182
2023-08-15 11:32:09 -07:00
Kristiyan Kostadinov
422d0d5ca3 refactor(compiler): handle deferred when trigger with a pipe (#51368)
Fixes that we weren't processing `when` conditions correctly which led to a compilation error when a pipe is used inside the expression.

PR Close #51368
2023-08-15 10:03:59 -07:00
Kristiyan Kostadinov
5212b47bbf refactor(compiler): introduce defer trigger instructions (#51315)
Adds the logic for generating the instructions for the various deferred triggers.

PR Close #51315
2023-08-11 06:55:13 -07:00
Kristiyan Kostadinov
79f9d49fad refactor(compiler): introduce defer block instructions (#51315)
Adds the logic for generating `{#defer}`, `{:placeholder}`, `{:loading}` and `{:error}` block instructions in the compiler.

PR Close #51315
2023-08-11 06:55:13 -07:00
Kristiyan Kostadinov
151b04f683 refactor(compiler): add method for generating template instructions (#51315)
Moves the logic for creating a `ɵɵtemplate` instruction into a separate method so that it can be reused for `defer` blocks.

PR Close #51315
2023-08-11 06:55:13 -07:00
Kristiyan Kostadinov
36b180ade4 refactor(compiler): implement conditional block AST (#51299)
Adds the AST for `if`, `else if` and `else` blocks.

PR Close #51299
2023-08-10 13:48:55 -07:00
Kristiyan Kostadinov
4424920f0b refactor(compiler): implement for block AST (#51299)
Adds the AST for `for` and `empty` blocks.

PR Close #51299
2023-08-10 13:48:55 -07:00
Kristiyan Kostadinov
31c6c5e944 refactor(compiler): implement switch block AST (#51299)
Adds the AST for `switch`, `case` and `default` blocks.

PR Close #51299
2023-08-10 13:48:55 -07:00
Payam Valadkhan
827e10ae0e refactor(compiler): add a factory for component dependencies in local compilation mode (#51089)
A factory generator function called "i0.ɵɵgetComponentDepsFactory" is added to generate a factory function for component dependencies. This function will use the deps tracker to calculate the component's dependencies.

For standalone components the component imports (if exists) will be passed to this function. Alternatively this function can grab the imports directly from the decorate, but such extraaction needs some runtime logic which overlapps with what the trait compiler is doing. So better to pass the imports directly to this function at compile time.

PR Close #51089
2023-08-08 13:58:48 -07:00
Kristiyan Kostadinov
b1f96096d3 refactor(compiler): correctly identify lazy directives and pipes used after a nested defer block (#51262)
Fixes that if a directive/pipe is used after a nested `defer` block, we weren't tracking it as lazy anymore. This was due to the fact that we were resetting the `isInDeferBlock` to false every time instead of the previous value.

PR Close #51262
2023-08-04 11:27:39 -04:00
Kristiyan Kostadinov
1045abd3c8 refactor(compiler): add more deferred validations (#51262)
Adds validations for the following invalid deferred block structures:
* Duplicated triggers.
* Multiple `minimum` parameters on `placeholder` and `loading` blocks.
* Multiple `after` parameters on `loading` blocks.

PR Close #51262
2023-08-04 11:27:39 -04:00
Kristiyan Kostadinov
d11548f2ef refactor(compiler): store deferred triggers as a map (#51262)
Stores the `deferred` block triggers as a map instead of an array, because triggers can't be duplicated and because having to search through an array will be inconvenient later on.

I've also added a `DeferredBlock.visitAll` method to deduplicate the logic from the various visitor implementations.

PR Close #51262
2023-08-04 11:27:39 -04:00
Andrew Kushnir
efb486e8bc refactor(compiler): handle defer blocks in TemplateDefinitionBuilder (#51162)
Updates the TemplateDefinitionBuilder class to generate the `defer` instruction for `{#defer}` blocks. Also generates dependency function that would be invoked at runtime (with dynamic imports inside).

PR Close #51162
2023-08-01 11:50:05 -07:00
Andrew Kushnir
08992a5f2f refactor(compiler): compute the list of dependencies for defer blocks (#51162)
This commit brings the logic to calculate teh set of dependencies for each defer block. For each dependency we also identify whether it can be defer-loaded or not.

PR Close #51162
2023-08-01 11:50:05 -07:00
Andrew Kushnir
553cbaed84 refactor(compiler): update TemplateBinder and DirectiveBinder to work with defer blocks (#51162)
This commit updates the logic of the TemplateBinder and DirectiveBinder classes to recognize defer blocks. The logic is updated to prevent Directive and Pipe matching inside the defer block. Instead, the scope for those blocks would be calculated separately.

PR Close #51162
2023-08-01 11:50:04 -07:00
Dylan Hunn
5a0ecdb58b refactor(compiler): Allow host binding functions to specialize bindings (#50899)
Interestingly, host bindings are parsed quite differently from template functions. For example, bindings such as `[style.foo]: 3px` would be parsed into a value, unit, and type when bound to a template, but will not be parsed as such when used in a host binding.

In this commit, we remedy this shortcoming by adding support for bindings in host binding functions to the template pipeline. In particular, we create a phase to process these bindings, and transform them into the correct output binding kind.

Additionally, we fix some other minor bugs and omissions.

Finally, we enable compilation of host bindings with the template pipeline, which requires us to turn off a number of failing tests.

PR Close #50899
2023-07-27 15:08:05 -07:00
Dylan Hunn
7652ec7b67 refactor(compiler): Allow host bindings to be ingested into the template pipeline. (#50899)
Alter the compiler code to ingest and process host bindings, using the newly updated compilation passes.

This is currently switched off in the outer compiler layer, but lays the foundation for actually generating the host binding functions using template pipeline.

PR Close #50899
2023-07-27 15:08:05 -07:00
Dylan Hunn
8f67c0751d refactor(compiler): Prepare the template pipeline to support host bindings. (#50899)
Refactor `compilation.ts` by introducing two new concepts:
1. A compilation unit, which has create and update ops. Compilations of individual views are compilation units, as are individual host bindings.
2. Aa compilation job, which has several compilation units. For example, a whole component is a compilation job, because it can have many view compilation units. A host binding compilation is a job in addition to a unit, because each host binding unit is always a singleton.

Then, we begin modifying phases to accept general compilation jobs instead of component compilations specifically, which will allow us to run them on host bindings. In particular, we update the following phases: `phaseReify`, and `phaseChaining`.

PR Close #50899
2023-07-27 15:08:04 -07:00
Kristiyan Kostadinov
7410d6847b refactor(compiler): add compiler flag to enable deferred blocks for testing (#51079)
Adds a new compiler option that will allow `defer` (and other) blocks to be enabled when writing unit tests.

PR Close #51079
2023-07-18 17:05:29 +00:00
Kristiyan Kostadinov
76d22ae752 refactor(compiler): add defer trigger parsing (#51050)
Adds the logic to parse the `when` and `on` triggers in a deferred block.

PR Close #51050
2023-07-17 21:05:47 +00:00
Kristiyan Kostadinov
9e61616ffe refactor(compiler): introduce deferred block AST (#51050)
Adds the logic to create `defer`-specific AST nodes from the generic HTML `BlockGroup` and `Block`. The logic for parsing the triggers will be in the next commit.

PR Close #51050
2023-07-17 21:05:47 +00:00
Kristiyan Kostadinov
b14a78ebb0 refactor(compiler): implement block syntax in html ast (#50953)
⚠️Disclaimer⚠️ this PR implements syntax that is still in an open RFC. It will be adjusted once the RFC is closed.

These changes implement the `BlockGroup` and `Block` AST nodes that will then be used to generate instructions based on the new syntax. A `BlockGroup` is a container for `Block` instances. The first block of a block is always implicit and required while any subsequent blocks are optional.

PR Close #50953
2023-07-13 09:32:53 -07:00
Payam Valadkhan
a15a56cb5d refactor(compiler): add a new interface for NgModule metadata to t rebase be used in local compilation mode (#50577)
The new interface is discrete-unioned with the existing interface to cover the cases for local and global (i.e., full and partial) compilation modes.

This change of interface required some adjustmeents cross repo which explains the changes made to other files.

PR Close #50577
2023-06-30 11:38:35 -07:00
Paul Gschwendtner
12bad6576d fix(compiler-cli): libraries compiled with v16.1+ breaking with Angular framework v16.0.x (#50714)
If a library is compiling with Angular v16.1.0, the library will break
for users that are still on Angular v16.0.x. This happens because the
`DirectiveDeclaration` or `ComponentDeclaration` types are not expecting
an extra field for `signals` metadata. This field was only added to the
generic types in `16.1.0`- so compilations fail with errors like this:

```
Error: node_modules/@angular/material/icon/index.d.ts:204:18 -
  error TS2707: Generic type 'ɵɵComponentDeclaration' requires between 7 and 9 type arguments.
```

To fix this, we quickly roll back the code for inserting this metadata
field. That way, libraries remain compatible with all v16.x framework
versions.

We continue to include the `signals` metadata if `signals: true` is set.
This is not public API anyway right now- so cannot happen- but imagine
we expose some signal APIs in e.g. 16.2.x, then we'd need this metadata
and can reasonably expect signal-component library users to use a more
recent framework core version.

PR Close #50714
2023-06-14 16:27:59 +02:00
Alex Rickabaugh
1928cd82d9 refactor(compiler): add ConstantPool to template pipeline compilations (#50118)
This commit adds the `ConstantPool` to `ComponentCompilation`, making it
available to all phases of the template pipeline. Constant extraction is a
common operation in pipeline phases.

PR Close #50118
2023-06-01 08:46:06 -07:00
Kristiyan Kostadinov
68017d4e75 feat(core): add ability to transform input values (#50420)
According to the HTML specification most attributes are defined as strings, however some can be interpreted as different types like booleans or numbers. [In the HTML standard](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes), boolean attributes are considered `true` if they are present on a DOM node and `false` if they are omitted. Common examples of boolean attributes are `disabled` on interactive elements like `<button>` or `checked` on `<input type="checkbox">`. Another example of an attribute that is defined as a string, but interpreted as a different type is the `value` attribute of `<input type="number">` which logs a warning and ignores the value if it can't be parsed as a number.

Historically, authoring Angular inputs that match the native behavior in a type-safe way has been difficult for developers, because Angular interprets all static attributes as strings. While some recent TypeScript versions made this easier by allowing setters and getters to have different types, supporting this pattern still requires a lot of boilerplate and additional properties to be declared. For example, currently developers have to write something like this to have a `disabled` input that behaves like the native one:

```typescript
import {Directive, Input} from '@angular/core';

@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
  @Input()
  get disabled() {
    return this._disabled;
  }
  set disabled(value: any) {
    this._disabled = typeof value === 'boolean' ? value : (value != null && value !== 'false');
  }
  private _disabled = false;
}
```

This feature aims to address the issue by introducing a `transform` property on inputs. If an input has a `transform` function, any values set through the template will be passed through the function before being assigned to the directive instance. The example from above can be rewritten to the following:

```typescript
import {Directive, Input, booleanAttribute} from '@angular/core';

@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
  @Input({transform: booleanAttribute}) disabled: boolean = false;
}
```

These changes also add the `booleanAttribute` and `numberAttribute` utilities to `@angular/core` since they're common enough to be useful for most projects.

Fixes #8968.
Fixes #14761.

PR Close #50420
2023-05-30 13:01:13 -07:00
Kristiyan Kostadinov
f6da091228 refactor(compiler): introduce compiler infrastructure for input transforms (#50225)
Adds the necessary compiler changes to support input transform functions. The compiler output has changed in the following ways:

### Directive handler
The directive handler now extracts a reference to the input transform function and it resolves the type of its first parameter. It also asserts that the type can be referenced in the compiled output and that it doesn't clash with any pre-existing `ngAcceptInputType_` members.

### .d.ts
In the generated declaration files the compiler now inserts an `ngAcceptInputType_` member for each input with a `transform` function. The member's type corresponds to the type of the first parameter of the function, e.g.

```typescript
// foo.directive.ts
@Directive()
export class Foo {
  @Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}

// foo.directive.d.ts
export class Foo {
  value: number;
  static ngAcceptInputType_value: string;
}
```

### Type check block
If an input has `transform` function, the TCB will use the type of its first parameter for the setter type. This uses the same infrastructure as the `ngAcceptInputType_` members.

### Directive declaration
The generated runtime directive declaration call now includes the `transform` function in the `inputs` map, if the input is being transformed. The function will be picked up by the runtime in the next commit to do the actual transformation.

```typescript
// foo.directive.ts
@Directive()
export class Foo {
  @Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}

// foo.directive.js
export class Foo {
  ɵdir = ɵɵdefineDirective({
    inputs: {
      value: ['value', 'value', incomingValue => parseInt(incomingValue)]
    }
  });
}
```

PR Close #50225
2023-05-22 14:48:02 +00:00
Matthieu Riegler
f755853b75 refactor(compiler): Remove unused TransformVisitor & NullVisitor (#48646)
NullVisitor & TransformVisitor are unused and unexported outside the compiler package, we can remove them.

PR Close #48646
2023-05-09 14:38:09 -07:00
Andrew Scott
5214df4958 refactor(compiler-cli): Add signals to internal directive metadata (#49981)
This commit adds the `signals: boolean` property to the internal
directive/component metadata. This does not add it to the public API
yet, as the feature has no internal support other than compiler
detection.

PR Close #49981
2023-04-25 15:39:18 -07:00
Alex Rickabaugh
0443cb4b38 refactor(compiler): reorder fields for template pipeline output (#49797)
The template pipeline was emitting fields on the component definition in a
different order than the `TemplateDefinitionBuilder`, which causes test
failures. Additionally, the `consts` field was being emitted even if it was
empty.

PR Close #49797
2023-04-18 17:00:50 +00:00