-
Notifications
You must be signed in to change notification settings - Fork 2k
/
url.js
143 lines (133 loc) · 4.5 KB
/
url.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
* 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;
}
// do not resolve '#' links, they are used for routing
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);