* Removes module wrapper.
* Adds basic tests.
* Adds very ugly normalization (pending refactoring).
--- /dev/null
+var EscapeStore = require('./escape-store');
+var QuoteScanner = require('./quote-scanner');
+
+var FreeTextProcessor = function FreeTextProcessor() {
+ this.matches = new EscapeStore('FREE_TEXT');
+};
+
+// Strip content tags by replacing them by the a special
+// marker for further restoring. It's done via string scanning
+// instead of regexps to speed up the process.
+FreeTextProcessor.prototype.escape = function(data) {
+ var self = this;
+
+ return new QuoteScanner(data).each(function(match, store) {
+ var placeholder = self.matches.store(match);
+ store.push(placeholder);
+ });
+};
+
+function normalize(text, data, cursor) {
+ // FIXME: this is a hack
+ var lastSemicolon = data.lastIndexOf(';', cursor);
+ var lastOpenBrace = data.lastIndexOf('{', cursor);
+ var lastOne = 0;
+
+ if (lastSemicolon > -1 && lastOpenBrace > -1)
+ lastOne = Math.max(lastSemicolon, lastOpenBrace);
+ else if (lastSemicolon == -1)
+ lastOne = lastOpenBrace;
+ else
+ lastOne = lastSemicolon;
+
+ var context = data.substring(lastOne + 1, cursor);
+
+ if (/\[[\w\d\-]+[\*\|\~\^\$]?=$/.test(context))
+ text = text.replace(/\\\n|\\\r\n/g, '');
+
+ if (/^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/.test(text) && !/format\($/.test(context)) {
+ var isFont = /^(font|font\-family):/.test(context);
+ var isAttribute = /\[[\w\d\-]+[\*\|\~\^\$]?=$/.test(context);
+ var isKeyframe = /@(-moz-|-o-|-webkit-)?keyframes /.test(context);
+ var isAnimation = /^(-moz-|-o-|-webkit-)?animation(-name)?:/.test(context);
+
+ if (isFont || isAttribute || isKeyframe || isAnimation)
+ text = text.substring(1, text.length - 1);
+ }
+
+ return text;
+}
+
+FreeTextProcessor.prototype.restore = function(data) {
+ var tempData = [];
+ var cursor = 0;
+
+ for (; cursor < data.length;) {
+ var nextMatch = this.matches.nextMatch(data, cursor);
+ if (nextMatch.start < 0)
+ break;
+
+ tempData.push(data.substring(cursor, nextMatch.start));
+ var text = normalize(this.matches.restore(nextMatch.match), data, nextMatch.start);
+ tempData.push(text);
+
+ cursor = nextMatch.end;
+ }
+
+ return tempData.length > 0 ?
+ tempData.join('') + data.substring(cursor, data.length) :
+ data;
+};
+
+module.exports = FreeTextProcessor;
+++ /dev/null
-(function() {
- var EscapeStore = require('./escape-store');
- var QuoteScanner = require('./quote-scanner');
-
- var Free = function Free() {
- this.matches = new EscapeStore('FREE_TEXT');
- };
-
- // Strip content tags by replacing them by the a special
- // marker for further restoring. It's done via string scanning
- // instead of regexps to speed up the process.
- Free.prototype.escape = function(data) {
- var self = this;
-
- return new QuoteScanner(data).each(function(match, store) {
- var placeholder = self.matches.store(match);
- store.push(placeholder);
- });
- };
-
- Free.prototype.restore = function(data) {
- return data.replace(this.matches.placeholderRegExp, this.matches.restore);
- };
-
- module.exports = Free;
-})();
--- /dev/null
+var vows = require('vows');
+var assert = require('assert');
+var FreeTextProcessor = require('../../lib/text/free-text-processor');
+
+function processorContext(context) {
+ var vowContext = {};
+
+ function escaped (targetCSS) {
+ return function (sourceCSS) {
+ var result = new FreeTextProcessor().escape(sourceCSS);
+ assert.equal(result, targetCSS);
+ };
+ }
+
+ function restored (targetCSS) {
+ return function (sourceCSS) {
+ var processor = new FreeTextProcessor();
+ 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(FreeTextProcessor)
+ .addBatch(
+ processorContext({
+ 'no quotes': [
+ 'a{color:red;display:block}',
+ 'a{color:red;display:block}',
+ 'a{color:red;display:block}'
+ ],
+ 'single quoted': [
+ 'a{color:red;content:\'1234\';display:block}',
+ 'a{color:red;content:__ESCAPED_FREE_TEXT_CLEAN_CSS0__;display:block}',
+ 'a{color:red;content:\'1234\';display:block}'
+ ],
+ 'double quoted': [
+ 'a{color:red;content:"1234";display:block}',
+ 'a{color:red;content:__ESCAPED_FREE_TEXT_CLEAN_CSS0__;display:block}',
+ 'a{color:red;content:"1234";display:block}'
+ ],
+ 'inside format': [
+ '@font-face{font-family:X;src:X.ttf format(\'opentype\')}',
+ '@font-face{font-family:X;src:X.ttf format(__ESCAPED_FREE_TEXT_CLEAN_CSS0__)}',
+ '@font-face{font-family:X;src:X.ttf format(\'opentype\')}'
+ ],
+ 'attribute': [
+ 'a[data-type="search"]{}',
+ 'a[data-type=__ESCAPED_FREE_TEXT_CLEAN_CSS0__]{}',
+ 'a[data-type=search]{}'
+ ],
+ 'font name': [
+ 'a{font-family:"Times","Times New Roman",serif}',
+ 'a{font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0__,__ESCAPED_FREE_TEXT_CLEAN_CSS1__,serif}',
+ 'a{font-family:Times,"Times New Roman",serif}'
+ ]
+ })
+ )
+ .export(module);