1 | define([ |
---|
2 | "../buildControl", |
---|
3 | "../fileUtils", |
---|
4 | "dojo/_base/lang" |
---|
5 | ], function(bc, fileUtils, lang) { |
---|
6 | // note: this transform originated from the v1.6 build system. In 1.8 it has been enhanced to handle |
---|
7 | // relative paths for imports when the src and destination tree structures are not predictable. |
---|
8 | // |
---|
9 | // The cssImportIgnore logic remains although it is of questionable value because it depends on naming a file |
---|
10 | // exactly as it is named in an import directive. Instead, the tag "importIgnore" can be used to prevent expanding |
---|
11 | // a CSS file and the tag "noOptimize" can be used to prevent optimizing a CSS file. The tag system gives a |
---|
12 | // much more general method to handle this problem. Of course cssImportIgnore can be disabled by simply setting |
---|
13 | // it to falsy. |
---|
14 | |
---|
15 | var cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/g, |
---|
16 | cssUrlRegExp = /url\(\s*([^\)]+)\s*\)?/g, |
---|
17 | |
---|
18 | checkSlashes = function(name){ |
---|
19 | return name.replace(/\\/g, "/"); |
---|
20 | }, |
---|
21 | |
---|
22 | cssImportIgnore = (bc.cssImportIgnore ? |
---|
23 | // trim any spaces off of all items; add a trailing comma for fast lookups |
---|
24 | bc.cssImportIgnore.split(",").map(function(item){return lang.trim(item);}).join(",") + "," : |
---|
25 | ""), |
---|
26 | |
---|
27 | cleanCssUrlQuotes = function(url){ |
---|
28 | // If an URL from a CSS url value contains start/end quotes, remove them. |
---|
29 | // This is not done in the regexp, since the CSS spec allows for ' and " in the URL |
---|
30 | // which makes the regex overly complicated if they are backslash escaped. |
---|
31 | |
---|
32 | url = lang.trim(url); |
---|
33 | if(url.charAt(0) == "'" || url.charAt(0) == '"'){ |
---|
34 | url = url.substring(1, url.length - 1); |
---|
35 | } |
---|
36 | return url; |
---|
37 | }, |
---|
38 | |
---|
39 | removeComments = function(text, filename){ |
---|
40 | var originalText = text, |
---|
41 | startIndex = -1, |
---|
42 | endIndex; |
---|
43 | while((startIndex = text.indexOf("/*")) != -1){ |
---|
44 | endIndex = text.indexOf("*/", startIndex + 2); |
---|
45 | if(endIndex == -1){ |
---|
46 | bc.log("cssOptimizeImproperComment", ["CSS file", filename]); |
---|
47 | return originalText; |
---|
48 | } |
---|
49 | text = text.substring(0, startIndex) + text.substring(endIndex + 2, text.length); |
---|
50 | } |
---|
51 | return text; |
---|
52 | }, |
---|
53 | |
---|
54 | isRelative = function(url){ |
---|
55 | var colonIndex = url.indexOf(":"); |
---|
56 | return url.charAt(0) != "/" && (colonIndex == -1 || colonIndex > url.indexOf("/")); |
---|
57 | }, |
---|
58 | |
---|
59 | getDestRelativeFilename = function(dest, relativeResource){ |
---|
60 | var referenceSegments = dest.split("/"), |
---|
61 | relativeSegments = relativeResource.dest.split("/"); |
---|
62 | referenceSegments.pop(); |
---|
63 | while(referenceSegments.length && relativeSegments.length && referenceSegments[0]==relativeSegments[0]){ |
---|
64 | referenceSegments.shift(); |
---|
65 | relativeSegments.shift(); |
---|
66 | } |
---|
67 | for(var i = 0; i<referenceSegments.length; i++){ |
---|
68 | relativeSegments.unshift(".."); |
---|
69 | } |
---|
70 | return relativeSegments.join("/"); |
---|
71 | }, |
---|
72 | |
---|
73 | getDestRelativeUrlFromSrcUrl = function(dest, fullSrcFilename, msgInfo){ |
---|
74 | var relativeResource = bc.resources[fullSrcFilename]; |
---|
75 | if(relativeResource){ |
---|
76 | return getDestRelativeFilename(dest, relativeResource); |
---|
77 | }else{ |
---|
78 | bc.log("cssOptimizeUnableToResolveURL", msgInfo); |
---|
79 | } |
---|
80 | return 0; |
---|
81 | }, |
---|
82 | |
---|
83 | flattenCss = function(resource){ |
---|
84 | if(resource.optimizedText){ |
---|
85 | return; |
---|
86 | } |
---|
87 | var dest = resource.dest, |
---|
88 | src = resource.src, |
---|
89 | srcReferencePath = fileUtils.getFilepath(checkSlashes(src)), |
---|
90 | text = resource.text; |
---|
91 | text = text.replace(/^\uFEFF/, ''); // remove BOM |
---|
92 | text = removeComments(text, src); |
---|
93 | text = text.replace(cssImportRegExp, function(fullMatch, urlStart, importUrl, urlEnd, mediaTypes){ |
---|
94 | importUrl = checkSlashes(cleanCssUrlQuotes(importUrl)); |
---|
95 | var fixedRelativeUrl, |
---|
96 | fullSrcFilename = (isRelative(importUrl) ? fileUtils.compactPath(fileUtils.catPath(srcReferencePath, importUrl)) : importUrl), |
---|
97 | importResource = bc.resources[fullSrcFilename], |
---|
98 | ignore = false; |
---|
99 | |
---|
100 | // don't expand if explicitly instructed not to by build control |
---|
101 | if( |
---|
102 | (cssImportIgnore && cssImportIgnore.indexOf(importUrl + ",") != -1) || // the v1.6- technique |
---|
103 | (importResource && importResource.tag.importIgnore) // the v1.8+ technique |
---|
104 | ){ |
---|
105 | ignore = true; |
---|
106 | bc.log("cssOptimizeIgnored", ["CSS file", src, "import directive", fullMatch]); |
---|
107 | } |
---|
108 | |
---|
109 | // don't expand if multiple media types given |
---|
110 | if(mediaTypes && lang.trim(mediaTypes)!="all"){ |
---|
111 | ignore = true; |
---|
112 | bc.log("cssOptimizeIgnoredMultiMediaTypes", ["CSS file", src, "import directive", fullMatch]); |
---|
113 | } |
---|
114 | |
---|
115 | // even if not expanding, make sure relative urls are adjusted correctly |
---|
116 | if(ignore){ |
---|
117 | if(isRelative(importUrl) && (fixedRelativeUrl = getDestRelativeUrlFromSrcUrl(dest, fullSrcFilename, ["CSS file", src, "import directive", fullMatch]))){ |
---|
118 | return '@import url("' + fixedRelativeUrl + '")' + (mediaTypes || "") + ";"; |
---|
119 | }else{ |
---|
120 | // right or wrong, this is our only choice at this point |
---|
121 | return fullMatch; |
---|
122 | } |
---|
123 | } |
---|
124 | |
---|
125 | // at this point, we want to expand the import into the calling resource |
---|
126 | |
---|
127 | if(!importResource){ |
---|
128 | bc.log("cssOptimizeIgnoredNoResource", ["CSS file", src, "import directive", fullMatch]); |
---|
129 | return fullMatch; |
---|
130 | } |
---|
131 | |
---|
132 | flattenCss(importResource); |
---|
133 | var importContents = importResource.optimizedText; |
---|
134 | |
---|
135 | // since importContents is flattened, all relative urls are computed with respect to the destination location of importResource |
---|
136 | // these urls need to be adjusted with respect to resource |
---|
137 | var importResourceDestPath = fileUtils.getFilepath(importResource.dest); |
---|
138 | return importContents.replace(cssUrlRegExp, function(fullMatch, urlMatch){ |
---|
139 | var fixedUrl = checkSlashes(cleanCssUrlQuotes(urlMatch)), |
---|
140 | queryString = "", |
---|
141 | queryStart = fixedUrl.search(/[?#]/); |
---|
142 | if(queryStart > 0){ |
---|
143 | queryString = fixedUrl.slice(queryStart); |
---|
144 | fixedUrl = fixedUrl.slice(0, queryStart); |
---|
145 | } |
---|
146 | |
---|
147 | if(isRelative(fixedUrl)){ |
---|
148 | var fullDestFilename = fileUtils.compactPath(fileUtils.catPath(importResourceDestPath, fixedUrl)), |
---|
149 | relativeResource = bc.resourcesByDest[fullDestFilename]; |
---|
150 | if(!relativeResource){ |
---|
151 | bc.log("cssOptimizeUnableToResolveURL", ["CSS file", src, "import", importResource.src, "relative URL", fullMatch]); |
---|
152 | }else{ |
---|
153 | return 'url("' + getDestRelativeFilename(resource.dest, relativeResource) + queryString + '")'; |
---|
154 | } |
---|
155 | } |
---|
156 | // right or wrong, this is our only choice at this point |
---|
157 | return fullMatch; |
---|
158 | }); |
---|
159 | }); |
---|
160 | |
---|
161 | if(/keepLines/i.test(bc.cssOptimize)){ |
---|
162 | // remove multiple empty lines. |
---|
163 | text = text.replace(/(\r\n)+/g, "\r\n").replace(/\n+/g, "\n"); |
---|
164 | }else{ |
---|
165 | // remove newlines and extra spaces |
---|
166 | text = text.replace(/[\r\n]/g, "").replace(/\s+/g, " ").replace(/\{\s/g, "{").replace(/\s\}/g, "}"); |
---|
167 | } |
---|
168 | |
---|
169 | resource.optimizedText = text; |
---|
170 | if(!resource.tag.noOptimize){ |
---|
171 | resource.rawText = resource.text; |
---|
172 | resource.text = text; |
---|
173 | } |
---|
174 | }; |
---|
175 | |
---|
176 | return function(resource, callback) { |
---|
177 | try{ |
---|
178 | if(bc.cssOptimize && !resource.tag.noOptimize){ |
---|
179 | flattenCss(resource); |
---|
180 | bc.log("cssOptimize", ["file", resource.src]); |
---|
181 | } |
---|
182 | }catch(e){ |
---|
183 | bc.log("cssOptimizeFailed", ["file", resource.src, "error", e]); |
---|
184 | } |
---|
185 | }; |
---|
186 | }); |
---|