You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
7.1 KiB
196 lines
7.1 KiB
define([ |
|
"../buildControl", |
|
"../fileUtils", |
|
"dojo/_base/lang" |
|
], function(bc, fileUtils, lang) { |
|
// note: this transform originated from the v1.6 build system. In 1.8 it has been enhanced to handle |
|
// relative paths for imports when the src and destination tree structures are not predictable. |
|
// |
|
// The cssImportIgnore logic remains although it is of questionable value because it depends on naming a file |
|
// exactly as it is named in an import directive. Instead, the tag "importIgnore" can be used to prevent expanding |
|
// a CSS file and the tag "noOptimize" can be used to prevent optimizing a CSS file. The tag system gives a |
|
// much more general method to handle this problem. Of course cssImportIgnore can be disabled by simply setting |
|
// it to falsy. |
|
|
|
var cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/g, |
|
cssUrlRegExp = /url\(\s*([^\)]+)\s*\)?/g, |
|
|
|
checkSlashes = function(name){ |
|
return name.replace(/\\/g, "/"); |
|
}, |
|
|
|
cssImportIgnore = (bc.cssImportIgnore ? |
|
// trim any spaces off of all items; add a trailing comma for fast lookups |
|
bc.cssImportIgnore.split(",").map(function(item){return lang.trim(item);}).join(",") + "," : |
|
""), |
|
|
|
cleanCssUrlQuotes = function(url){ |
|
// If an URL from a CSS url value contains start/end quotes, remove them. |
|
// This is not done in the regexp, since the CSS spec allows for ' and " in the URL |
|
// which makes the regex overly complicated if they are backslash escaped. |
|
|
|
url = lang.trim(url); |
|
if(url.charAt(0) == "'" || url.charAt(0) == '"'){ |
|
url = url.substring(1, url.length - 1); |
|
} |
|
return url; |
|
}, |
|
|
|
removeComments = function(text, filename){ |
|
var originalText = text, |
|
startIndex = -1, |
|
endIndex; |
|
while((startIndex = text.indexOf("/*")) != -1){ |
|
endIndex = text.indexOf("*/", startIndex + 2); |
|
if(endIndex == -1){ |
|
bc.log("cssOptimizeImproperComment", ["CSS file", filename]); |
|
return originalText; |
|
} |
|
text = text.substring(0, startIndex) + text.substring(endIndex + 2, text.length); |
|
} |
|
return text; |
|
}, |
|
|
|
isRelative = function(url){ |
|
var colonIndex = url.indexOf(":"); |
|
return url.charAt(0) != "/" && (colonIndex == -1 || colonIndex > url.indexOf("/")); |
|
}, |
|
|
|
getDestRelativeFilename = function(dest, relativeResource){ |
|
var referenceSegments = dest.split("/"), |
|
relativeSegments = relativeResource.dest.split("/"); |
|
referenceSegments.pop(); |
|
while(referenceSegments.length && relativeSegments.length && referenceSegments[0]==relativeSegments[0]){ |
|
referenceSegments.shift(); |
|
relativeSegments.shift(); |
|
} |
|
for(var i = 0; i<referenceSegments.length; i++){ |
|
relativeSegments.unshift(".."); |
|
} |
|
return relativeSegments.join("/"); |
|
}, |
|
|
|
getDestRelativeUrlFromSrcUrl = function(dest, fullSrcFilename, msgInfo){ |
|
var relativeResource = bc.resources[fullSrcFilename]; |
|
if(relativeResource){ |
|
return getDestRelativeFilename(dest, relativeResource); |
|
}else{ |
|
bc.log("cssOptimizeUnableToResolveURL", msgInfo); |
|
} |
|
return 0; |
|
}, |
|
|
|
flattenCss = function(resource){ |
|
if(resource.optimizedText){ |
|
return; |
|
} |
|
var dest = resource.dest, |
|
src = resource.src, |
|
srcReferencePath = fileUtils.getFilepath(checkSlashes(src)), |
|
text = resource.text, |
|
imports = []; |
|
text = removeComments(text, src); |
|
text = text.replace(cssImportRegExp, function(fullMatch, urlStart, importUrl, urlEnd, mediaTypes){ |
|
importUrl = checkSlashes(cleanCssUrlQuotes(importUrl)); |
|
var fixedRelativeUrl, |
|
fullSrcFilename = (isRelative(importUrl) ? fileUtils.compactPath(fileUtils.catPath(srcReferencePath, importUrl)) : importUrl), |
|
importResource = bc.resources[fullSrcFilename], |
|
ignore = false; |
|
|
|
// don't expand if explicitly instructed not to by build control |
|
if( |
|
(cssImportIgnore && cssImportIgnore.indexOf(importUrl + ",") != -1) || // the v1.6- technique |
|
(importResource && importResource.tag.importIgnore) // the v1.8+ technique |
|
){ |
|
ignore = true; |
|
bc.log("cssOptimizeIgnored", ["CSS file", src, "import directive", fullMatch]); |
|
} |
|
|
|
// don't expand if multiple media types given |
|
if(mediaTypes && lang.trim(mediaTypes)!="all"){ |
|
ignore = true; |
|
bc.log("cssOptimizeIgnoredMultiMediaTypes", ["CSS file", src, "import directive", fullMatch]); |
|
} |
|
|
|
// even if not expanding, make sure relative urls are adjusted correctly |
|
if(ignore){ |
|
if(isRelative(importUrl) && (fixedRelativeUrl = getDestRelativeUrlFromSrcUrl(dest, fullSrcFilename, ["CSS file", src, "import directive", fullMatch]))){ |
|
return '@import url("' + fixedRelativeUrl + '")' + (mediaTypes || "") + ";"; |
|
}else{ |
|
// right or wrong, this is our only choice at this point |
|
return fullMatch; |
|
} |
|
} |
|
|
|
// at this point, we want to expand the import into the calling resource |
|
|
|
if(!importResource){ |
|
bc.log("cssOptimizeIgnoredNoResource", ["CSS file", src, "import directive", fullMatch]); |
|
return fullMatch; |
|
} |
|
|
|
flattenCss(importResource); |
|
var importContents = importResource.optimizedText; |
|
|
|
// since importContents is flattened, all relative urls are computed with respect to the destination location of importResource |
|
// these urls need to be adjusted with respect to resource |
|
var importResourceDestPath = fileUtils.getFilepath(importResource.dest); |
|
return importContents.replace(cssUrlRegExp, function(fullMatch, urlMatch){ |
|
var fixedUrl = checkSlashes(cleanCssUrlQuotes(urlMatch)), |
|
queryString = "", |
|
queryStart = fixedUrl.search(/[?#]/); |
|
if(queryStart > 0){ |
|
queryString = fixedUrl.slice(queryStart); |
|
fixedUrl = fixedUrl.slice(0, queryStart); |
|
} |
|
|
|
if(isRelative(fixedUrl)){ |
|
var fullDestFilename = fileUtils.compactPath(fileUtils.catPath(importResourceDestPath, fixedUrl)), |
|
relativeResource = bc.resourcesByDest[fullDestFilename]; |
|
if(!relativeResource){ |
|
bc.log("cssOptimizeUnableToResolveURL", ["CSS file", src, "import", importResource.src, "relative URL", fullMatch]); |
|
}else{ |
|
return 'url("' + getDestRelativeFilename(resource.dest, relativeResource) + queryString + '")'; |
|
} |
|
} |
|
// right or wrong, this is our only choice at this point |
|
return fullMatch; |
|
}); |
|
}); |
|
|
|
//Hoist imports to maintain valid CSS |
|
text = text.replace(cssImportRegExp, function(fullMatch){ |
|
imports.push(fullMatch); |
|
return ""; |
|
}); |
|
|
|
if(imports.length) { |
|
text = imports.join("\n") + "\n" + text; |
|
} |
|
|
|
if(/keepLines/i.test(bc.cssOptimize)){ |
|
// remove multiple empty lines. |
|
text = text.replace(/(\r\n)+/g, "\r\n").replace(/\n+/g, "\n"); |
|
}else{ |
|
// remove newlines and extra spaces |
|
text = text.replace(/[\r\n]/g, "").replace(/\s+/g, " ").replace(/\{\s/g, "{").replace(/\s\}/g, "}"); |
|
} |
|
|
|
resource.optimizedText = text; |
|
if(!resource.tag.noOptimize){ |
|
resource.rawText = resource.text; |
|
resource.text = text; |
|
} |
|
}; |
|
|
|
return function(resource, callback) { |
|
try{ |
|
if(bc.cssOptimize && !resource.tag.noOptimize){ |
|
flattenCss(resource); |
|
bc.log("cssOptimize", ["file", resource.src]); |
|
} |
|
}catch(e){ |
|
bc.log("cssOptimizeFailed", ["file", resource.src, "error", e]); |
|
} |
|
}; |
|
});
|
|
|