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;
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 ) {
});
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 = '<input {{#if value}}checked="checked"{{/if}}>';
+ equal(minify(input, customAttrOptions), input);
+
+ input = '<input checked="checked">';
+ equal(minify(input, customAttrOptions), input);
+
+ // With multiple rules
+ customAttrOptions = {
+ customAttrSurround: [
+ [ /\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/ ],
+ [ /\{\{#unless\s+\w+\}\}/, /\{\{\/unless\}\}/ ]
+ ]
};
input = '<input {{#if value}}checked="checked"{{/if}}>';
- equal(minify(input, customAttrOptions), '<input {{#if value}}checked="checked"{{/if}}>');
+ equal(minify(input, customAttrOptions), input);
+
+ input = '<input {{#unless value}}checked="checked"{{/unless}}>';
+ equal(minify(input, customAttrOptions), input);
+
+ input = '<input {{#if value1}}data-attr="example"{{/if}} {{#unless value2}}checked="checked"{{/unless}}>';
+ equal(minify(input, customAttrOptions), input);
input = '<input checked="checked">';
- equal(minify(input, customAttrOptions), '<input checked="checked">');
+ 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 = '<input {{#if value}}checked="checked"{{/if}}>';
+ equal(minify(input, customAttrOptions), '<input {{#if value}}checked{{/if}}>');
+
+ input = '<input {{#if value1}}checked="checked"{{/if}} {{#if value2}}data-attr="foo"{{/if}}>';
+ equal(minify(input, customAttrOptions), '<input {{#if value1}}checked{{/if}} {{#if value2}}data-attr=foo{{/if}}>');
});
test('collapsing whitespace', function() {