/* * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ (function(scope) { var urlResolver = { resolveDom: function(root, url) { url = url || baseUrl(root); this.resolveAttributes(root, url); this.resolveStyles(root, url); // handle template.content var templates = root.querySelectorAll('template'); if (templates) { for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) { if (t.content) { this.resolveDom(t.content, url); } } } }, resolveTemplate: function(template) { this.resolveDom(template.content, baseUrl(template)); }, resolveStyles: function(root, url) { var styles = root.querySelectorAll('style'); if (styles) { for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) { this.resolveStyle(s, url); } } }, resolveStyle: function(style, url) { url = url || baseUrl(style); style.textContent = this.resolveCssText(style.textContent, url); }, resolveCssText: function(cssText, baseUrl, keepAbsolute) { cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP); return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP); }, resolveAttributes: function(root, url) { if (root.hasAttributes && root.hasAttributes()) { this.resolveElementAttributes(root, url); } // search for attributes that host urls var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR); if (nodes) { for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) { this.resolveElementAttributes(n, url); } } }, resolveElementAttributes: function(node, url) { url = url || baseUrl(node); URL_ATTRS.forEach(function(v) { var attr = node.attributes[v]; var value = attr && attr.value; var replacement; if (value && value.search(URL_TEMPLATE_SEARCH) < 0) { if (v === 'style') { replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP); } else { replacement = resolveRelativeUrl(url, value); } attr.value = replacement; } }); } }; var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; var URL_ATTRS = ['href', 'src', 'action', 'style', 'url']; var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; var URL_TEMPLATE_SEARCH = '{{.*}}'; var URL_HASH = '#'; function baseUrl(node) { var u = new URL(node.ownerDocument.baseURI); u.search = ''; u.hash = ''; return u; } function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) { return cssText.replace(regexp, function(m, pre, url, post) { var urlPath = url.replace(/["']/g, ''); urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute); return pre + '\'' + urlPath + '\'' + post; }); } function resolveRelativeUrl(baseUrl, url, keepAbsolute) { // do not resolve '/' absolute urls if (url && url[0] === '/') { return url; } var u = new URL(url, baseUrl); return keepAbsolute ? u.href : makeDocumentRelPath(u.href); } function makeDocumentRelPath(url) { var root = baseUrl(document.documentElement); var u = new URL(url, root); if (u.host === root.host && u.port === root.port && u.protocol === root.protocol) { return makeRelPath(root, u); } else { return url; } } // make a relative path from source to target function makeRelPath(sourceUrl, targetUrl) { var source = sourceUrl.pathname; var target = targetUrl.pathname; var s = source.split('/'); var t = target.split('/'); while (s.length && s[0] === t[0]){ s.shift(); t.shift(); } for (var i = 0, l = s.length - 1; i < l; i++) { t.unshift('..'); } // empty '#' is discarded but we need to preserve it. var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash; return t.join('/') + targetUrl.search + hash; } // exports scope.urlResolver = urlResolver; })(Polymer);