forked from dart-archive/angular.dart
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(urls): support relative CSS / template URLs in components
Example: When defining a component called 'foo' in file '/myproject/has/many/folders/foo.dart', prior to the change you would do so thusly: @component( selector: 'foo', templateUrl: '/myproject/has/many/folders/foo.html', cssUrl: '/myproject/has/many/folders/foo.css' ) The above is still valid, but now can also be defined rather more succinctly as: @component( selector: 'foo', templateUrl: 'foo.html', cssUrl: 'foo.css' ) A full table of what URLs will be changed/unchanged is below: You Say: Transformer replies: 'packages/foo/bar.html' 'packages/foo/bar.html' 'foo.html' 'packages/foo/bar.html' './foo.html' 'packages/foo/bar.html' '/foo.html' '/foo.html' 'http://google.com/foo.html' 'http://google.com/foo.html' *Note that as shown any absolute paths you define will not be bothered by the transformer, so if you have a templateUrl living at <root>/foo.html, you can still reach it with '/foo.html'! This feature is defaulted to an "off" state through the addition of the ResourceResolverConfig class. To turn the feature on, bind a new ResourceResolverConfig in your module: module.bind(ResourceResolverConfig, toValue: new ResourceResolverConfig(useRelativeUrls: true)); *Testing*: - e2e transformer tests can now be done on the sample application found in the test_transformers folder
- Loading branch information
Showing
37 changed files
with
1,327 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/** | ||
* Dart port of | ||
* https://github.com/Polymer/platform-dev/blob/896e245a0046a397bfc0190d958d2bd162e8f53c/src/url.js | ||
* | ||
* This converts URIs within a document from relative URIs to being absolute URIs. | ||
*/ | ||
|
||
library angular.core_dom.absolute_uris; | ||
|
||
import 'dart:html'; | ||
import 'dart:js' as js; | ||
|
||
import 'package:di/di.dart'; | ||
|
||
import 'package:angular/core_dom/type_to_uri_mapper.dart'; | ||
|
||
@Injectable() | ||
class ResourceUrlResolver { | ||
static final RegExp cssUrlRegexp = new RegExp(r'''(\burl\((?:[\s]+)?)(['"]?)([^]*)(\2(?:[\s]+)?\))'''); | ||
static final RegExp cssImportRegexp = new RegExp(r'(@import[\s]+(?!url\())([^;]*)(;)'); | ||
static const List<String> urlAttrs = const ['href', 'src', 'action']; | ||
static final String urlAttrsSelector = '[${urlAttrs.join('],[')}]'; | ||
static final RegExp urlTemplateSearch = new RegExp('{{.*}}'); | ||
static final RegExp quotes = new RegExp("[\"\']"); | ||
|
||
// Ensures that Uri.base is http/https. | ||
final _baseUri = Uri.base.origin + ("/"); | ||
|
||
final TypeToUriMapper _uriMapper; | ||
final ResourceResolverConfig _config; | ||
|
||
ResourceUrlResolver(this._uriMapper, this._config); | ||
|
||
String resolveHtml(String html, [Uri baseUri]) { | ||
if (baseUri == null) { | ||
return html; | ||
} | ||
HtmlDocument document = new DomParser().parseFromString( | ||
"<!doctype html><html><body>$html</body></html>", "text/html"); | ||
_resolveDom(document.body, baseUri); | ||
return document.body.innerHtml; | ||
} | ||
|
||
/** | ||
* Resolves all relative URIs within the DOM from being relative to | ||
* [originalBase] to being absolute. | ||
*/ | ||
void _resolveDom(Node root, Uri baseUri) { | ||
_resolveAttributes(root, baseUri); | ||
_resolveStyles(root, baseUri); | ||
|
||
// handle template.content | ||
for (var template in _querySelectorAll(root, 'template')) { | ||
if (template.content != null) { | ||
_resolveDom(template.content, baseUri); | ||
} | ||
} | ||
} | ||
|
||
Iterable<Element> _querySelectorAll(Node node, String selectors) { | ||
if (node is DocumentFragment) { | ||
return node.querySelectorAll(selectors); | ||
} | ||
if (node is Element) { | ||
return node.querySelectorAll(selectors); | ||
} | ||
return const []; | ||
} | ||
|
||
void _resolveStyles(Node node, Uri baseUri) { | ||
var styles = _querySelectorAll(node, 'style'); | ||
for (var style in styles) { | ||
_resolveStyle(style, baseUri); | ||
} | ||
} | ||
|
||
void _resolveStyle(StyleElement style, Uri baseUri) { | ||
style.text = resolveCssText(style.text, baseUri); | ||
} | ||
|
||
String resolveCssText(String cssText, Uri baseUri) { | ||
cssText = _replaceUrlsInCssText(cssText, baseUri, cssUrlRegexp); | ||
return _replaceUrlsInCssText(cssText, baseUri, cssImportRegexp); | ||
} | ||
|
||
void _resolveAttributes(Node root, Uri baseUri) { | ||
if (root is Element) { | ||
_resolveElementAttributes(root, baseUri); | ||
} | ||
|
||
for (var node in _querySelectorAll(root, urlAttrsSelector)) { | ||
_resolveElementAttributes(node, baseUri); | ||
} | ||
} | ||
|
||
void _resolveElementAttributes(Element element, Uri baseUri) { | ||
var attrs = element.attributes; | ||
for (var attr in urlAttrs) { | ||
if (attrs.containsKey(attr)) { | ||
var value = attrs[attr]; | ||
if (!value.contains(urlTemplateSearch)) { | ||
attrs[attr] = combine(baseUri, value).toString(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
String _replaceUrlsInCssText(String cssText, Uri baseUri, RegExp regexp) { | ||
return cssText.replaceAllMapped(regexp, (match) { | ||
var url = match[3].trim(); | ||
var urlPath = combine(baseUri, url).toString(); | ||
return '${match[1].trim()}${match[2]}${urlPath}${match[2]})'; | ||
}); | ||
} | ||
/// Combines a type-based URI with a relative URI. | ||
/// | ||
/// [baseUri] is assumed to use package: syntax for package-relative | ||
/// URIs, while [uri] is assumed to use 'packages/' syntax for | ||
/// package-relative URIs. Resulting URIs will use 'packages/' to indicate | ||
/// package-relative URIs. | ||
String combine(Uri baseUri, String uri) { | ||
if (!_config.useRelativeUrls) { | ||
return uri; | ||
} | ||
|
||
if (uri == null) { | ||
uri = baseUri.path; | ||
} else { | ||
// if it's absolute but not package-relative, then just use that | ||
// The "packages/" test is just for backward compatibility. It's ok to | ||
// not resolve them, even through they're relative URLs, because in a Dart | ||
// application, "packages/" is managed by pub which creates a symlinked | ||
// hierarchy and they should all resolve to the same file at any level | ||
// that a "packages/" exists. | ||
if (uri.startsWith("/") || uri.startsWith('packages/')) { | ||
return uri; | ||
} | ||
} | ||
// If it's not absolute, then resolve it first | ||
Uri resolved = baseUri.resolve(uri); | ||
|
||
// If it's package-relative, tack on 'packages/' - Note that eventually | ||
// we may want to change this to be '/packages/' to make it truly absolute | ||
if (resolved.scheme == 'package') { | ||
return 'packages/${resolved.path}'; | ||
} else if (resolved.isAbsolute && resolved.toString().startsWith(_baseUri)) { | ||
var path = resolved.path; | ||
return path.startsWith("/") ? path.substring(1) : path; | ||
} else { | ||
return resolved.toString(); | ||
} | ||
} | ||
|
||
String combineWithType(Type type, String uri) { | ||
if (_config.useRelativeUrls) { | ||
return combine(_uriMapper.uriForType(type), uri); | ||
} else { | ||
return uri; | ||
} | ||
} | ||
} | ||
|
||
@Injectable() | ||
class ResourceResolverConfig { | ||
bool useRelativeUrls; | ||
|
||
ResourceResolverConfig({this.useRelativeUrls}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.