From: Jakub Pawlowicz Date: Fri, 26 Sep 2014 11:44:36 +0000 (+0100) Subject: Improves UrlsProcessor. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=9adeeb6e379ec00a4ccda93e968c30ba3f876f72;p=clean-css.git Improves UrlsProcessor. * Adds prototypal OO. * Adds basic tests. --- diff --git a/lib/text/urls-processor.js b/lib/text/urls-processor.js new file mode 100644 index 00000000..24be9774 --- /dev/null +++ b/lib/text/urls-processor.js @@ -0,0 +1,70 @@ +var EscapeStore = require('./escape-store'); + +var UrlsProcessor = function UrlsProcessor() { + this.urls = new EscapeStore('URL'); +}; + +// Strip urls by replacing them by a special +// marker for further restoring. It's done via string scanning +// instead of regexps to speed up the process. +UrlsProcessor.prototype.escape = function (data) { + var nextStart = 0; + var nextEnd = 0; + var cursor = 0; + var tempData = []; + + for (; nextEnd < data.length;) { + nextStart = data.indexOf('url(', nextEnd); + if (nextStart == -1) + break; + + nextEnd = data.indexOf(')', nextStart); + + var url = data.substring(nextStart, nextEnd + 1); + var placeholder = this.urls.store(url); + tempData.push(data.substring(cursor, nextStart)); + tempData.push(placeholder); + + cursor = nextEnd + 1; + } + + return tempData.length > 0 ? + tempData.join('') + data.substring(cursor, data.length) : + data; +}; + +function normalize(url) { + url = url + .replace(/\\?\n|\\?\r\n/g, '') + .replace(/(\s{2,}|\s)/g, ' ') + .replace(/^url\((['"])? /, 'url($1') + .replace(/ (['"])?\)$/, '$1)'); + + if (url.indexOf(' ') == -1 && !/url\(['"]data:[^;]+;charset/.test(url)) + url = url.replace(/["']/g, ''); + + return url; +} + +UrlsProcessor.prototype.restore = function (data) { + var tempData = []; + var cursor = 0; + + for (; cursor < data.length;) { + var nextMatch = this.urls.nextMatch(data, cursor); + if (nextMatch.start < 0) + break; + + tempData.push(data.substring(cursor, nextMatch.start)); + var url = normalize(this.urls.restore(nextMatch.match)); + tempData.push(url); + + cursor = nextMatch.end; + } + + return tempData.length > 0 ? + tempData.join('') + data.substring(cursor, data.length) : + data; +}; + +module.exports = UrlsProcessor; diff --git a/lib/text/urls.js b/lib/text/urls.js deleted file mode 100644 index 3e04c4b0..00000000 --- a/lib/text/urls.js +++ /dev/null @@ -1,41 +0,0 @@ -var EscapeStore = require('./escape-store'); - -module.exports = function Urls() { - var urls = new EscapeStore('URL'); - - return { - // Strip urls by replacing them by a special - // marker for further restoring. It's done via string scanning - // instead of regexps to speed up the process. - escape: function(data) { - var nextStart = 0; - var nextEnd = 0; - var cursor = 0; - var tempData = []; - - for (; nextEnd < data.length;) { - nextStart = data.indexOf('url(', nextEnd); - if (nextStart == -1) - break; - - nextEnd = data.indexOf(')', nextStart); - - var url = data.substring(nextStart, nextEnd + 1); - var placeholder = urls.store(url); - tempData.push(data.substring(cursor, nextStart)); - tempData.push(placeholder); - cursor = nextEnd + 1; - } - - return tempData.length > 0 ? - tempData.join('') + data.substring(cursor, data.length) : - data; - }, - - restore: function(data) { - return data.replace(urls.placeholderRegExp, function(placeholder) { - return urls.restore(placeholder).replace(/\s/g, ''); - }); - } - }; -}; diff --git a/test/text/urls-processor-test.js b/test/text/urls-processor-test.js new file mode 100644 index 00000000..f742b2f9 --- /dev/null +++ b/test/text/urls-processor-test.js @@ -0,0 +1,79 @@ +var vows = require('vows'); +var assert = require('assert'); +var UrlsProcessor = require('../../lib/text/urls-processor'); + +function processorContext(context) { + var vowContext = {}; + + function escaped (targetCSS) { + return function (sourceCSS) { + var result = new UrlsProcessor().escape(sourceCSS); + assert.equal(result, targetCSS); + }; + } + + function restored (targetCSS) { + return function (sourceCSS) { + var processor = new UrlsProcessor(); + var result = processor.restore(processor.escape(sourceCSS)); + assert.equal(result, targetCSS); + }; + } + + for (var key in context) { + vowContext[key] = { + topic: context[key][0], + escaped: escaped(context[key][1]), + restored: restored(context[key][2]) + }; + } + + return vowContext; +} + +vows.describe(UrlsProcessor) + .addBatch( + processorContext({ + 'no urls': [ + 'a{color:red}', + 'a{color:red}', + 'a{color:red}' + ], + 'unquoted': [ + 'div{background:url(some/file.png) repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url(some/file.png) repeat}' + ], + 'single quoted': [ + 'div{background:url(\'some/file.png\') repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url(some/file.png) repeat}' + ], + 'single quoted with whitespace': [ + 'div{background:url(\'some/file name.png\') repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url(\'some/file name.png\') repeat}' + ], + 'double quoted': [ + 'div{background:url("some/file.png") repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url(some/file.png) repeat}' + ], + 'double quoted with whitespace': [ + 'div{background:url("some/file name.png") repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url("some/file name.png") repeat}' + ], + 'multiple': [ + 'div{background:url(one/file.png) repeat}p{background:url(second/file.png)}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}p{background:__ESCAPED_URL_CLEAN_CSS1__}', + 'div{background:url(one/file.png) repeat}p{background:url(second/file.png)}' + ], + 'whitespace': [ + 'div{background:url(\' some/\nfile.png \') repeat}', + 'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}', + 'div{background:url(some/file.png) repeat}' + ] + }) + ) + .export(module);