Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions modules/angular2/src/compiler/html_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
stringify,
assertionsEnabled,
StringJoiner,
RegExpWrapper,
serializeEnum,
CONST_EXPR
} from 'angular2/src/facade/lang';
Expand All @@ -17,7 +16,7 @@ import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
import {Injectable} from 'angular2/src/core/di';
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
import {ParseError, ParseLocation, ParseSourceSpan} from './parse_util';
import {HtmlTagDefinition, getHtmlTagDefinition} from './html_tags';
import {HtmlTagDefinition, getHtmlTagDefinition, getHtmlTagNamespacePrefix} from './html_tags';

export class HtmlTreeError extends ParseError {
static create(elementName: string, location: ParseLocation, msg: string): HtmlTreeError {
Expand Down Expand Up @@ -134,7 +133,7 @@ class TreeBuilder {
if (this.peek.type === HtmlTokenType.TAG_OPEN_END_VOID) {
this._advance();
selfClosing = true;
if (namespacePrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
if (getHtmlTagNamespacePrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
this.errors.push(HtmlTreeError.create(
fullName, startTagToken.sourceSpan.start,
`Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
Expand Down Expand Up @@ -237,16 +236,9 @@ function getElementFullName(prefix: string, localName: string,
if (isBlank(prefix)) {
prefix = getHtmlTagDefinition(localName).implicitNamespacePrefix;
if (isBlank(prefix) && isPresent(parentElement)) {
prefix = namespacePrefix(parentElement.name);
prefix = getHtmlTagNamespacePrefix(parentElement.name);
}
}

return mergeNsAndName(prefix, localName);
}

var NS_PREFIX_RE = /^@([^:]+)/g;

function namespacePrefix(elementName: string): string {
var match = RegExpWrapper.firstMatch(NS_PREFIX_RE, elementName);
return isBlank(match) ? null : match[1];
}
22 changes: 21 additions & 1 deletion modules/angular2/src/compiler/html_tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import {isPresent, isBlank, normalizeBool, CONST_EXPR} from 'angular2/src/facade/lang';
import {
isPresent,
isBlank,
normalizeBool,
RegExpWrapper,
CONST_EXPR
} from 'angular2/src/facade/lang';

// see http://www.w3.org/TR/html51/syntax.html#named-character-references
// see https://html.spec.whatwg.org/multipage/entities.json
Expand Down Expand Up @@ -381,3 +387,17 @@ export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition {
var result = TAG_DEFINITIONS[tagName.toLowerCase()];
return isPresent(result) ? result : DEFAULT_TAG_DEFINITION;
}

var NS_PREFIX_RE = /^@([^:]+):(.+)/g;

export function splitHtmlTagNamespace(elementName: string): string[] {
if (elementName[0] != '@') {
return [null, elementName];
}
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, elementName);
return [match[1], match[2]];
}

export function getHtmlTagNamespacePrefix(elementName: string): string {
return splitHtmlTagNamespace(elementName)[0];
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import {Injectable} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {splitHtmlTagNamespace} from 'angular2/src/compiler/html_tags';

import {ElementSchemaRegistry} from './element_schema_registry';

const NAMESPACE_URIS =
CONST_EXPR({'xlink': 'http://www.w3.org/1999/xlink', 'svg': 'http://www.w3.org/2000/svg'});

@Injectable()
export class DomElementSchemaRegistry extends ElementSchemaRegistry {
private _protoElements = new Map<string, Element>();

private _getProtoElement(tagName: string): Element {
var element = this._protoElements.get(tagName);
if (isBlank(element)) {
element = DOM.createElement(tagName);
var nsAndName = splitHtmlTagNamespace(tagName);
element = isPresent(nsAndName[0]) ?
DOM.createElementNS(NAMESPACE_URIS[nsAndName[0]], nsAndName[1]) :
DOM.createElement(nsAndName[1]);
this._protoElements.set(tagName, element);
}
return element;
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/platform/server/parse5_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export class Parse5DomAdapter extends DomAdapter {
createElement(tagName): HTMLElement {
return treeAdapter.createElement(tagName, 'http://www.w3.org/1999/xhtml', []);
}
createElementNS(ns, tagName): HTMLElement { throw 'not implemented'; }
createElementNS(ns, tagName): HTMLElement { return treeAdapter.createElement(tagName, ns, []); }
createTextNode(text: string): Text {
var t = <any>this.createComment(text);
t.type = 'text';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@ export function main() {
expect(registry.getMappedPropName('title')).toEqual('title');
expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown');
});

it('should detect properties on namespaced elements',
() => { expect(registry.hasProperty('@svg:g', 'id')).toBeTruthy(); });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about adding a test that would be falsy ?

});
}
3 changes: 3 additions & 0 deletions modules/playground/e2e_test/svg/svg_spec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library playground.e2e_test.svg.svg_spec;

main() {}
15 changes: 15 additions & 0 deletions modules/playground/e2e_test/svg/svg_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';

describe('SVG', function() {

var URL = 'playground/src/svg/index.html';

afterEach(verifyNoBrowserErrors);
beforeEach(() => { browser.get(URL); });

it('should display SVG component contents', function() {
var svgText = element.all(by.css('g text')).get(0);
expect(svgText.getText()).toEqual('Hello');
});

});
1 change: 1 addition & 0 deletions modules/playground/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ transformers:
- web/src/routing/index.dart
- web/src/template_driven_forms/index.dart
- web/src/zippy_component/index.dart
- web/src/svg/index.dart
- web/src/material/button/index.dart
- web/src/material/checkbox/index.dart
- web/src/material/dialog/index.dart
Expand Down
10 changes: 10 additions & 0 deletions modules/playground/src/svg/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<title>SVG</title>
<body>
<svg-app>
Loading...
</svg-app>
$SCRIPTS$
</body>
</html>
22 changes: 22 additions & 0 deletions modules/playground/src/svg/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {bootstrap} from 'angular2/bootstrap';
import {Component} from 'angular2/core';

@Component({selector: '[svg-group]', template: `<svg:text x="20" y="20">Hello</svg:text>`})
class SvgGroup {
}


@Component({
selector: 'svg-app',
template: `<svg>
<g svg-group></g>
</svg>`,
directives: [SvgGroup]
})
class SvgApp {
}


export function main() {
bootstrap(SvgApp);
}
1 change: 1 addition & 0 deletions tools/broccoli/trees/browser_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const kServedPaths = [
'playground/src/key_events',
'playground/src/routing',
'playground/src/sourcemap',
'playground/src/svg',
'playground/src/todo',
'playground/src/upgrade',
'playground/src/zippy_component',
Expand Down