1 |
|
2 | import {ViewResources, resource, ViewCompileInstruction} from 'aurelia-templating';
|
3 | import {Loader} from 'aurelia-loader';
|
4 | import {Container} from 'aurelia-dependency-injection';
|
5 | import {relativeToFile} from 'aurelia-path';
|
6 | import {DOM, FEATURE} from 'aurelia-pal';
|
7 |
|
8 | let cssUrlMatcher = /url\((?!['"]data)([^)]+)\)/gi;
|
9 |
|
10 | function fixupCSSUrls(address: string, css: string) {
|
11 | if (typeof css !== 'string') {
|
12 | throw new Error(`Failed loading required CSS file: ${address}`);
|
13 | }
|
14 | return css.replace(cssUrlMatcher, (match, p1) => {
|
15 | let quote = p1.charAt(0);
|
16 | if (quote === '\'' || quote === '"') {
|
17 | p1 = p1.substr(1, p1.length - 2);
|
18 | }
|
19 | return 'url(\'' + relativeToFile(p1, address) + '\')';
|
20 | });
|
21 | }
|
22 |
|
23 | class CSSResource {
|
24 | |
25 |
|
26 |
|
27 | address: string;
|
28 | |
29 |
|
30 |
|
31 | _scoped: any;
|
32 | |
33 |
|
34 |
|
35 | _global: boolean;
|
36 | |
37 |
|
38 |
|
39 | _alreadyGloballyInjected: boolean;
|
40 |
|
41 | constructor(address: string) {
|
42 | this.address = address;
|
43 | this._scoped = null;
|
44 | this._global = false;
|
45 | this._alreadyGloballyInjected = false;
|
46 | }
|
47 |
|
48 | initialize(container: Container, Target: Function): void {
|
49 | this._scoped = new (Target as any)(this);
|
50 | }
|
51 |
|
52 | register(registry: ViewResources, name?: string): void {
|
53 | if (name === 'scoped') {
|
54 | registry.registerViewEngineHooks(this._scoped);
|
55 | } else {
|
56 | this._global = true;
|
57 | }
|
58 | }
|
59 |
|
60 | load(container: Container): Promise<CSSResource> {
|
61 | return container.get(Loader)
|
62 | .loadText(this.address)
|
63 | .catch(() => null)
|
64 | .then(text => {
|
65 | text = fixupCSSUrls(this.address, text);
|
66 | this._scoped.css = text;
|
67 | if (this._global) {
|
68 | this._alreadyGloballyInjected = true;
|
69 | DOM.injectStyles(text);
|
70 | }
|
71 | return this;
|
72 | });
|
73 | }
|
74 | }
|
75 |
|
76 | class CSSViewEngineHooks {
|
77 | owner: CSSResource;
|
78 | css: any;
|
79 | _global: boolean;
|
80 | constructor(owner: CSSResource) {
|
81 | this.owner = owner;
|
82 | this.css = null;
|
83 | }
|
84 |
|
85 | beforeCompile(content: DocumentFragment, resources: ViewResources, instruction: ViewCompileInstruction): void {
|
86 | if (instruction.targetShadowDOM) {
|
87 | DOM.injectStyles(this.css, content as any, true);
|
88 | } else if (FEATURE.scopedCSS) {
|
89 | let styleNode = DOM.injectStyles(this.css, content as any, true) as Element;
|
90 | styleNode.setAttribute('scoped', 'scoped');
|
91 | } else if (this._global && !this.owner._alreadyGloballyInjected) {
|
92 | DOM.injectStyles(this.css);
|
93 | this.owner._alreadyGloballyInjected = true;
|
94 | }
|
95 | }
|
96 | }
|
97 |
|
98 | export function _createCSSResource(address: string): Function {
|
99 | @resource(new CSSResource(address))
|
100 | class ViewCSS extends CSSViewEngineHooks {}
|
101 | return ViewCSS;
|
102 | }
|