source: Dev/trunk/src/client/util/build/transforms/optimizeCss.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 6.9 KB
Line 
1define([
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});
Note: See TracBrowser for help on using the repository browser.