From bc708bc14792c803bad03dbb95844a355434de1b Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Tue, 8 Jul 2014 13:56:42 -0500 Subject: [PATCH] Multiple attribute-surrounding expressions customAttrOpen / customAttrClose replaced by customAttrSurround customAttrSurround is an array of tuples. Each tuple represents an open/close regexp pair. --- src/htmlminifier.js | 3 +- src/htmlparser.js | 107 ++++++++++++++++++++++++++------------------ tests/minifier.js | 48 +++++++++++++++++--- 3 files changed, 108 insertions(+), 50 deletions(-) diff --git a/src/htmlminifier.js b/src/htmlminifier.js index 060447e..abae81c 100644 --- a/src/htmlminifier.js +++ b/src/htmlminifier.js @@ -596,8 +596,7 @@ doctype: function(doctype) { buffer.push(options.useShortDoctype ? '' : collapseWhitespace(doctype)); }, - customAttrOpen: options.customAttrOpen, - customAttrClose: options.customAttrClose + customAttrSurround: options.customAttrSurround }); results.push.apply(results, buffer); diff --git a/src/htmlparser.js b/src/htmlparser.js index cecd65a..35435fd 100644 --- a/src/htmlparser.js +++ b/src/htmlparser.js @@ -63,41 +63,62 @@ var reCache = {}, stackedTag, reStackedTag, tagMatch; - var HTMLParser = global.HTMLParser = function( html, handler ) { - var index, chars, match, stack = [], last = html, prevTag, nextTag; - stack.last = function() { - return this[ this.length - 1 ]; - }; - - var startTag, attr; + function startTagForHandler( handler ) { var customStartTagAttrs; - if (handler.customAttrOpen && handler.customAttrClose) { + + if ( handler.customAttrSurround ) { + var attrClauses = []; + + for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { + // Capture the custom attribute opening and closing markup surrounding the standard attribute rules + attrClauses[i] = '(?:\\s*' + + handler.customAttrSurround[i][0].source + + startTagAttrs.source + + handler.customAttrSurround[i][1].source + + ')'; + } + attrClauses.unshift(startTagAttrs.source); + customStartTagAttrs = new RegExp( - '(' + - '(?:' + - // Capture the custom attribute opening and closing markup surrounding the standard attribute rules - '(?:\\s*' + handler.customAttrOpen.source + startTagAttrs.source + handler.customAttrClose.source + ')' + - '|' + - // Or capture just the standard attribute rules - startTagAttrs.source + - ')*' + - ')' - ); - attr = new RegExp( - '(?:' + - '(' + handler.customAttrOpen.source + ')' + - singleAttr.source + - '(' + handler.customAttrClose.source + ')' + - ')|(?:' + singleAttr.source + ')', 'g' + '((?:' + attrClauses.join('|') + ')*)' ); } else { // No custom attribute wrappers specified, so just capture the standard attribute rules customStartTagAttrs = new RegExp('(' + startTagAttrs.source + ')'); - attr = new RegExp(singleAttr.source, 'g'); } - startTag = new RegExp(startTagOpen.source + customStartTagAttrs.source + startTagClose.source); + return new RegExp(startTagOpen.source + customStartTagAttrs.source + startTagClose.source); + } + + function attrForHandler( handler ) { + if ( handler.customAttrSurround ) { + var attrClauses = []; + for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { + attrClauses[i] = '(?:' + + '(' + handler.customAttrSurround[i][0].source + ')' + + singleAttr.source + + '(' + handler.customAttrSurround[i][1].source + ')' + + ')'; + } + attrClauses.unshift('(?:' + singleAttr.source + ')'); + + return new RegExp(attrClauses.join('|'), 'g'); + } + else { + return new RegExp(singleAttr.source, 'g'); + } + } + + + var HTMLParser = global.HTMLParser = function( html, handler ) { + var index, chars, match, stack = [], last = html, prevTag, nextTag; + stack.last = function() { + return this[ this.length - 1 ]; + }; + + var startTag = startTagForHandler(handler); + var attr = attrForHandler(handler); while ( html ) { chars = true; @@ -257,27 +278,27 @@ if ( handler.start ) { var attrs = []; - rest.replace(attr, function() { + rest.replace(attr, function () { var name, value, fallbackValue, customOpen, customClose; - if (arguments.length === 7) { - name = arguments[1]; + name = arguments[1]; + if ( name ) { fallbackValue = arguments[2]; - value = fallbackValue - || arguments[3] - || arguments[4]; + value = fallbackValue || arguments[3] || arguments[4]; } - else { - name = arguments[2] || arguments[7]; - fallbackValue = arguments[3]; - value = fallbackValue - || arguments[4] - || arguments[5] - || arguments[8] - || arguments[9] - || arguments[10]; - customOpen = arguments[1]; - customClose = arguments[6]; + else if ( handler.customAttrSurround ) { + for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { + name = arguments[i * 6 + 6]; + if ( name ) { + fallbackValue = arguments[i * 6 + 7]; + value = fallbackValue + || arguments[i * 6 + 8] + || arguments[i * 6 + 9]; + customOpen = arguments[i * 6 + 5]; + customClose = arguments[i * 6 + 10]; + break; + } + } } if ( value === undefined ) { diff --git a/tests/minifier.js b/tests/minifier.js index 31f3a95..c64b556 100644 --- a/tests/minifier.js +++ b/tests/minifier.js @@ -503,16 +503,54 @@ }); test('preserving custom attribute-wrapping markup', function() { - var customAttrOptions = { - customAttrOpen: /\{\{#if\s+\w+\}\}/, - customAttrClose: /\{\{\/if\}\}/ + var customAttrOptions; + + // With a single rule + customAttrOptions = { + customAttrSurround: [ [ /\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/ ] ] + }; + + input = ''; + equal(minify(input, customAttrOptions), input); + + input = ''; + equal(minify(input, customAttrOptions), input); + + // With multiple rules + customAttrOptions = { + customAttrSurround: [ + [ /\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/ ], + [ /\{\{#unless\s+\w+\}\}/, /\{\{\/unless\}\}/ ] + ] }; input = ''; - equal(minify(input, customAttrOptions), ''); + equal(minify(input, customAttrOptions), input); + + input = ''; + equal(minify(input, customAttrOptions), input); + + input = ''; + equal(minify(input, customAttrOptions), input); input = ''; - equal(minify(input, customAttrOptions), ''); + equal(minify(input, customAttrOptions), input); + + // With multiple rules and richer options + customAttrOptions = { + customAttrSurround: [ + [ /\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/ ], + [ /\{\{#unless\s+\w+\}\}/, /\{\{\/unless\}\}/ ] + ], + collapseBooleanAttributes: true, + removeAttributeQuotes: true + }; + + input = ''; + equal(minify(input, customAttrOptions), ''); + + input = ''; + equal(minify(input, customAttrOptions), ''); }); test('collapsing whitespace', function() { -- 2.34.1