* Adds prototypal OO.
* Adds basic tests.
--- /dev/null
+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;
+++ /dev/null
-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, '');
- });
- }
- };
-};
--- /dev/null
+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);