2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-08-25 00:50:16 -07:00
|
|
|
import {AUTO_STYLE} from '@angular/core';
|
2016-10-06 15:10:27 -07:00
|
|
|
|
2016-10-19 13:42:39 -07:00
|
|
|
import {isPresent} from '../facade/lang';
|
2016-09-27 17:12:25 -07:00
|
|
|
import {AnimationKeyframe, AnimationStyles} from '../private_import_core';
|
2016-08-30 18:07:40 -07:00
|
|
|
|
feat(browser): use AppModules for bootstrap in the browser
This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:
```
@AppModule({
modules: [BrowserModule],
precompile: [MainComponent],
providers: […], // additional providers
directives: […], // additional platform directives
pipes: […] // additional platform pipes
})
class MyModule {
constructor(appRef: ApplicationRef) {
appRef.bootstrap(MainComponent);
}
}
// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);
// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```
The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.
Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
public so that the offline compiler can resolve the token
BREAKING CHANGES:
- short form bootstrap does no longer allow
to inject compiler internals (i.e. everything
from `@angular/compiler). Inject `Compiler` instead.
To provide custom providers for the compiler,
create a custom compiler via `browserCompiler({providers: [...]})`
and pass that into the `bootstrap` method.
2016-06-30 13:07:17 -07:00
|
|
|
import {AnimationDriver} from './animation_driver';
|
2016-06-08 22:50:52 -07:00
|
|
|
import {dashCaseToCamelCase} from './util';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {WebAnimationsPlayer} from './web_animations_player';
|
2016-05-25 12:46:22 -07:00
|
|
|
|
|
|
|
export class WebAnimationsDriver implements AnimationDriver {
|
2016-06-08 16:38:52 -07:00
|
|
|
animate(
|
|
|
|
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
2016-07-01 16:01:57 -07:00
|
|
|
duration: number, delay: number, easing: string): WebAnimationsPlayer {
|
2016-06-08 22:50:52 -07:00
|
|
|
var formattedSteps: {[key: string]: string | number}[] = [];
|
2016-06-08 16:38:52 -07:00
|
|
|
var startingStyleLookup: {[key: string]: string | number} = {};
|
2016-05-25 12:46:22 -07:00
|
|
|
if (isPresent(startingStyles) && startingStyles.styles.length > 0) {
|
2016-07-01 16:01:57 -07:00
|
|
|
startingStyleLookup = _populateStyles(element, startingStyles, {});
|
2016-05-25 12:46:22 -07:00
|
|
|
startingStyleLookup['offset'] = 0;
|
|
|
|
formattedSteps.push(startingStyleLookup);
|
|
|
|
}
|
|
|
|
|
|
|
|
keyframes.forEach((keyframe: AnimationKeyframe) => {
|
2016-07-01 16:01:57 -07:00
|
|
|
let data = _populateStyles(element, keyframe.styles, startingStyleLookup);
|
2016-06-08 22:50:52 -07:00
|
|
|
data['offset'] = keyframe.offset;
|
2016-05-25 12:46:22 -07:00
|
|
|
formattedSteps.push(data);
|
|
|
|
});
|
|
|
|
|
|
|
|
// this is a special case when only styles are applied as an
|
|
|
|
// animation. When this occurs we want to animate from start to
|
|
|
|
// end with the same values. Removing the offset and having only
|
|
|
|
// start/end values is suitable enough for the web-animations API
|
|
|
|
if (formattedSteps.length == 1) {
|
|
|
|
var start = formattedSteps[0];
|
2016-06-08 22:50:52 -07:00
|
|
|
start['offset'] = null;
|
2016-05-25 12:46:22 -07:00
|
|
|
formattedSteps = [start, start];
|
|
|
|
}
|
|
|
|
|
2016-07-02 02:00:46 -07:00
|
|
|
var playerOptions: {[key: string]: string | number} = {
|
2016-06-17 16:49:05 -07:00
|
|
|
'duration': duration,
|
|
|
|
'delay': delay,
|
|
|
|
'fill': 'both' // we use `both` because it allows for styling at 0% to work with `delay`
|
|
|
|
};
|
|
|
|
|
2016-07-02 02:00:46 -07:00
|
|
|
// we check for this to avoid having a null|undefined value be present
|
|
|
|
// for the easing (which results in an error for certain browsers #9752)
|
|
|
|
if (easing) {
|
|
|
|
playerOptions['easing'] = easing;
|
|
|
|
}
|
|
|
|
|
2016-07-01 16:01:57 -07:00
|
|
|
return new WebAnimationsPlayer(element, formattedSteps, playerOptions);
|
2016-06-08 22:50:52 -07:00
|
|
|
}
|
2016-05-25 12:46:22 -07:00
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
function _populateStyles(
|
2016-06-08 22:50:52 -07:00
|
|
|
element: any, styles: AnimationStyles,
|
|
|
|
defaultStyles: {[key: string]: string | number}): {[key: string]: string | number} {
|
|
|
|
var data: {[key: string]: string | number} = {};
|
2016-05-25 12:46:22 -07:00
|
|
|
styles.styles.forEach((entry) => {
|
2016-10-03 16:46:05 -07:00
|
|
|
Object.keys(entry).forEach(prop => {
|
|
|
|
const val = entry[prop];
|
2016-06-08 22:50:52 -07:00
|
|
|
var formattedProp = dashCaseToCamelCase(prop);
|
2016-07-01 16:01:57 -07:00
|
|
|
data[formattedProp] =
|
|
|
|
val == AUTO_STYLE ? val : val.toString() + _resolveStyleUnit(val, prop, formattedProp);
|
2016-05-25 12:46:22 -07:00
|
|
|
});
|
|
|
|
});
|
2016-10-03 16:46:05 -07:00
|
|
|
Object.keys(defaultStyles).forEach(prop => {
|
2016-06-08 22:50:52 -07:00
|
|
|
if (!isPresent(data[prop])) {
|
2016-10-03 16:46:05 -07:00
|
|
|
data[prop] = defaultStyles[prop];
|
2016-06-08 22:50:52 -07:00
|
|
|
}
|
|
|
|
});
|
2016-05-25 12:46:22 -07:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2016-06-08 22:50:52 -07:00
|
|
|
function _resolveStyleUnit(
|
|
|
|
val: string | number, userProvidedProp: string, formattedProp: string): string {
|
2016-05-25 12:46:22 -07:00
|
|
|
var unit = '';
|
2016-06-08 22:50:52 -07:00
|
|
|
if (_isPixelDimensionStyle(formattedProp) && val != 0 && val != '0') {
|
2016-10-19 13:42:39 -07:00
|
|
|
if (typeof val === 'number') {
|
2016-05-25 12:46:22 -07:00
|
|
|
unit = 'px';
|
|
|
|
} else if (_findDimensionalSuffix(val.toString()).length == 0) {
|
2016-08-25 00:50:16 -07:00
|
|
|
throw new Error('Please provide a CSS unit value for ' + userProvidedProp + ':' + val);
|
2016-05-25 12:46:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
const _$0 = 48;
|
|
|
|
const _$9 = 57;
|
|
|
|
const _$PERIOD = 46;
|
|
|
|
|
|
|
|
function _findDimensionalSuffix(value: string): string {
|
|
|
|
for (var i = 0; i < value.length; i++) {
|
2016-10-06 15:10:27 -07:00
|
|
|
var c = value.charCodeAt(i);
|
2016-05-25 12:46:22 -07:00
|
|
|
if ((c >= _$0 && c <= _$9) || c == _$PERIOD) continue;
|
|
|
|
return value.substring(i, value.length);
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
function _isPixelDimensionStyle(prop: string): boolean {
|
|
|
|
switch (prop) {
|
|
|
|
case 'width':
|
|
|
|
case 'height':
|
2016-06-08 22:50:52 -07:00
|
|
|
case 'minWidth':
|
|
|
|
case 'minHeight':
|
|
|
|
case 'maxWidth':
|
|
|
|
case 'maxHeight':
|
2016-05-25 12:46:22 -07:00
|
|
|
case 'left':
|
|
|
|
case 'top':
|
|
|
|
case 'bottom':
|
|
|
|
case 'right':
|
2016-06-08 22:50:52 -07:00
|
|
|
case 'fontSize':
|
|
|
|
case 'outlineWidth':
|
|
|
|
case 'outlineOffset':
|
|
|
|
case 'paddingTop':
|
|
|
|
case 'paddingLeft':
|
|
|
|
case 'paddingBottom':
|
|
|
|
case 'paddingRight':
|
|
|
|
case 'marginTop':
|
|
|
|
case 'marginLeft':
|
|
|
|
case 'marginBottom':
|
|
|
|
case 'marginRight':
|
|
|
|
case 'borderRadius':
|
|
|
|
case 'borderWidth':
|
|
|
|
case 'borderTopWidth':
|
|
|
|
case 'borderLeftWidth':
|
|
|
|
case 'borderRightWidth':
|
|
|
|
case 'borderBottomWidth':
|
|
|
|
case 'textIndent':
|
2016-05-25 12:46:22 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|