feature(ShadowDomTransformer): create a compiler step to transform the shadow DOM
This commit is contained in:
		
							parent
							
								
									7bf5ab8f43
								
							
						
					
					
						commit
						47042bc503
					
				| @ -120,7 +120,6 @@ export class Compiler { | ||||
|   } | ||||
| 
 | ||||
|   _compileTemplate(template: Element, cmpMetadata): Promise<ProtoView> { | ||||
|     this._shadowDomStrategy.processTemplate(template, cmpMetadata); | ||||
|     var pipeline = new CompilePipeline(this.createSteps(cmpMetadata)); | ||||
|     var compileElements = pipeline.process(template); | ||||
|     var protoView = compileElements[0].inheritedProtoView; | ||||
|  | ||||
| @ -39,6 +39,8 @@ export class CompileElement { | ||||
|   inheritedElementBinder:ElementBinder; | ||||
|   distanceToParentInjector:number; | ||||
|   compileChildren: boolean; | ||||
|   ignoreBindings: boolean; | ||||
| 
 | ||||
|   constructor(element:Element) { | ||||
|     this.element = element; | ||||
|     this._attrs = null; | ||||
| @ -64,6 +66,8 @@ export class CompileElement { | ||||
|     this.inheritedElementBinder = null; | ||||
|     this.distanceToParentInjector = 0; | ||||
|     this.compileChildren = true; | ||||
|     // set to true to ignore all the bindings on the element
 | ||||
|     this.ignoreBindings = false; | ||||
|   } | ||||
| 
 | ||||
|   refreshAttrs() { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import {ChangeDetection, Parser} from 'angular2/change_detection'; | ||||
| import {List} from 'angular2/src/facade/collection'; | ||||
| import {List, ListWrapper} from 'angular2/src/facade/collection'; | ||||
| 
 | ||||
| import {PropertyBindingParser} from './property_binding_parser'; | ||||
| import {TextInterpolationParser} from './text_interpolation_parser'; | ||||
| @ -9,9 +9,11 @@ import {ElementBindingMarker} from './element_binding_marker'; | ||||
| import {ProtoViewBuilder} from './proto_view_builder'; | ||||
| import {ProtoElementInjectorBuilder} from './proto_element_injector_builder'; | ||||
| import {ElementBinderBuilder} from './element_binder_builder'; | ||||
| import {ShadowDomTransformer} from './shadow_dom_transformer'; | ||||
| import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; | ||||
| import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; | ||||
| import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; | ||||
| import {stringify} from 'angular2/src/facade/lang'; | ||||
| import {DOM} from 'angular2/src/facade/dom'; | ||||
| 
 | ||||
| /** | ||||
|  * Default steps used for compiling a template. | ||||
| @ -27,8 +29,14 @@ export function createDefaultSteps( | ||||
| 
 | ||||
|   var compilationUnit = stringify(compiledComponent.type); | ||||
| 
 | ||||
|   return [ | ||||
|     new ViewSplitter(parser, compilationUnit), | ||||
|   var steps = [new ViewSplitter(parser, compilationUnit)]; | ||||
| 
 | ||||
|   if (!(shadowDomStrategy instanceof NativeShadowDomStrategy)) { | ||||
|     var step = new ShadowDomTransformer(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head); | ||||
|     ListWrapper.push(steps, step); | ||||
|   } | ||||
| 
 | ||||
|   steps = ListWrapper.concat(steps,[ | ||||
|     new PropertyBindingParser(parser, compilationUnit), | ||||
|     new DirectiveParser(directives), | ||||
|     new TextInterpolationParser(parser, compilationUnit), | ||||
| @ -36,5 +44,7 @@ export function createDefaultSteps( | ||||
|     new ProtoViewBuilder(changeDetection, shadowDomStrategy), | ||||
|     new ProtoElementInjectorBuilder(), | ||||
|     new ElementBinderBuilder(parser, compilationUnit) | ||||
|   ]; | ||||
|   ]); | ||||
| 
 | ||||
|   return steps; | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,10 @@ const NG_BINDING_CLASS = 'ng-binding'; | ||||
|  */ | ||||
| export class ElementBindingMarker extends CompileStep { | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (current.ignoreBindings) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     var hasBindings = | ||||
|       (isPresent(current.textNodeBindings) && MapWrapper.size(current.textNodeBindings)>0) || | ||||
|       (isPresent(current.propertyBindings) && MapWrapper.size(current.propertyBindings)>0) || | ||||
|  | ||||
| @ -38,6 +38,10 @@ export class PropertyBindingParser extends CompileStep { | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (current.ignoreBindings) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     var attrs = current.attrs(); | ||||
|     MapWrapper.forEach(attrs, (attrValue, attrName) => { | ||||
|       var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName); | ||||
|  | ||||
							
								
								
									
										79
									
								
								modules/angular2/src/core/compiler/pipeline/shadow_dom_transformer.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								modules/angular2/src/core/compiler/pipeline/shadow_dom_transformer.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| import {CompileStep} from './compile_step'; | ||||
| import {CompileElement} from './compile_element'; | ||||
| import {CompileControl} from './compile_control'; | ||||
| 
 | ||||
| import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; | ||||
| import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; | ||||
| import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css'; | ||||
| 
 | ||||
| import {DOM, Element} from 'angular2/src/facade/dom'; | ||||
| import {isPresent, isBlank} from 'angular2/src/facade/lang'; | ||||
| import {StringMapWrapper} from 'angular2/src/facade/collection'; | ||||
| 
 | ||||
| var _cssCache = StringMapWrapper.create(); | ||||
| 
 | ||||
| export class ShadowDomTransformer extends CompileStep { | ||||
|   _selector: string; | ||||
|   _strategy: ShadowDomStrategy; | ||||
|   _styleHost: Element; | ||||
|   _lastInsertedStyle: Element; | ||||
| 
 | ||||
|   constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy, styleHost: Element) { | ||||
|     super(); | ||||
|     this._strategy = strategy; | ||||
|     this._selector = cmpMetadata.annotation.selector; | ||||
|     this._styleHost = styleHost; | ||||
|     this._lastInsertedStyle = null; | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     // May be remove the styles
 | ||||
|     if (DOM.tagName(current.element) == 'STYLE') { | ||||
|       current.ignoreBindings = true; | ||||
|       if (this._strategy.extractStyles()) { | ||||
|         DOM.remove(current.element); | ||||
|         var css = DOM.getText(current.element); | ||||
|         if (this._strategy.shim()) { | ||||
|           // The css generated here is unique for the component (because of the shim).
 | ||||
|           // Then we do not need to cache it.
 | ||||
|           css = shimCssText(css, this._selector); | ||||
|           this._insertStyle(this._styleHost, css); | ||||
|         } else { | ||||
|           var seen = isPresent(StringMapWrapper.get(_cssCache, css)); | ||||
|           if (!seen) { | ||||
|             StringMapWrapper.set(_cssCache, css, true); | ||||
|             this._insertStyle(this._styleHost, css); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       if (this._strategy.shim()) { | ||||
|         try { | ||||
|           DOM.setAttribute(current.element, this._selector, ''); | ||||
|         } catch(e) { | ||||
|           // TODO(vicb): for now only simple selector (tag name) are supported
 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   clearCache() { | ||||
|     _cssCache = StringMapWrapper.create(); | ||||
|   } | ||||
| 
 | ||||
|   _insertStyle(el: Element, css: string) { | ||||
|     var style = DOM.createStyleElement(css); | ||||
|     if (isBlank(this._lastInsertedStyle)) { | ||||
|       var firstChild = DOM.firstChild(el); | ||||
|       if (isPresent(firstChild)) { | ||||
|         DOM.insertBefore(firstChild, style); | ||||
|       } else { | ||||
|         DOM.appendChild(el, style); | ||||
|       } | ||||
|     } else { | ||||
|       DOM.insertAfter(this._lastInsertedStyle, style); | ||||
|     } | ||||
|     this._lastInsertedStyle = style; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -23,7 +23,7 @@ export class TextInterpolationParser extends CompileStep { | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (!current.compileChildren) { | ||||
|     if (!current.compileChildren || current.ignoreBindings) { | ||||
|       return; | ||||
|     } | ||||
|     var element = current.element; | ||||
|  | ||||
| @ -1,28 +1,22 @@ | ||||
| import {Type, isBlank, isPresent} from 'angular2/src/facade/lang'; | ||||
| import {DOM, Element, StyleElement} from 'angular2/src/facade/dom'; | ||||
| import {DOM, Element} from 'angular2/src/facade/dom'; | ||||
| import {List, ListWrapper} from 'angular2/src/facade/collection'; | ||||
| import {View} from './view'; | ||||
| import {Content} from './shadow_dom_emulation/content_tag'; | ||||
| import {LightDom} from './shadow_dom_emulation/light_dom'; | ||||
| import {DirectiveMetadata} from './directive_metadata'; | ||||
| import {shimCssText} from './shadow_dom_emulation/shim_css'; | ||||
| 
 | ||||
| export class ShadowDomStrategy { | ||||
|   attachTemplate(el:Element, view:View){} | ||||
|   constructLightDom(lightDomView:View, shadowDomView:View, el:Element){} | ||||
|   polyfillDirectives():List<Type>{ return null; } | ||||
|   processTemplate(template: Element, cmpMetadata: DirectiveMetadata) { return null; } | ||||
|   shim(): boolean { return false; } | ||||
|   extractStyles(): boolean { return false; } | ||||
| } | ||||
| 
 | ||||
| export class EmulatedShadowDomStrategy extends ShadowDomStrategy { | ||||
|   _styleHost: Element; | ||||
| 
 | ||||
|   constructor(styleHost: Element = null) { | ||||
|   constructor() { | ||||
|     super(); | ||||
|     if (isBlank(styleHost)) { | ||||
|       styleHost = DOM.defaultDoc().head; | ||||
|     } | ||||
|     this._styleHost = styleHost; | ||||
|   } | ||||
| 
 | ||||
|   attachTemplate(el:Element, view:View){ | ||||
| @ -38,25 +32,20 @@ export class EmulatedShadowDomStrategy extends ShadowDomStrategy { | ||||
|     return [Content]; | ||||
|   } | ||||
| 
 | ||||
|   processTemplate(template: Element, cmpMetadata: DirectiveMetadata) { | ||||
|     var templateRoot = DOM.templateAwareRoot(template); | ||||
|     var attrName = cmpMetadata.annotation.selector; | ||||
| 
 | ||||
|     // Shim CSS for emulated shadow DOM and attach the styles do the document head
 | ||||
|     var styles = _detachStyles(templateRoot); | ||||
|     for (var i = 0; i < styles.length; i++) { | ||||
|       var style = styles[i]; | ||||
|       var processedCss = shimCssText(DOM.getText(style), attrName); | ||||
|       DOM.setText(style, processedCss); | ||||
|   shim(): boolean { | ||||
|     return true; | ||||
|   } | ||||
|     _attachStyles(this._styleHost, styles); | ||||
| 
 | ||||
|     // Update the DOM to trigger the CSS
 | ||||
|     _addAttributeToChildren(templateRoot, attrName); | ||||
|   extractStyles(): boolean { | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export class NativeShadowDomStrategy extends ShadowDomStrategy { | ||||
|   constructor() { | ||||
|     super(); | ||||
|   } | ||||
| 
 | ||||
|   attachTemplate(el:Element, view:View){ | ||||
|     moveViewNodesIntoParent(el.createShadowRoot(), view); | ||||
|   } | ||||
| @ -69,8 +58,12 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy { | ||||
|     return []; | ||||
|   } | ||||
| 
 | ||||
|   processTemplate(template: Element, cmpMetadata: DirectiveMetadata) { | ||||
|     return template; | ||||
|   shim(): boolean { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   extractStyles(): boolean { | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -79,38 +72,3 @@ function moveViewNodesIntoParent(parent, view) { | ||||
|     DOM.appendChild(parent, view.nodes[i]); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // TODO(vicb): union types: el is an Element or a Document Fragment
 | ||||
| function _detachStyles(el): List<StyleElement> { | ||||
|   var nodeList = DOM.querySelectorAll(el, 'style'); | ||||
|   var styles = []; | ||||
|   for (var i = 0; i < nodeList.length; i++) { | ||||
|     var style = DOM.remove(nodeList[i]); | ||||
|     ListWrapper.push(styles, style); | ||||
|   } | ||||
|   return styles; | ||||
| } | ||||
| 
 | ||||
| // Move the styles as the first children of the template
 | ||||
| function _attachStyles(el: Element, styles: List<StyleElement>) { | ||||
|   var firstChild = DOM.firstChild(el); | ||||
|   for (var i = styles.length - 1; i >= 0; i--) { | ||||
|     var style = styles[i]; | ||||
|     if (isPresent(firstChild)) { | ||||
|       DOM.insertBefore(firstChild, style); | ||||
|     } else { | ||||
|       DOM.appendChild(el, style); | ||||
|     } | ||||
|     firstChild = style; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // TODO(vicb): union types: el is an Element or a Document Fragment
 | ||||
| function _addAttributeToChildren(el, attrName:string) { | ||||
|   // TODO(vicb): currently the code crashes when the attrName is not an el selector
 | ||||
|   var children = DOM.querySelectorAll(el, "*"); | ||||
|   for (var i = 0; i < children.length; i++) { | ||||
|     var child = children[i]; | ||||
|     DOM.setAttribute(child, attrName, ''); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -103,6 +103,12 @@ class DOM { | ||||
|     el.setAttribute(attrName, attrValue); | ||||
|     return el; | ||||
|   } | ||||
|   static StyleElement createStyleElement(String css, [HtmlDocument doc = null]) { | ||||
|     if (doc == null) doc = document; | ||||
|     var el = doc.createElement("STYLE"); | ||||
|     el.text = css; | ||||
|     return el; | ||||
|   } | ||||
|   static clone(Node node) => node.clone(true); | ||||
|   static bool hasProperty(Element element, String name) => | ||||
|       new JsObject.fromBrowserObject(element).hasProperty(name); | ||||
|  | ||||
| @ -70,23 +70,6 @@ export function main() { | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('should use the shadow dom strategy to process the template', (done) => { | ||||
|       // TODO(vicb) test in Dart when the bug is fixed
 | ||||
|       // https://code.google.com/p/dart/issues/detail?id=18249
 | ||||
|       if (IS_DARTIUM) { | ||||
|         done(); | ||||
|         return; | ||||
|       } | ||||
|       var templateHtml = 'processed template'; | ||||
|       var compiler = createCompiler((parent, current, control) => { | ||||
|         current.inheritedProtoView = new ProtoView(current.element, null, null); | ||||
|       }, new FakeShadowDomStrategy(templateHtml)); | ||||
|       compiler.compile(MainComponent, null).then( (protoView) => { | ||||
|         expect(DOM.getInnerHTML(protoView.element)).toEqual('processed template'); | ||||
|         done(); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('should load nested components', (done) => { | ||||
|       var mainEl = el('<div></div>'); | ||||
|       var compiler = createCompiler( (parent, current, control) => { | ||||
| @ -244,15 +227,3 @@ class MockStep extends CompileStep { | ||||
|     this.processClosure(parent, current, control); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class FakeShadowDomStrategy extends NativeShadowDomStrategy { | ||||
|   templateHtml: string; | ||||
|   constructor(templateHtml: string) { | ||||
|     super(); | ||||
|     this.templateHtml = templateHtml; | ||||
|   } | ||||
| 
 | ||||
|   processTemplate(template: Element, cmpMetadata: DirectiveMetadata) { | ||||
|     DOM.setInnerHTML(template, this.templateHtml); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -14,7 +14,8 @@ import {Template, Decorator, Component} from 'angular2/src/core/annotations/anno | ||||
| export function main() { | ||||
|   describe('ElementBindingMarker', () => { | ||||
| 
 | ||||
|     function createPipeline({textNodeBindings, propertyBindings, variableBindings, eventBindings, directives}={}) { | ||||
|     function createPipeline({textNodeBindings, propertyBindings, variableBindings, eventBindings, | ||||
|       directives, ignoreBindings}={}) { | ||||
|       var reader = new DirectiveMetadataReader(); | ||||
|       return new CompilePipeline([ | ||||
|         new MockStep((parent, current, control) => { | ||||
| @ -30,6 +31,9 @@ export function main() { | ||||
|             if (isPresent(eventBindings)) { | ||||
|               current.eventBindings = eventBindings; | ||||
|             } | ||||
|             if (isPresent(ignoreBindings)) { | ||||
|               current.ignoreBindings = ignoreBindings; | ||||
|             } | ||||
|             if (isPresent(directives)) { | ||||
|               for (var i=0; i<directives.length; i++) { | ||||
|                 current.addDirective(reader.read(directives[i])); | ||||
| @ -44,6 +48,14 @@ export function main() { | ||||
|       assertBinding(results[0], false); | ||||
|     }); | ||||
| 
 | ||||
|     it('should not mark elements when ignoreBindings is true', () => { | ||||
|       var textNodeBindings = MapWrapper.create(); | ||||
|       MapWrapper.set(textNodeBindings, 0, 'expr'); | ||||
|       var results = createPipeline({textNodeBindings: textNodeBindings, | ||||
|         ignoreBindings: true}).process(el('<div></div>')); | ||||
|       assertBinding(results[0], false); | ||||
|     }); | ||||
| 
 | ||||
|     it('should mark elements with text node bindings', () => { | ||||
|       var textNodeBindings = MapWrapper.create(); | ||||
|       MapWrapper.set(textNodeBindings, 0, 'expr'); | ||||
|  | ||||
| @ -3,15 +3,24 @@ import {PropertyBindingParser} from 'angular2/src/core/compiler/pipeline/propert | ||||
| import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline'; | ||||
| import {DOM} from 'angular2/src/facade/dom'; | ||||
| import {MapWrapper} from 'angular2/src/facade/collection'; | ||||
| 
 | ||||
| import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element'; | ||||
| import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step' | ||||
| import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control'; | ||||
| import {Lexer, Parser} from 'angular2/change_detection'; | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('PropertyBindingParser', () => { | ||||
|     function createPipeline() { | ||||
|       return new CompilePipeline([new PropertyBindingParser(new Parser(new Lexer()), null)]); | ||||
|     function createPipeline(ignoreBindings = false) { | ||||
|       return new CompilePipeline([ | ||||
|         new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }), | ||||
|         new PropertyBindingParser(new Parser(new Lexer()), null)]); | ||||
|     } | ||||
| 
 | ||||
|     it('should not parse bindings when ignoreBindings is true', () => { | ||||
|       var results = createPipeline(true).process(el('<div [a]="b"></div>')); | ||||
|       expect(results[0].propertyBindings).toBe(null); | ||||
|     }); | ||||
| 
 | ||||
|     it('should detect [] syntax', () => { | ||||
|       var results = createPipeline().process(el('<div [a]="b"></div>')); | ||||
|       expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b'); | ||||
| @ -69,3 +78,14 @@ export function main() { | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| class MockStep extends CompileStep { | ||||
|   processClosure:Function; | ||||
|   constructor(process) { | ||||
|     super(); | ||||
|     this.processClosure = process; | ||||
|   } | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     this.processClosure(parent, current, control); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										128
									
								
								modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | ||||
| import {describe, beforeEach, expect, it, iit, ddescribe, el} from 'angular2/test_lib'; | ||||
| 
 | ||||
| import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline'; | ||||
| import {ShadowDomTransformer} from 'angular2/src/core/compiler/pipeline/shadow_dom_transformer'; | ||||
| import {Component} from 'angular2/src/core/annotations/annotations'; | ||||
| import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; | ||||
| import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; | ||||
| import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css'; | ||||
| 
 | ||||
| import {DOM} from 'angular2/src/facade/dom'; | ||||
| import {MapWrapper} from 'angular2/src/facade/collection'; | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('ShadowDomTransformer', () => { | ||||
|     function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) { | ||||
|       var component = new Component({selector: selector}); | ||||
|       var meta = new DirectiveMetadata(null, component, null); | ||||
|       var transformer = new ShadowDomTransformer(meta, strategy, styleHost); | ||||
|       transformer.clearCache(); | ||||
|       return new CompilePipeline([transformer]); | ||||
|     } | ||||
| 
 | ||||
|     it('it should set ignoreBindings to true for style elements', () => { | ||||
|       var host = DOM.createElement('div'); | ||||
|       var pipeline = createPipeline('foo', new FakeStrategy(false, false), host); | ||||
|       var results = pipeline.process(el('<div><style></style></div>')); | ||||
|       expect(results[0].ignoreBindings).toBe(false); | ||||
|       expect(results[1].ignoreBindings).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     describe('css', () => { | ||||
|       it('should not extract the styles when extractStyles() is false', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(false, false), host); | ||||
|         var template = el('<style>.s{}</style>'); | ||||
|         pipeline.process(template); | ||||
|         expect(template).toHaveText('.s{}'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should move the styles to the host when extractStyles() is true', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(true, false), host); | ||||
|         var template = el('<div><style>.s{}</style></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(template).toHaveText(''); | ||||
|         expect(host).toHaveText('.s{}'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should preserve original content when moving styles', () => { | ||||
|         var host = el('<div>original content</div>'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(true, false), host); | ||||
|         var template = el('<div><style>.s{}</style></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(template).toHaveText(''); | ||||
|         expect(host).toHaveText('.s{}original content'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should move the styles to the host in the original order', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(true, false), host); | ||||
|         var template = el('<div><style>.s1{}</style><style>.s2{}</style></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(host).toHaveText('.s1{}.s2{}'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should shim the styles when shim() and extractStyles() are true', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(true, true), host); | ||||
|         var template = el('<div><style>.s1{}</style></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(host).toHaveText(shimCssText('.s1{}', 'foo')); | ||||
|       }); | ||||
| 
 | ||||
|       it('should deduplicate styles before moving them when shim() is false', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(true, false), host); | ||||
|         var template = el('<div><style>.s1{}</style><style>.s1{}</style><style>.s1{}</style></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(host).toHaveText('.s1{}'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('html', () => { | ||||
|       it('should add an attribute to all children when shim() is true', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(false, true), host); | ||||
|         var template = el('<div><span></span></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(DOM.getOuterHTML(template)).toEqual('<div foo=""><span foo=""></span></div>') | ||||
|       }); | ||||
| 
 | ||||
|       it('should not modify the template when shim() is false', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo', new FakeStrategy(false, false), host); | ||||
|         var template = el('<div><span></span></div>'); | ||||
|         pipeline.process(template); | ||||
|         expect(DOM.getOuterHTML(template)).toEqual('<div><span></span></div>') | ||||
|       }); | ||||
| 
 | ||||
|       it('should not throw with complex selectors', () => { | ||||
|         var host = DOM.createElement('div'); | ||||
|         var pipeline = createPipeline('foo[bar]', new FakeStrategy(false, true), host); | ||||
|         var template = el('<div><span></span></div>'); | ||||
|         expect(() => pipeline.process(template)).not.toThrow(); | ||||
|       }); | ||||
| 
 | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| class FakeStrategy extends ShadowDomStrategy { | ||||
|   _extractStyles: boolean; | ||||
|   _shim: boolean; | ||||
| 
 | ||||
|   constructor(extractStyles: boolean, shim: boolean) { | ||||
|     super(); | ||||
|     this._extractStyles = extractStyles; | ||||
|     this._shim = shim; | ||||
|   } | ||||
| 
 | ||||
|   extractStyles(): boolean { | ||||
|     return this._extractStyles; | ||||
|   } | ||||
| 
 | ||||
|   shim(): boolean { | ||||
|     return this._shim; | ||||
|   } | ||||
| } | ||||
| @ -3,19 +3,27 @@ import {TextInterpolationParser} from 'angular2/src/core/compiler/pipeline/text_ | ||||
| import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline'; | ||||
| import {DOM} from 'angular2/src/facade/dom'; | ||||
| import {MapWrapper} from 'angular2/src/facade/collection'; | ||||
| 
 | ||||
| import {Lexer, Parser} from 'angular2/change_detection'; | ||||
| import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element'; | ||||
| import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step' | ||||
| import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control'; | ||||
| import {IgnoreChildrenStep} from './pipeline_spec'; | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('TextInterpolationParser', () => { | ||||
|     function createPipeline() { | ||||
|     function createPipeline(ignoreBindings = false) { | ||||
|       return new CompilePipeline([ | ||||
|         new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }), | ||||
|         new IgnoreChildrenStep(), | ||||
|         new TextInterpolationParser(new Parser(new Lexer()), null) | ||||
|       ]); | ||||
|     } | ||||
| 
 | ||||
|     it('should not look for text interpolation when ignoreBindings is true', () => { | ||||
|       var results = createPipeline(true).process(el('<div>{{expr1}}<span></span>{{expr2}}</div>')); | ||||
|       expect(results[0].textNodeBindings).toBe(null); | ||||
|     }); | ||||
| 
 | ||||
|     it('should find text interpolation in normal elements', () => { | ||||
|       var results = createPipeline().process(el('<div>{{expr1}}<span></span>{{expr2}}</div>')); | ||||
|       var bindings = results[0].textNodeBindings; | ||||
| @ -55,3 +63,14 @@ export function main() { | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| class MockStep extends CompileStep { | ||||
|   processClosure:Function; | ||||
|   constructor(process) { | ||||
|     super(); | ||||
|     this.processClosure = process; | ||||
|   } | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     this.processClosure(parent, current, control); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,62 +0,0 @@ | ||||
| import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib'; | ||||
| import {NativeShadowDomStrategy, EmulatedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; | ||||
| import {DOM} from 'angular2/src/facade/dom'; | ||||
| import {Component} from 'angular2/src/core/annotations/annotations'; | ||||
| import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('Shadow DOM strategy', () => { | ||||
|     var strategy, | ||||
|         component = new Component({selector: 'mycmp'}), | ||||
|         metadata = new DirectiveMetadata(null, component, null); | ||||
| 
 | ||||
|     describe('Native', () => { | ||||
|       beforeEach(() => { | ||||
|         strategy = new NativeShadowDomStrategy(); | ||||
|       }); | ||||
| 
 | ||||
|       it('should leave the styles in the template', () => { | ||||
|         var tpl = DOM.createTemplate('<style>.s1{}</style><div>content</div>'); | ||||
|         strategy.processTemplate(tpl, metadata); | ||||
|         expect(tpl.content).toHaveText('.s1{}content'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should not modify the content of the template', () => { | ||||
|         var html = '<p>content<span></span></p>'; | ||||
|         var tpl = DOM.createTemplate(html); | ||||
|         strategy.processTemplate(tpl, metadata); | ||||
|         expect(DOM.getInnerHTML(tpl)).toEqual(html); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('Emulated', () => { | ||||
|       var root; | ||||
|       beforeEach(() => { | ||||
|         root = el('<div>'); | ||||
|         strategy = new EmulatedShadowDomStrategy(root); | ||||
|       }); | ||||
| 
 | ||||
|       it('should move the styles from the template to the root', () => { | ||||
|         var tpl = DOM.createTemplate('<style>.s1{}</style><div>content</div><style>.s2{}</style>'); | ||||
|         strategy.processTemplate(tpl, metadata); | ||||
|         expect(root).toHaveText('.s1[mycmp] {}.s2[mycmp] {}'); | ||||
|         expect(tpl.content).toHaveText('content'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should insert the styles as the first children of the host', () => { | ||||
|         DOM.setInnerHTML(root, '<p>root content</p>') | ||||
|         var tpl = DOM.createTemplate('<style>.s1{}</style><style>.s2{}</style>'); | ||||
|         strategy.processTemplate(tpl, metadata); | ||||
|         expect(root).toHaveText('.s1[mycmp] {}.s2[mycmp] {}root content'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should add the component selector to all template children', () => { | ||||
|         var html = '<p>content<span></span></p>'; | ||||
|         var processedHtml = '<p mycmp="">content<span mycmp=""></span></p>'; | ||||
|         var tpl = DOM.createTemplate(html); | ||||
|         strategy.processTemplate(tpl, metadata); | ||||
|         expect(DOM.getInnerHTML(tpl)).toEqual(processedHtml); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user