2015-09-02 18:07:31 -04:00
|
|
|
import {
|
|
|
|
ddescribe,
|
|
|
|
describe,
|
|
|
|
xdescribe,
|
|
|
|
it,
|
|
|
|
iit,
|
|
|
|
xit,
|
|
|
|
expect,
|
|
|
|
beforeEach,
|
|
|
|
afterEach,
|
|
|
|
AsyncTestCompleter,
|
2015-09-14 18:59:09 -04:00
|
|
|
inject,
|
|
|
|
beforeEachBindings
|
2015-10-13 03:29:13 -04:00
|
|
|
} from 'angular2/testing_internal';
|
2015-10-11 01:11:13 -04:00
|
|
|
import {provide} from 'angular2/src/core/di';
|
2015-11-05 17:07:57 -05:00
|
|
|
import {SpyXHR} from './spies';
|
|
|
|
import {XHR} from 'angular2/src/compiler/xhr';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
2015-09-02 18:07:31 -04:00
|
|
|
|
2015-11-02 11:39:14 -05:00
|
|
|
import {CONST_EXPR, isPresent, isBlank, StringWrapper, isArray} from 'angular2/src/facade/lang';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
2015-09-02 18:07:31 -04:00
|
|
|
import {evalModule} from './eval_module';
|
2015-11-05 17:07:57 -05:00
|
|
|
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
2015-09-14 18:59:09 -04:00
|
|
|
import {
|
2015-09-18 13:33:23 -04:00
|
|
|
CompileDirectiveMetadata,
|
|
|
|
CompileTemplateMetadata,
|
|
|
|
CompileTypeMetadata
|
2015-11-05 17:07:57 -05:00
|
|
|
} from 'angular2/src/compiler/directive_metadata';
|
|
|
|
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
|
2015-10-05 13:10:07 -04:00
|
|
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
2015-10-11 01:11:13 -04:00
|
|
|
import {TEST_PROVIDERS} from './test_bindings';
|
2015-11-05 17:07:57 -05:00
|
|
|
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
// Attention: These module names have to correspond to real modules!
|
2015-11-05 17:07:57 -05:00
|
|
|
var MODULE_URL = `package:angular2/test/compiler/style_compiler_spec${MODULE_SUFFIX}`;
|
|
|
|
var IMPORT_ABS_STYLESHEET_URL = `package:angular2/test/compiler/style_compiler_import.css`;
|
2015-10-01 13:07:49 -04:00
|
|
|
var IMPORT_REL_STYLESHEET_URL = './style_compiler_import.css';
|
2015-09-02 18:07:31 -04:00
|
|
|
// Note: Not a real module, only used via mocks.
|
2015-10-01 13:07:49 -04:00
|
|
|
var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
|
2015-11-05 17:07:57 -05:00
|
|
|
`package:angular2/test/compiler/style_compiler_transitive_import.css`;
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
export function main() {
|
|
|
|
describe('StyleCompiler', () => {
|
2015-09-14 18:59:09 -04:00
|
|
|
var xhr: SpyXHR;
|
2015-09-18 13:33:23 -04:00
|
|
|
|
2015-09-14 18:59:09 -04:00
|
|
|
beforeEachBindings(() => {
|
2015-09-02 18:07:31 -04:00
|
|
|
xhr = <any>new SpyXHR();
|
2015-10-12 14:30:34 -04:00
|
|
|
return [TEST_PROVIDERS, provide(XHR, {useValue: xhr})];
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
|
2015-09-14 18:59:09 -04:00
|
|
|
var compiler: StyleCompiler;
|
|
|
|
|
2015-11-02 11:39:14 -05:00
|
|
|
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
describe('compileComponentRuntime', () => {
|
2015-09-18 13:33:23 -04:00
|
|
|
var xhrUrlResults;
|
|
|
|
var xhrCount;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
xhrCount = 0;
|
|
|
|
xhrUrlResults = {};
|
2015-10-01 13:07:49 -04:00
|
|
|
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: blue}';
|
|
|
|
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT] =
|
|
|
|
`a {color: green}@import ${IMPORT_REL_STYLESHEET_URL};`;
|
2015-09-18 13:33:23 -04:00
|
|
|
});
|
|
|
|
|
2015-10-28 03:59:19 -04:00
|
|
|
function compile(styles: string[], styleAbsUrls: string[],
|
|
|
|
encapsulation: ViewEncapsulation): Promise<string[]> {
|
2015-09-18 13:33:23 -04:00
|
|
|
// Note: Can't use MockXHR as the xhr is called recursively,
|
|
|
|
// so we can't trigger flush.
|
|
|
|
xhr.spy('get').andCallFake((url) => {
|
|
|
|
var response = xhrUrlResults[url];
|
|
|
|
xhrCount++;
|
|
|
|
if (isBlank(response)) {
|
|
|
|
throw new BaseException(`Unexpected url ${url}`);
|
|
|
|
}
|
|
|
|
return PromiseWrapper.resolve(response);
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
2015-11-02 11:39:14 -05:00
|
|
|
return compiler.compileComponentRuntime(new CompileTemplateMetadata(
|
|
|
|
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
|
2015-09-02 18:07:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
describe('no shim', () => {
|
|
|
|
var encapsulation = ViewEncapsulation.None;
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
|
|
|
|
.then(styles => {
|
|
|
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
2015-11-02 11:39:14 -05:00
|
|
|
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
|
|
|
expect(styles)
|
2015-11-02 11:39:14 -05:00
|
|
|
.toEqual(['div {color: red}', ['a {color: green}', ['span {color: blue}']]]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('with shim', () => {
|
|
|
|
var encapsulation = ViewEncapsulation.Emulated;
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
|
|
|
|
.then(styles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(styles, [
|
2015-11-02 11:39:14 -05:00
|
|
|
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
|
|
|
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
|
2015-09-18 13:33:23 -04:00
|
|
|
]);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(styles, [
|
2015-11-02 11:39:14 -05:00
|
|
|
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
|
|
|
['span[_ngcontent-%COMP%] {color: blue}']
|
2015-09-18 13:33:23 -04:00
|
|
|
]);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT],
|
|
|
|
encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(styles, [
|
2015-11-02 11:39:14 -05:00
|
|
|
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
|
|
|
[
|
|
|
|
'a[_ngcontent-%COMP%] {color: green}',
|
|
|
|
['span[_ngcontent-%COMP%] {color: blue}']
|
|
|
|
]
|
2015-09-18 13:33:23 -04:00
|
|
|
]);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
2015-09-18 13:33:23 -04:00
|
|
|
|
|
|
|
it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
|
|
|
|
PromiseWrapper.all([
|
2015-10-01 13:07:49 -04:00
|
|
|
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None),
|
|
|
|
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
2015-09-18 13:33:23 -04:00
|
|
|
])
|
|
|
|
.then((styleArrays) => {
|
2015-11-02 11:39:14 -05:00
|
|
|
expect(styleArrays[0]).toEqual([['span {color: blue}']]);
|
|
|
|
expect(styleArrays[1]).toEqual([['span {color: blue}']]);
|
2015-09-18 13:33:23 -04:00
|
|
|
expect(xhrCount).toBe(1);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then((styles0) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
|
|
|
|
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then((styles1) => {
|
2015-11-02 11:39:14 -05:00
|
|
|
expect(styles0).toEqual([['span {color: blue}']]);
|
|
|
|
expect(styles1).toEqual([['span {color: blue}']]);
|
2015-09-18 13:33:23 -04:00
|
|
|
expect(xhrCount).toBe(1);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then((_) => {
|
|
|
|
compiler.clearCache();
|
2015-10-01 13:07:49 -04:00
|
|
|
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
|
|
|
|
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None);
|
2015-09-18 13:33:23 -04:00
|
|
|
})
|
|
|
|
.then((styles) => {
|
|
|
|
expect(xhrCount).toBe(2);
|
2015-11-02 11:39:14 -05:00
|
|
|
expect(styles).toEqual([['span {color: black}']]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('compileComponentCodeGen', () => {
|
2015-10-28 03:59:19 -04:00
|
|
|
function compile(styles: string[], styleAbsUrls: string[],
|
|
|
|
encapsulation: ViewEncapsulation): Promise<string[]> {
|
2015-11-02 11:39:14 -05:00
|
|
|
var sourceExpression = compiler.compileComponentCodeGen(new CompileTemplateMetadata(
|
|
|
|
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
|
2015-09-18 13:33:23 -04:00
|
|
|
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
|
|
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
|
|
|
};
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
describe('no shim', () => {
|
|
|
|
var encapsulation = ViewEncapsulation.None;
|
|
|
|
|
2015-09-28 13:30:33 -04:00
|
|
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
2015-09-18 13:33:23 -04:00
|
|
|
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
|
|
|
|
.then(styles => {
|
|
|
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
it('should compile css rules with newlines and quotes',
|
2015-09-18 13:33:23 -04:00
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(['div\n{"color": \'red\'}'], [], encapsulation)
|
|
|
|
.then(styles => {
|
|
|
|
expect(styles).toEqual(['div\n{"color": \'red\'}']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
2015-11-02 11:39:14 -05:00
|
|
|
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
2015-09-24 08:34:40 -04:00
|
|
|
}), 1000);
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('with shim', () => {
|
|
|
|
var encapsulation = ViewEncapsulation.Emulated;
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
|
|
|
|
.then(styles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(styles, [
|
2015-11-02 11:39:14 -05:00
|
|
|
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
|
|
|
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
|
2015-09-18 13:33:23 -04:00
|
|
|
]);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(styles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(styles, [
|
2015-11-02 11:39:14 -05:00
|
|
|
'div[_ngcontent-%COMP%] {color: red}',
|
|
|
|
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
|
2015-09-18 13:33:23 -04:00
|
|
|
]);
|
|
|
|
async.done();
|
|
|
|
});
|
2015-09-24 08:34:40 -04:00
|
|
|
}), 1000);
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('compileStylesheetCodeGen', () => {
|
2015-09-24 08:34:40 -04:00
|
|
|
function compile(style: string): Promise<string[][]> {
|
2015-10-01 13:07:49 -04:00
|
|
|
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_URL, style);
|
2015-09-18 13:33:23 -04:00
|
|
|
return PromiseWrapper.all(sourceModules.map(sourceModule => {
|
|
|
|
var sourceWithImports = testableModule(sourceModule).getSourceWithImports();
|
|
|
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
}
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile('div {color: red;}')
|
|
|
|
.then(stylesAndShimStyles => {
|
2015-10-29 13:57:54 -04:00
|
|
|
var expected = [['div {color: red;}'], ['div[_ngcontent-%COMP%] {color: red;}']];
|
2015-09-24 08:34:40 -04:00
|
|
|
compareStyles(stylesAndShimStyles[0], expected[0]);
|
|
|
|
compareStyles(stylesAndShimStyles[1], expected[1]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
|
|
|
|
it('should allow to import rules with relative paths',
|
2015-09-18 13:33:23 -04:00
|
|
|
inject([AsyncTestCompleter], (async) => {
|
2015-10-01 13:07:49 -04:00
|
|
|
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
|
2015-09-18 13:33:23 -04:00
|
|
|
.then(stylesAndShimStyles => {
|
2015-09-24 08:34:40 -04:00
|
|
|
var expected = [
|
2015-11-02 11:39:14 -05:00
|
|
|
['div {color: red}', ['span {color: blue}']],
|
2015-09-24 08:34:40 -04:00
|
|
|
[
|
2015-10-29 13:57:54 -04:00
|
|
|
'div[_ngcontent-%COMP%] {color: red}',
|
2015-11-02 11:39:14 -05:00
|
|
|
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
|
2015-09-24 08:34:40 -04:00
|
|
|
]
|
|
|
|
];
|
|
|
|
compareStyles(stylesAndShimStyles[0], expected[0]);
|
|
|
|
compareStyles(stylesAndShimStyles[1], expected[1]);
|
2015-09-18 13:33:23 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
2015-09-02 18:07:31 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-09-14 18:59:09 -04:00
|
|
|
|
|
|
|
function testableExpression(source: SourceExpression): SourceModule {
|
|
|
|
var testableSource = `${source.declarations.join('\n')}
|
2015-11-02 11:39:14 -05:00
|
|
|
${codeGenValueFn(['_'], source.expression, '_run')};
|
|
|
|
${codeGenExportVariable('run')}_run;`;
|
2015-09-14 18:59:09 -04:00
|
|
|
return new SourceModule(null, testableSource);
|
|
|
|
}
|
|
|
|
|
|
|
|
function testableModule(sourceModule: SourceModule): SourceModule {
|
2015-09-17 12:58:18 -04:00
|
|
|
var testableSource = `${sourceModule.sourceWithModuleRefs}
|
2015-11-02 11:39:14 -05:00
|
|
|
${codeGenValueFn(['_'], 'STYLES', '_run')};
|
|
|
|
${codeGenExportVariable('run')}_run;`;
|
2015-10-01 13:07:49 -04:00
|
|
|
return new SourceModule(sourceModule.moduleUrl, testableSource);
|
2015-09-02 18:07:31 -04:00
|
|
|
}
|
2015-09-08 11:36:59 -04:00
|
|
|
|
|
|
|
// Needed for Android browsers which add an extra space at the end of some lines
|
2015-11-02 11:39:14 -05:00
|
|
|
function compareStyles(styles: Array<string | any[]>, expectedStyles: Array<string | any[]>) {
|
2015-09-08 11:36:59 -04:00
|
|
|
expect(styles.length).toEqual(expectedStyles.length);
|
|
|
|
for (var i = 0; i < styles.length; i++) {
|
2015-11-02 11:39:14 -05:00
|
|
|
var style = styles[i];
|
|
|
|
if (isArray(style)) {
|
|
|
|
compareStyles(<any[]>style, <any[]>expectedStyles[i]);
|
|
|
|
} else {
|
|
|
|
expect(StringWrapper.replaceAll(<string>style, /\s+\n/g, '\n')).toEqual(expectedStyles[i]);
|
|
|
|
}
|
2015-09-08 11:36:59 -04:00
|
|
|
}
|
|
|
|
}
|