attrFragment = attrName + '=' + attrValue;
}
- return (' ' + attrFragment);
+ return (' ' + attr.customOpen + attrFragment + attr.customClose);
}
function setDefaultTesters(options) {
},
doctype: function(doctype) {
buffer.push(options.useShortDoctype ? '<!DOCTYPE html>' : collapseWhitespace(doctype));
- }
+ },
+ customAttrOpen: options.customAttrOpen,
+ customAttrClose: options.customAttrClose
});
results.push.apply(results, buffer);
'use strict';
// Regular Expressions for parsing tags and attributes
- var startTag = /^<([\w:-]+)((?:\s*[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
+ var startTagOpen = /^<([\w:-]+)/,
+ startTagAttrs = /(?:\s*[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*/,
+ startTagClose = /\s*(\/?)>/,
endTag = /^<\/([\w:-]+)[^>]*>/,
endingSlash = /\/>$/,
- attr = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,
+ singleAttr = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/,
doctype = /^<!DOCTYPE [^>]+>/i,
startIgnore = /<(%|\?)/,
endIgnore = /(%|\?)>/;
return this[ this.length - 1 ];
};
+ var startTag, attr;
+ var customStartTagAttrs;
+ if (handler.customAttrOpen && handler.customAttrClose) {
+ 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'
+ );
+ }
+ 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);
+
while ( html ) {
chars = true;
if ( handler.start ) {
var attrs = [];
- rest.replace(attr, function(match, name) {
- var value = arguments[2] ? arguments[2] :
- arguments[3] ? arguments[3] :
- arguments[4] ? arguments[4] :
- fillAttrs[name] ? name : arguments[2];
+ rest.replace(attr, function() {
+ var name, value, fallbackValue, customOpen, customClose;
+
+ if (arguments.length === 7) {
+ name = arguments[1];
+ fallbackValue = arguments[2];
+ 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];
+ }
+
+ if ( value === undefined ) {
+ value = fillAttrs[name] ? name : fallbackValue;
+ }
+
attrs.push({
name: name,
value: value,
- escaped: value && value.replace(/(^|[^\\])"/g, '$1"')
+ escaped: value && value.replace(/(^|[^\\])"/g, '$1"'),
+ customOpen: customOpen || '',
+ customClose: customClose || ''
});
});
equal(minify(input, { removeAttributeQuotes: true }), '<p class=foo|bar:baz></p>');
});
+ test('preserving custom attribute-wrapping markup', function() {
+ var customAttrOptions = {
+ customAttrOpen: /\{\{#if\s+\w+\}\}/,
+ customAttrClose: /\{\{\/if\}\}/
+ };
+
+ input = '<input {{#if value}}checked="checked"{{/if}}>';
+ equal(minify(input, customAttrOptions), '<input {{#if value}}checked="checked"{{/if}}>');
+
+ input = '<input checked="checked">';
+ equal(minify(input, customAttrOptions), '<input checked="checked">');
+ });
+
test('collapsing whitespace', function() {
input = '<script type="text/javascript"> \n\t alert(1) \n\n\n \t <\/script>';
output = '<script type="text/javascript">alert(1)<\/script>';