| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** Transfomer that inlines polymer-element definitions from html imports. */ | 5 /** Transfomer that inlines polymer-element definitions from html imports. */ |
| 6 library polymer.src.transform.import_inliner; | 6 library polymer.src.transform.import_inliner; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 | 9 |
| 10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
| 11 import 'package:path/path.dart' as path; |
| 11 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; | 12 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; |
| 13 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; |
| 12 import 'common.dart'; | 14 import 'common.dart'; |
| 13 | 15 |
| 14 /** Recursively inlines polymer-element definitions from html imports. */ | 16 /** Recursively inlines polymer-element definitions from html imports. */ |
| 15 // TODO(sigmund): make sure we match semantics of html-imports for tags other | 17 // TODO(sigmund): make sure we match semantics of html-imports for tags other |
| 16 // than polymer-element (see dartbug.com/12613). | 18 // than polymer-element (see dartbug.com/12613). |
| 17 class ImportedElementInliner extends Transformer { | 19 class ImportedElementInliner extends Transformer { |
| 18 Future<bool> isPrimary(Asset input) => | 20 /** Only run this transformer on .html files. */ |
| 19 new Future.value(input.id.extension == ".html"); | 21 final String allowedExtensions = ".html"; |
| 20 | 22 |
| 21 Future apply(Transform transform) { | 23 Future apply(Transform transform) { |
| 22 var seen = new Set<AssetId>(); | 24 var seen = new Set<AssetId>(); |
| 23 var elements = []; | 25 var elements = []; |
| 24 var id = transform.primaryId; | 26 var id = transform.primaryId; |
| 25 seen.add(id); | 27 seen.add(id); |
| 26 return getPrimaryContent(transform).then((content) { | 28 return getPrimaryContent(transform).then((content) { |
| 27 var document = parseHtml(content, id.path, transform.logger); | 29 var document = parseHtml(content, id.path, transform.logger); |
| 28 var future = _visitImports(document, id, transform, seen, elements); | 30 var future = _visitImports(document, id, transform, seen, elements); |
| 29 return future.then((importsFound) { | 31 return future.then((importsFound) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 if (seen.contains(id)) return new Future.value(null); | 73 if (seen.contains(id)) return new Future.value(null); |
| 72 seen.add(id); | 74 seen.add(id); |
| 73 return _collectPolymerElements(id, transform, seen, elements); | 75 return _collectPolymerElements(id, transform, seen, elements); |
| 74 }).then((_) => true); | 76 }).then((_) => true); |
| 75 } | 77 } |
| 76 | 78 |
| 77 /** | 79 /** |
| 78 * Loads an asset identified by [id], visits its imports and collects it's | 80 * Loads an asset identified by [id], visits its imports and collects it's |
| 79 * polymer-element definitions. | 81 * polymer-element definitions. |
| 80 */ | 82 */ |
| 81 Future _collectPolymerElements( | 83 Future _collectPolymerElements(AssetId id, Transform transform, |
| 82 AssetId id, Transform transform, Set<AssetId> seen, List elements) { | 84 Set<AssetId> seen, List elements) { |
| 83 return getContent(transform, id) | 85 return getContent(transform, id).then((content) { |
| 84 .then((content) => parseHtml( | 86 var document = parseHtml( |
| 85 content, id.path, transform.logger, checkDocType: false)) | 87 content, id.path, transform.logger, checkDocType: false); |
| 86 .then((document) { | 88 return _visitImports(document, id, transform, seen, elements).then((_) { |
| 87 return _visitImports(document, id, transform, seen, elements) | 89 var normalizer = new _UrlNormalizer(transform, id); |
| 88 .then((_) => elements.addAll(document.queryAll('polymer-element'))); | 90 for (var element in document.queryAll('polymer-element')) { |
| 89 }); | 91 normalizer.visit(document); |
| 92 elements.add(element); |
| 93 } |
| 94 }); |
| 95 }); |
| 90 } | 96 } |
| 91 } | 97 } |
| 98 |
| 99 /** Internally adjusts urls in the html that we are about to inline. */ |
| 100 class _UrlNormalizer extends TreeVisitor { |
| 101 final Transform transform; |
| 102 |
| 103 /** Asset where the original content (and original url) was found. */ |
| 104 final AssetId sourceId; |
| 105 |
| 106 _UrlNormalizer(this.transform, this.sourceId); |
| 107 |
| 108 visitElement(Element node) { |
| 109 for (var key in node.attributes.keys) { |
| 110 if (_urlAttributes.contains(key)) { |
| 111 var url = node.attributes[key]; |
| 112 if (url != null && url != '') { |
| 113 node.attributes[key] = _newUrl(url, node.sourceSpan); |
| 114 } |
| 115 } |
| 116 } |
| 117 super.visitElement(node); |
| 118 } |
| 119 |
| 120 _newUrl(String href, Span span) { |
| 121 var uri = Uri.parse(href); |
| 122 if (uri.isAbsolute) return href; |
| 123 if (!uri.scheme.isEmpty) return href; |
| 124 if (!uri.host.isEmpty) return href; |
| 125 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. |
| 126 if (path.isAbsolute(href)) return href; |
| 127 |
| 128 var id = resolve(sourceId, href, transform.logger, span); |
| 129 var primaryId = transform.primaryId; |
| 130 |
| 131 if (id.path.startsWith('lib/')) { |
| 132 return 'packages/${id.package}/${id.path.substring(4)}'; |
| 133 } |
| 134 |
| 135 if (id.path.startsWith('asset/')) { |
| 136 return 'assets/${id.package}/${id.path.substring(6)}'; |
| 137 } |
| 138 |
| 139 if (primaryId.package != id.package) { |
| 140 // Techincally we shouldn't get there |
| 141 logger.error("don't know how to include $id from $primaryId", span); |
| 142 return null; |
| 143 } |
| 144 |
| 145 var builder = path.url; |
| 146 return builder.relative(builder.join('/', id.path), |
| 147 from: builder.join('/', builder.dirname(primaryId.path))); |
| 148 } |
| 149 } |
| 150 |
| 151 /** |
| 152 * HTML attributes that expect a URL value. |
| 153 * <http://dev.w3.org/html5/spec/section-index.html#attributes-1> |
| 154 * |
| 155 * Every one of these attributes is a URL in every context where it is used in |
| 156 * the DOM. The comments show every DOM element where an attribute can be used. |
| 157 */ |
| 158 const _urlAttributes = const [ |
| 159 'action', // in form |
| 160 'background', // in body |
| 161 'cite', // in blockquote, del, ins, q |
| 162 'data', // in object |
| 163 'formaction', // in button, input |
| 164 'href', // in a, area, link, base, command |
| 165 'icon', // in command |
| 166 'manifest', // in html |
| 167 'poster', // in video |
| 168 'src', // in audio, embed, iframe, img, input, script, source, track, |
| 169 // video |
| 170 ]; |
| OLD | NEW |