4 QUnit.config.autostart = false;
5 if (typeof minify === 'undefined') {
6 self.minify = require('html-minifier').minify;
9 QUnit.test('`minifiy` exists', function(assert) {
13 QUnit.test('parsing non-trivial markup', function(assert) {
16 assert.equal(minify('</td>'), '');
17 assert.equal(minify('</p>'), '<p></p>');
18 assert.equal(minify('</br>'), '<br>');
19 assert.equal(minify('<br>x</br>'), '<br>x<br>');
20 assert.equal(minify('<p title="</p>">x</p>'), '<p title="</p>">x</p>');
21 assert.equal(minify('<p title=" <!-- hello world --> ">x</p>'), '<p title=" <!-- hello world --> ">x</p>');
22 assert.equal(minify('<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>'), '<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>');
23 assert.equal(minify('<p foo-bar=baz>xxx</p>'), '<p foo-bar="baz">xxx</p>');
24 assert.equal(minify('<p foo:bar=baz>xxx</p>'), '<p foo:bar="baz">xxx</p>');
25 assert.equal(minify('<p foo.bar=baz>xxx</p>'), '<p foo.bar="baz">xxx</p>');
27 input = '<div><div><div><div><div><div><div><div><div><div>' +
28 'i\'m 10 levels deep' +
29 '</div></div></div></div></div></div></div></div></div></div>';
31 assert.equal(minify(input), input);
33 assert.equal(minify('<script>alert(\'<!--\')</script>'), '<script>alert(\'<!--\')</script>');
34 assert.equal(minify('<script>alert(\'<!-- foo -->\')</script>'), '<script>alert(\'<!-- foo -->\')</script>');
35 assert.equal(minify('<script>alert(\'-->\')</script>'), '<script>alert(\'-->\')</script>');
37 assert.equal(minify('<a title="x"href=" ">foo</a>'), '<a title="x" href="">foo</a>');
38 assert.equal(minify('<p id=""class=""title="">x'), '<p id="" class="" title="">x</p>');
39 assert.equal(minify('<p x="x\'"">x</p>'), '<p x="x\'">x</p>', 'trailing quote should be ignored');
40 assert.equal(minify('<a href="#"><p>Click me</p></a>'), '<a href="#"><p>Click me</p></a>');
41 assert.equal(minify('<span><button>Hit me</button></span>'), '<span><button>Hit me</button></span>');
42 assert.equal(minify('<object type="image/svg+xml" data="image.svg"><div>[fallback image]</div></object>'),
43 '<object type="image/svg+xml" data="image.svg"><div>[fallback image]</div></object>'
46 assert.equal(minify('<ng-include src="x"></ng-include>'), '<ng-include src="x"></ng-include>');
47 assert.equal(minify('<ng:include src="x"></ng:include>'), '<ng:include src="x"></ng:include>');
48 assert.equal(minify('<ng-include src="\'views/partial-notification.html\'"></ng-include><div ng-view=""></div>'),
49 '<ng-include src="\'views/partial-notification.html\'"></ng-include><div ng-view=""></div>'
52 // will cause test to time-out if fail
53 input = '<p>For more information, read <a href=https://stackoverflow.com/questions/17408815/fieldset-resizes-wrong-appears-to-have-unremovable-min-width-min-content/17863685#17863685>this Stack Overflow answer</a>.</p>';
54 output = '<p>For more information, read <a href="https://stackoverflow.com/questions/17408815/fieldset-resizes-wrong-appears-to-have-unremovable-min-width-min-content/17863685#17863685">this Stack Overflow answer</a>.</p>';
55 assert.equal(minify(input), output);
57 input = '<html â¡></html>';
58 assert.equal(minify(input), input);
60 input = '<h:ællæ></h:ællæ>';
61 assert.equal(minify(input), input);
64 assert.throws(function() {
66 }, 'Invalid tag name');
68 input = '<begriffs.pagination ng-init="perPage=20" collection="logs" url="\'/api/logs?user=-1\'" per-page="perPage" per-page-presets="[10,20,50,100]" template-url="/assets/paginate-anything.html"></begriffs.pagination>';
69 assert.equal(minify(input), input);
71 // https://github.com/kangax/html-minifier/issues/41
72 assert.equal(minify('<some-tag-1></some-tag-1><some-tag-2></some-tag-2>'),
73 '<some-tag-1></some-tag-1><some-tag-2></some-tag-2>'
76 // https://github.com/kangax/html-minifier/issues/40
77 assert.equal(minify('[\']["]'), '[\']["]');
79 // https://github.com/kangax/html-minifier/issues/21
80 assert.equal(minify('<a href="test.html"><div>hey</div></a>'), '<a href="test.html"><div>hey</div></a>');
82 // https://github.com/kangax/html-minifier/issues/17
83 assert.equal(minify(':) <a href="http://example.com">link</a>'), ':) <a href="http://example.com">link</a>');
85 // https://github.com/kangax/html-minifier/issues/169
86 assert.equal(minify('<a href>ok</a>'), '<a href>ok</a>');
88 assert.equal(minify('<a onclick></a>'), '<a onclick></a>');
90 // https://github.com/kangax/html-minifier/issues/229
91 assert.equal(minify('<CUSTOM-TAG></CUSTOM-TAG><div>Hello :)</div>'), '<custom-tag></custom-tag><div>Hello :)</div>');
93 // https://github.com/kangax/html-minifier/issues/507
94 input = '<tag v-ref:vm_pv :imgs=" objpicsurl_ "></tag>';
95 assert.equal(minify(input), input);
96 assert.throws(function() {
97 minify('<tag v-ref:vm_pv :imgs=" objpicsurl_ " ss"123></tag>');
98 }, 'invalid attribute name');
100 // https://github.com/kangax/html-minifier/issues/512
101 input = '<input class="form-control" type="text" style="" id="{{vm.formInputName}}" name="{{vm.formInputName}}"' +
102 ' placeholder="YYYY-MM-DD"' +
103 ' date-range-picker' +
104 ' data-ng-model="vm.value"' +
105 ' data-ng-model-options="{ debounce: 1000 }"' +
106 ' data-ng-pattern="vm.options.format"' +
107 ' data-options="vm.datepickerOptions">';
108 assert.equal(minify(input), input);
109 assert.throws(function() {
111 '<input class="form-control" type="text" style="" id="{{vm.formInputName}}" name="{{vm.formInputName}}"' +
112 ' <!--FIXME hardcoded placeholder - dates may not be used for service required fields yet. -->' +
113 ' placeholder="YYYY-MM-DD"' +
114 ' date-range-picker' +
115 ' data-ng-model="vm.value"' +
116 ' data-ng-model-options="{ debounce: 1000 }"' +
117 ' data-ng-pattern="vm.options.format"' +
118 ' data-options="vm.datepickerOptions">'
120 }, 'HTML comment inside tag');
122 input = '<br a=\u00A0 b=" " c="\u00A0">';
123 output = '<br a="\u00A0" b=" " c="\u00A0">';
124 assert.equal(minify(input), output);
125 output = '<br a="\u00A0"b="\u00A0"c="\u00A0">';
126 assert.equal(minify(input, {
127 decodeEntities: true,
128 removeTagWhitespace: true,
130 output = '<br a=\u00A0 b=\u00A0 c=\u00A0>';
131 assert.equal(minify(input, {
132 decodeEntities: true,
133 removeAttributeQuotes: true
135 assert.equal(minify(input, {
136 decodeEntities: true,
137 removeAttributeQuotes: true,
138 removeTagWhitespace: true,
142 QUnit.test('options', function(assert) {
143 var input = '<p>blah<span>blah 2<span>blah 3</span></span></p>';
144 assert.equal(minify(input), input);
145 assert.equal(minify(input, {}), input);
148 QUnit.test('case normalization', function(assert) {
149 assert.equal(minify('<P>foo</p>'), '<p>foo</p>');
150 assert.equal(minify('<DIV>boo</DIV>'), '<div>boo</div>');
151 assert.equal(minify('<DIV title="moo">boo</DiV>'), '<div title="moo">boo</div>');
152 assert.equal(minify('<DIV TITLE="blah">boo</DIV>'), '<div title="blah">boo</div>');
153 assert.equal(minify('<DIV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
154 assert.equal(minify('<DiV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
157 QUnit.test('space normalization between attributes', function(assert) {
158 assert.equal(minify('<p title="bar">foo</p>'), '<p title="bar">foo</p>');
159 assert.equal(minify('<img src="test"/>'), '<img src="test">');
160 assert.equal(minify('<p title = "bar">foo</p>'), '<p title="bar">foo</p>');
161 assert.equal(minify('<p title\n\n\t =\n "bar">foo</p>'), '<p title="bar">foo</p>');
162 assert.equal(minify('<img src="test" \n\t />'), '<img src="test">');
163 assert.equal(minify('<input title="bar" id="boo" value="hello world">'), '<input title="bar" id="boo" value="hello world">');
166 QUnit.test('space normalization around text', function(assert) {
168 input = ' <p>blah</p>\n\n\n ';
169 assert.equal(minify(input), input);
170 output = '<p>blah</p>';
171 assert.equal(minify(input, { collapseWhitespace: true }), output);
172 output = ' <p>blah</p> ';
173 assert.equal(minify(input, {
174 collapseWhitespace: true,
175 conservativeCollapse: true
177 output = '<p>blah</p>\n';
178 assert.equal(minify(input, {
179 collapseWhitespace: true,
180 preserveLineBreaks: true
182 output = ' <p>blah</p>\n';
183 assert.equal(minify(input, {
184 collapseWhitespace: true,
185 conservativeCollapse: true,
186 preserveLineBreaks: true
189 'a', 'abbr', 'acronym', 'b', 'big', 'del', 'em', 'font', 'i', 'ins', 'kbd',
190 'mark', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup',
191 'time', 'tt', 'u', 'var'
192 ].forEach(function(el) {
193 assert.equal(minify('foo <' + el + '>baz</' + el + '> bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '> bar');
194 assert.equal(minify('foo<' + el + '>baz</' + el + '>bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '>bar');
195 assert.equal(minify('foo <' + el + '>baz</' + el + '>bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '>bar');
196 assert.equal(minify('foo<' + el + '>baz</' + el + '> bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '> bar');
197 assert.equal(minify('foo <' + el + '> baz </' + el + '> bar', { collapseWhitespace: true }), 'foo <' + el + '>baz </' + el + '>bar');
198 assert.equal(minify('foo<' + el + '> baz </' + el + '>bar', { collapseWhitespace: true }), 'foo<' + el + '> baz </' + el + '>bar');
199 assert.equal(minify('foo <' + el + '> baz </' + el + '>bar', { collapseWhitespace: true }), 'foo <' + el + '>baz </' + el + '>bar');
200 assert.equal(minify('foo<' + el + '> baz </' + el + '> bar', { collapseWhitespace: true }), 'foo<' + el + '> baz </' + el + '>bar');
201 assert.equal(minify('<div>foo <' + el + '>baz</' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '> bar</div>');
202 assert.equal(minify('<div>foo<' + el + '>baz</' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '>bar</div>');
203 assert.equal(minify('<div>foo <' + el + '>baz</' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '>bar</div>');
204 assert.equal(minify('<div>foo<' + el + '>baz</' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '> bar</div>');
205 assert.equal(minify('<div>foo <' + el + '> baz </' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz </' + el + '>bar</div>');
206 assert.equal(minify('<div>foo<' + el + '> baz </' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '> baz </' + el + '>bar</div>');
207 assert.equal(minify('<div>foo <' + el + '> baz </' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz </' + el + '>bar</div>');
208 assert.equal(minify('<div>foo<' + el + '> baz </' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '> baz </' + el + '>bar</div>');
210 // Don't trim whitespace around element, but do trim within
212 'bdi', 'bdo', 'button', 'cite', 'code', 'dfn', 'math', 'q', 'rt', 'rtc', 'ruby', 'svg'
213 ].forEach(function(el) {
214 assert.equal(minify('foo <' + el + '>baz</' + el + '> bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '> bar');
215 assert.equal(minify('foo<' + el + '>baz</' + el + '>bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '>bar');
216 assert.equal(minify('foo <' + el + '>baz</' + el + '>bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '>bar');
217 assert.equal(minify('foo<' + el + '>baz</' + el + '> bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '> bar');
218 assert.equal(minify('foo <' + el + '> baz </' + el + '> bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '> bar');
219 assert.equal(minify('foo<' + el + '> baz </' + el + '>bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '>bar');
220 assert.equal(minify('foo <' + el + '> baz </' + el + '>bar', { collapseWhitespace: true }), 'foo <' + el + '>baz</' + el + '>bar');
221 assert.equal(minify('foo<' + el + '> baz </' + el + '> bar', { collapseWhitespace: true }), 'foo<' + el + '>baz</' + el + '> bar');
222 assert.equal(minify('<div>foo <' + el + '>baz</' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '> bar</div>');
223 assert.equal(minify('<div>foo<' + el + '>baz</' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '>bar</div>');
224 assert.equal(minify('<div>foo <' + el + '>baz</' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '>bar</div>');
225 assert.equal(minify('<div>foo<' + el + '>baz</' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '> bar</div>');
226 assert.equal(minify('<div>foo <' + el + '> baz </' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '> bar</div>');
227 assert.equal(minify('<div>foo<' + el + '> baz </' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '>bar</div>');
228 assert.equal(minify('<div>foo <' + el + '> baz </' + el + '>bar</div>', { collapseWhitespace: true }), '<div>foo <' + el + '>baz</' + el + '>bar</div>');
229 assert.equal(minify('<div>foo<' + el + '> baz </' + el + '> bar</div>', { collapseWhitespace: true }), '<div>foo<' + el + '>baz</' + el + '> bar</div>');
232 ['<span> foo </span>', '<span>foo</span>'],
233 [' <span> foo </span> ', '<span>foo</span>'],
234 ['<nobr>a</nobr>', '<nobr>a</nobr>'],
235 ['<nobr>a </nobr>', '<nobr>a</nobr>'],
236 ['<nobr> a</nobr>', '<nobr>a</nobr>'],
237 ['<nobr> a </nobr>', '<nobr>a</nobr>'],
238 ['a<nobr>b</nobr>c', 'a<nobr>b</nobr>c'],
239 ['a<nobr>b </nobr>c', 'a<nobr>b </nobr>c'],
240 ['a<nobr> b</nobr>c', 'a<nobr> b</nobr>c'],
241 ['a<nobr> b </nobr>c', 'a<nobr> b </nobr>c'],
242 ['a<nobr>b</nobr> c', 'a<nobr>b</nobr> c'],
243 ['a<nobr>b </nobr> c', 'a<nobr>b</nobr> c'],
244 ['a<nobr> b</nobr> c', 'a<nobr> b</nobr> c'],
245 ['a<nobr> b </nobr> c', 'a<nobr> b</nobr> c'],
246 ['a <nobr>b</nobr>c', 'a <nobr>b</nobr>c'],
247 ['a <nobr>b </nobr>c', 'a <nobr>b </nobr>c'],
248 ['a <nobr> b</nobr>c', 'a <nobr>b</nobr>c'],
249 ['a <nobr> b </nobr>c', 'a <nobr>b </nobr>c'],
250 ['a <nobr>b</nobr> c', 'a <nobr>b</nobr> c'],
251 ['a <nobr>b </nobr> c', 'a <nobr>b</nobr> c'],
252 ['a <nobr> b</nobr> c', 'a <nobr>b</nobr> c'],
253 ['a <nobr> b </nobr> c', 'a <nobr>b</nobr> c']
254 ].forEach(function(inputs) {
255 assert.equal(minify(inputs[0], {
256 collapseWhitespace: true,
257 conservativeCollapse: true
258 }), inputs[0], inputs[0]);
259 assert.equal(minify(inputs[0], { collapseWhitespace: true }), inputs[1], inputs[0]);
260 var input = '<div>' + inputs[0] + '</div>';
261 assert.equal(minify(input, {
262 collapseWhitespace: true,
263 conservativeCollapse: true
265 var output = '<div>' + inputs[1] + '</div>';
266 assert.equal(minify(input, { collapseWhitespace: true }), output, input);
268 assert.equal(minify('<p>foo <img> bar</p>', { collapseWhitespace: true }), '<p>foo <img> bar</p>');
269 assert.equal(minify('<p>foo<img>bar</p>', { collapseWhitespace: true }), '<p>foo<img>bar</p>');
270 assert.equal(minify('<p>foo <img>bar</p>', { collapseWhitespace: true }), '<p>foo <img>bar</p>');
271 assert.equal(minify('<p>foo<img> bar</p>', { collapseWhitespace: true }), '<p>foo<img> bar</p>');
272 assert.equal(minify('<p>foo <wbr> bar</p>', { collapseWhitespace: true }), '<p>foo<wbr> bar</p>');
273 assert.equal(minify('<p>foo<wbr>bar</p>', { collapseWhitespace: true }), '<p>foo<wbr>bar</p>');
274 assert.equal(minify('<p>foo <wbr>bar</p>', { collapseWhitespace: true }), '<p>foo <wbr>bar</p>');
275 assert.equal(minify('<p>foo<wbr> bar</p>', { collapseWhitespace: true }), '<p>foo<wbr> bar</p>');
276 assert.equal(minify('<p>foo <wbr baz moo=""> bar</p>', { collapseWhitespace: true }), '<p>foo<wbr baz moo=""> bar</p>');
277 assert.equal(minify('<p>foo<wbr baz moo="">bar</p>', { collapseWhitespace: true }), '<p>foo<wbr baz moo="">bar</p>');
278 assert.equal(minify('<p>foo <wbr baz moo="">bar</p>', { collapseWhitespace: true }), '<p>foo <wbr baz moo="">bar</p>');
279 assert.equal(minify('<p>foo<wbr baz moo=""> bar</p>', { collapseWhitespace: true }), '<p>foo<wbr baz moo=""> bar</p>');
280 assert.equal(minify('<p> <a href="#"> <code>foo</code></a> bar</p>', { collapseWhitespace: true }), '<p><a href="#"><code>foo</code></a> bar</p>');
281 assert.equal(minify('<p><a href="#"><code>foo </code></a> bar</p>', { collapseWhitespace: true }), '<p><a href="#"><code>foo</code></a> bar</p>');
282 assert.equal(minify('<p> <a href="#"> <code> foo</code></a> bar </p>', { collapseWhitespace: true }), '<p><a href="#"><code>foo</code></a> bar</p>');
283 assert.equal(minify('<div> Empty <!-- or --> not </div>', { collapseWhitespace: true }), '<div>Empty<!-- or --> not</div>');
284 assert.equal(minify('<div> a <input><!-- b --> c </div>', {
285 collapseWhitespace: true,
287 }), '<div>a <input> c</div>');
290 '<!-- d --> a <? b ?> c ',
291 ' <!-- d -->a <? b ?> c ',
292 ' a<!-- d --> <? b ?> c ',
293 ' a <!-- d --><? b ?> c ',
294 ' a <? b ?><!-- d --> c ',
295 ' a <? b ?> <!-- d -->c ',
296 ' a <? b ?> c<!-- d --> ',
297 ' a <? b ?> c <!-- d -->'
298 ].forEach(function(input) {
299 assert.equal(minify(input, {
300 collapseWhitespace: true,
301 conservativeCollapse: true
303 assert.equal(minify(input, {
304 collapseWhitespace: true,
306 }), 'a <? b ?> c', input);
307 assert.equal(minify(input, {
308 collapseWhitespace: true,
309 conservativeCollapse: true,
311 }), ' a <? b ?> c ', input);
312 input = '<p>' + input + '</p>';
313 assert.equal(minify(input, {
314 collapseWhitespace: true,
315 conservativeCollapse: true
317 assert.equal(minify(input, {
318 collapseWhitespace: true,
320 }), '<p>a <? b ?> c</p>', input);
321 assert.equal(minify(input, {
322 collapseWhitespace: true,
323 conservativeCollapse: true,
325 }), '<p> a <? b ?> c </p>', input);
327 input = '<li><i></i> <b></b> foo</li>';
328 output = '<li><i></i> <b></b> foo</li>';
329 assert.equal(minify(input, { collapseWhitespace: true }), output);
330 input = '<li><i> </i> <b></b> foo</li>';
331 assert.equal(minify(input, { collapseWhitespace: true }), output);
332 input = '<li> <i></i> <b></b> foo</li>';
333 assert.equal(minify(input, { collapseWhitespace: true }), output);
334 input = '<li><i></i> <b> </b> foo</li>';
335 assert.equal(minify(input, { collapseWhitespace: true }), output);
336 input = '<li> <i> </i> <b> </b> foo</li>';
337 assert.equal(minify(input, { collapseWhitespace: true }), output);
338 input = '<div> <a href="#"> <span> <b> foo </b> <i> bar </i> </span> </a> </div>';
339 output = '<div><a href="#"><span><b>foo </b><i>bar</i></span></a></div>';
340 assert.equal(minify(input, { collapseWhitespace: true }), output);
341 input = '<head> <!-- a --> <!-- b --><link> </head>';
342 output = '<head><!-- a --><!-- b --><link></head>';
343 assert.equal(minify(input, { collapseWhitespace: true }), output);
344 input = '<head> <!-- a --> <!-- b --> <!-- c --><link> </head>';
345 output = '<head><!-- a --><!-- b --><!-- c --><link></head>';
346 assert.equal(minify(input, { collapseWhitespace: true }), output);
347 input = '<p> foo\u00A0bar\nbaz \u00A0\nmoo\t</p>';
348 output = '<p>foo\u00A0bar baz \u00A0 moo</p>';
349 assert.equal(minify(input, { collapseWhitespace: true }), output);
350 input = '<label> foo </label>\n' +
352 '<object> bar </object>\n' +
353 '<select> baz </select>\n' +
354 '<textarea> moo </textarea>\n';
355 output = '<label>foo</label> <input> <object>bar</object> <select>baz</select> <textarea> moo </textarea>';
356 assert.equal(minify(input, { collapseWhitespace: true }), output);
363 output = '<pre>\nfoo\n<br>\nbar\n</pre>baz';
364 assert.equal(minify(input, { collapseWhitespace: true }), output);
367 QUnit.test('types of whitespace that should always be preserved', function(assert) {
369 var input = '<div>\u200afo\u200ao\u200a</div>';
370 assert.equal(minify(input, { collapseWhitespace: true }), input);
372 // Hair space passed as HTML entity:
373 var inputWithEntities = '<div> fo o </div>';
374 assert.equal(minify(inputWithEntities, { collapseWhitespace: true }), inputWithEntities);
376 // Hair space passed as HTML entity, in decodeEntities:true mode:
377 assert.equal(minify(inputWithEntities, { collapseWhitespace: true, decodeEntities: true }), input);
380 // Non-breaking space:
381 input = '<div>\xa0fo\xa0o\xa0</div>';
382 assert.equal(minify(input, { collapseWhitespace: true }), input);
384 // Non-breaking space passed as HTML entity:
385 inputWithEntities = '<div> fo o </div>';
386 assert.equal(minify(inputWithEntities, { collapseWhitespace: true }), inputWithEntities);
388 // Non-breaking space passed as HTML entity, in decodeEntities:true mode:
389 assert.equal(minify(inputWithEntities, { collapseWhitespace: true, decodeEntities: true }), input);
391 // Do not remove hair space when preserving line breaks between tags:
392 input = '<p></p>\u200a\n<p></p>\n';
393 assert.equal(minify(input, { collapseWhitespace: true, preserveLineBreaks: true }), input);
395 // Preserve hair space in attributes:
396 input = '<p class="foo\u200abar"></p>';
397 assert.equal(minify(input, { collapseWhitespace: true }), input);
399 // Preserve hair space in class names when deduplicating and reordering:
400 input = '<a class="0 1\u200a3 2 3"></a>';
401 assert.equal(minify(input, { sortClassName: false }), input);
402 assert.equal(minify(input, { sortClassName: true }), input);
405 QUnit.test('doctype normalization', function(assert) {
407 var output = '<!doctype html>';
409 input = '<!DOCTYPE html>';
410 assert.equal(minify(input, { useShortDoctype: false }), input);
411 assert.equal(minify(input, { useShortDoctype: true }), output);
413 input = '<!DOCTYPE\nhtml>';
414 assert.equal(minify(input, { useShortDoctype: false }), '<!DOCTYPE html>');
415 assert.equal(minify(input, { useShortDoctype: true }), output);
417 input = '<!DOCTYPE\thtml>';
418 assert.equal(minify(input, { useShortDoctype: false }), input);
419 assert.equal(minify(input, { useShortDoctype: true }), output);
421 input = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n "http://www.w3.org/TR/html4/strict.dtd">';
422 assert.equal(minify(input, { useShortDoctype: false }), '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">');
423 assert.equal(minify(input, { useShortDoctype: true }), output);
426 QUnit.test('removing comments', function(assert) {
429 input = '<!-- test -->';
430 assert.equal(minify(input, { removeComments: true }), '');
432 input = '<!-- foo --><div>baz</div><!-- bar\n\n moo -->';
433 assert.equal(minify(input, { removeComments: true }), '<div>baz</div>');
434 assert.equal(minify(input, { removeComments: false }), input);
436 input = '<p title="<!-- comment in attribute -->">foo</p>';
437 assert.equal(minify(input, { removeComments: true }), input);
439 input = '<script><!-- alert(1) --></script>';
440 assert.equal(minify(input, { removeComments: true }), input);
442 input = '<STYLE><!-- alert(1) --></STYLE>';
443 assert.equal(minify(input, { removeComments: true }), '<style><!-- alert(1) --></style>');
446 QUnit.test('ignoring comments', function(assert) {
449 input = '<!--! test -->';
450 assert.equal(minify(input, { removeComments: true }), input);
451 assert.equal(minify(input, { removeComments: false }), input);
453 input = '<!--! foo --><div>baz</div><!--! bar\n\n moo -->';
454 assert.equal(minify(input, { removeComments: true }), input);
455 assert.equal(minify(input, { removeComments: false }), input);
457 input = '<!--! foo --><div>baz</div><!-- bar\n\n moo -->';
458 assert.equal(minify(input, { removeComments: true }), '<!--! foo --><div>baz</div>');
459 assert.equal(minify(input, { removeComments: false }), input);
461 input = '<!-- ! test -->';
462 assert.equal(minify(input, { removeComments: true }), '');
463 assert.equal(minify(input, { removeComments: false }), input);
465 input = '<div>\n\n \t<div><div>\n\n<p>\n\n<!--! \t\n\nbar\n\n moo --> \n\n</p>\n\n </div> </div></div>';
466 output = '<div><div><div><p><!--! \t\n\nbar\n\n moo --></p></div></div></div>';
467 assert.equal(minify(input, { removeComments: true }), input);
468 assert.equal(minify(input, { removeComments: true, collapseWhitespace: true }), output);
469 assert.equal(minify(input, { removeComments: false }), input);
470 assert.equal(minify(input, { removeComments: false, collapseWhitespace: true }), output);
472 input = '<p rel="<!-- comment in attribute -->" title="<!--! ignored comment in attribute -->">foo</p>';
473 assert.equal(minify(input, { removeComments: true }), input);
476 QUnit.test('conditional comments', function(assert) {
479 input = '<![if IE 5]>test<![endif]>';
480 assert.equal(minify(input, { removeComments: true }), input);
482 input = '<!--[if IE 6]>test<![endif]-->';
483 assert.equal(minify(input, { removeComments: true }), input);
485 input = '<!--[if IE 7]>-->test<!--<![endif]-->';
486 assert.equal(minify(input, { removeComments: true }), input);
488 input = '<!--[if IE 8]><!-->test<!--<![endif]-->';
489 assert.equal(minify(input, { removeComments: true }), input);
491 input = '<!--[if lt IE 5.5]>test<![endif]-->';
492 assert.equal(minify(input, { removeComments: true }), input);
494 input = '<!--[if (gt IE 5)&(lt IE 7)]>test<![endif]-->';
495 assert.equal(minify(input, { removeComments: true }), input);
499 ' <!--[if lte IE 8]>\n' +
500 ' <script type="text/javascript">\n' +
501 ' alert("ie8!");\n' +
508 output = '<head><!--[if lte IE 8]>\n' +
509 ' <script type="text/javascript">\n' +
510 ' alert("ie8!");\n' +
513 assert.equal(minify(input, {
515 removeComments: true,
516 collapseWhitespace: true,
517 removeOptionalTags: true,
518 removeScriptTypeAttributes: true
520 output = '<head><!--[if lte IE 8]><script>alert("ie8!")</script><![endif]-->';
521 assert.equal(minify(input, {
523 removeComments: true,
524 collapseWhitespace: true,
525 removeOptionalTags: true,
526 removeScriptTypeAttributes: true,
527 processConditionalComments: true
530 input = '<!DOCTYPE html>\n' +
531 '<html lang="en">\n' +
533 ' <meta http-equiv="X-UA-Compatible"\n' +
534 ' content="IE=edge,chrome=1">\n' +
535 ' <meta charset="utf-8">\n' +
536 ' <!--[if lt IE 7]><html class="no-js ie6"><![endif]-->\n' +
537 ' <!--[if IE 7]><html class="no-js ie7"><![endif]-->\n' +
538 ' <!--[if IE 8]><html class="no-js ie8"><![endif]-->\n' +
539 ' <!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]-->\n' +
541 ' <title>Document</title>\n' +
546 output = '<!DOCTYPE html>' +
549 '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">' +
550 '<meta charset="utf-8">' +
551 '<!--[if lt IE 7]><html class="no-js ie6"><![endif]-->' +
552 '<!--[if IE 7]><html class="no-js ie7"><![endif]-->' +
553 '<!--[if IE 8]><html class="no-js ie8"><![endif]-->' +
554 '<!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]-->' +
555 '<title>Document</title></head><body></body></html>';
556 assert.equal(minify(input, {
557 removeComments: true,
558 collapseWhitespace: true
560 assert.equal(minify(input, {
561 removeComments: true,
562 collapseWhitespace: true,
563 processConditionalComments: true
567 QUnit.test('collapsing space in conditional comments', function(assert) {
570 input = '<!--[if IE 7]>\n\n \t\n \t\t ' +
571 '<link rel="stylesheet" href="/css/ie7-fixes.css" type="text/css" />\n\t' +
573 assert.equal(minify(input, { removeComments: true }), input);
574 assert.equal(minify(input, { removeComments: true, collapseWhitespace: true }), input);
575 output = '<!--[if IE 7]>\n\n \t\n \t\t ' +
576 '<link rel="stylesheet" href="/css/ie7-fixes.css" type="text/css">\n\t' +
578 assert.equal(minify(input, { removeComments: true, processConditionalComments: true }), output);
579 output = '<!--[if IE 7]>' +
580 '<link rel="stylesheet" href="/css/ie7-fixes.css" type="text/css">' +
582 assert.equal(minify(input, {
583 removeComments: true,
584 collapseWhitespace: true,
585 processConditionalComments: true
588 input = '<!--[if lte IE 6]>\n \n \n\n\n\t' +
589 '<p title=" sigificant whitespace ">blah blah</p>' +
591 assert.equal(minify(input, { removeComments: true }), input);
592 assert.equal(minify(input, { removeComments: true, collapseWhitespace: true }), input);
593 output = '<!--[if lte IE 6]>' +
594 '<p title=" sigificant whitespace ">blah blah</p>' +
596 assert.equal(minify(input, {
597 removeComments: true,
598 collapseWhitespace: true,
599 processConditionalComments: true
603 QUnit.test('remove comments from scripts', function(assert) {
606 input = '<script><!--\nalert(1);\n--></script>';
607 assert.equal(minify(input), input);
608 output = '<script>alert(1)</script>';
609 assert.equal(minify(input, { minifyJS: true }), output);
611 input = '<script><!--alert(2);--></script>';
612 assert.equal(minify(input), input);
613 output = '<script></script>';
614 assert.equal(minify(input, { minifyJS: true }), output);
616 input = '<script><!--alert(3);\n--></script>';
617 assert.equal(minify(input), input);
618 output = '<script></script>';
619 assert.equal(minify(input, { minifyJS: true }), output);
621 input = '<script><!--\nalert(4);--></script>';
622 assert.equal(minify(input), input);
623 assert.equal(minify(input, { minifyJS: true }), input);
625 input = '<script><!--alert(5);\nalert(6);\nalert(7);--></script>';
626 assert.equal(minify(input), input);
627 assert.equal(minify(input, { minifyJS: true }), input);
629 input = '<script><!--alert(8)</script>';
630 assert.equal(minify(input), input);
631 output = '<script></script>';
632 assert.equal(minify(input, { minifyJS: true }), output);
634 input = '<script type="text/javascript"> \n <!--\nalert("-->"); -->\n\n </script>';
635 assert.equal(minify(input), input);
636 assert.equal(minify(input, { minifyJS: true }), input);
638 input = '<script type="text/javascript"> \n <!--\nalert("-->");\n -->\n\n </script>';
639 assert.equal(minify(input), input);
640 output = '<script type="text/javascript">alert("--\\x3e")</script>';
641 assert.equal(minify(input, { minifyJS: true }), output);
643 input = '<script> // <!-- \n alert(1) // --> </script>';
644 assert.equal(minify(input), input);
645 output = '<script>alert(1)</script>';
646 assert.equal(minify(input, { minifyJS: true }), output);
648 input = '<script type="text/html">\n<div>\n</div>\n<!-- aa -->\n</script>';
649 assert.equal(minify(input), input);
650 assert.equal(minify(input, { minifyJS: true }), input);
653 QUnit.test('remove comments from styles', function(assert) {
656 input = '<style><!--\np.a{background:red}\n--></style>';
657 assert.equal(minify(input), input);
658 output = '<style>p.a{background:red}</style>';
659 assert.equal(minify(input, { minifyCSS: true }), output);
661 input = '<style><!--p.b{background:red}--></style>';
662 assert.equal(minify(input), input);
663 output = '<style>p.b{background:red}</style>';
664 assert.equal(minify(input, { minifyCSS: true }), output);
666 input = '<style><!--p.c{background:red}\n--></style>';
667 assert.equal(minify(input), input);
668 output = '<style>p.c{background:red}</style>';
669 assert.equal(minify(input, { minifyCSS: true }), output);
671 input = '<style><!--\np.d{background:red}--></style>';
672 assert.equal(minify(input), input);
673 output = '<style>p.d{background:red}</style>';
674 assert.equal(minify(input, { minifyCSS: true }), output);
676 input = '<style><!--p.e{background:red}\np.f{background:red}\np.g{background:red}--></style>';
677 assert.equal(minify(input), input);
678 output = '<style>p.e{background:red}p.f{background:red}p.g{background:red}</style>';
679 assert.equal(minify(input, { minifyCSS: true }), output);
681 input = '<style>p.h{background:red}<!--\np.i{background:red}\n-->p.j{background:red}</style>';
682 assert.equal(minify(input), input);
683 output = '<style>p.h{background:red}p.i{background:red}p.j{background:red}</style>';
684 assert.equal(minify(input, { minifyCSS: true }), output);
686 input = '<style type="text/css"><!-- p { color: red } --></style>';
687 assert.equal(minify(input), input);
688 output = '<style type="text/css">p{color:red}</style>';
689 assert.equal(minify(input, { minifyCSS: true }), output);
691 input = '<style type="text/css">p::before { content: "<!--" }</style>';
692 assert.equal(minify(input), input);
693 output = '<style type="text/css">p::before{content:"<!--"}</style>';
694 assert.equal(minify(input, { minifyCSS: true }), output);
696 input = '<style type="text/html">\n<div>\n</div>\n<!-- aa -->\n</style>';
697 assert.equal(minify(input), input);
698 assert.equal(minify(input, { minifyCSS: true }), input);
701 QUnit.test('remove CDATA sections from scripts/styles', function(assert) {
704 input = '<script><![CDATA[\nalert(1)\n]]></script>';
705 assert.equal(minify(input), input);
706 assert.equal(minify(input, { minifyJS: true }), input);
708 input = '<script><![CDATA[alert(2)]]></script>';
709 assert.equal(minify(input), input);
710 assert.equal(minify(input, { minifyJS: true }), input);
712 input = '<script><![CDATA[alert(3)\n]]></script>';
713 assert.equal(minify(input), input);
714 assert.equal(minify(input, { minifyJS: true }), input);
716 input = '<script><![CDATA[\nalert(4)]]></script>';
717 assert.equal(minify(input), input);
718 assert.equal(minify(input, { minifyJS: true }), input);
720 input = '<script><![CDATA[alert(5)\nalert(6)\nalert(7)]]></script>';
721 assert.equal(minify(input), input);
722 assert.equal(minify(input, { minifyJS: true }), input);
724 input = '<script>/*<![CDATA[*/alert(8)/*]]>*/</script>';
725 assert.equal(minify(input), input);
726 output = '<script>alert(8)</script>';
727 assert.equal(minify(input, { minifyJS: true }), output);
729 input = '<script>//<![CDATA[\nalert(9)\n//]]></script>';
730 assert.equal(minify(input), input);
731 output = '<script>alert(9)</script>';
732 assert.equal(minify(input, { minifyJS: true }), output);
734 input = '<script type="text/javascript"> /* \n\t <![CDATA[ */ alert(10) /* ]]> */ \n </script>';
735 assert.equal(minify(input), input);
736 output = '<script type="text/javascript">alert(10)</script>';
737 assert.equal(minify(input, { minifyJS: true }), output);
739 input = '<script>\n\n//<![CDATA[\nalert(11)//]]></script>';
740 assert.equal(minify(input), input);
741 output = '<script>alert(11)</script>';
742 assert.equal(minify(input, { minifyJS: true }), output);
744 input = '<style><![CDATA[\np.a{background:red}\n]]></style>';
745 assert.equal(minify(input), input);
746 output = '<style></style>';
747 assert.equal(minify(input, { minifyCSS: true }), output);
749 input = '<style><![CDATA[p.b{background:red}]]></style>';
750 assert.equal(minify(input), input);
751 output = '<style></style>';
752 assert.equal(minify(input, { minifyCSS: true }), output);
754 input = '<style><![CDATA[p.c{background:red}\n]]></style>';
755 assert.equal(minify(input), input);
756 output = '<style></style>';
757 assert.equal(minify(input, { minifyCSS: true }), output);
759 input = '<style><![CDATA[\np.d{background:red}]]></style>';
760 assert.equal(minify(input), input);
761 output = '<style></style>';
762 assert.equal(minify(input, { minifyCSS: true }), output);
764 input = '<style><![CDATA[p.e{background:red}\np.f{background:red}\np.g{background:red}]]></style>';
765 assert.equal(minify(input), input);
766 output = '<style>p.f{background:red}p.g{background:red}</style>';
767 assert.equal(minify(input, { minifyCSS: true }), output);
769 input = '<style>p.h{background:red}<![CDATA[\np.i{background:red}\n]]>p.j{background:red}</style>';
770 assert.equal(minify(input), input);
771 output = '<style>p.h{background:red}]]>p.j{background:red}</style>';
772 assert.equal(minify(input, { minifyCSS: true }), output);
774 input = '<style>/* <![CDATA[ */p { color: red } // ]]></style>';
775 assert.equal(minify(input), input);
776 output = '<style>p{color:red}</style>';
777 assert.equal(minify(input, { minifyCSS: true }), output);
779 input = '<style type="text/html">\n<div>\n</div>\n<![CDATA[ aa ]]>\n</style>';
780 assert.equal(minify(input), input);
781 assert.equal(minify(input, { minifyCSS: true }), input);
784 QUnit.test('custom processors', function(assert) {
787 function css(text, type) {
788 return (type || 'Normal') + ' CSS';
791 input = '<style>\n.foo { font: 12pt "bar" } </style>';
792 assert.equal(minify(input), input);
793 assert.equal(minify(input, { minifyCSS: null }), input);
794 assert.equal(minify(input, { minifyCSS: false }), input);
795 output = '<style>Normal CSS</style>';
796 assert.equal(minify(input, { minifyCSS: css }), output);
798 input = '<p style="font: 12pt \'bar\'"></p>';
799 assert.equal(minify(input), input);
800 assert.equal(minify(input, { minifyCSS: null }), input);
801 assert.equal(minify(input, { minifyCSS: false }), input);
802 output = '<p style="inline CSS"></p>';
803 assert.equal(minify(input, { minifyCSS: css }), output);
805 input = '<link rel="stylesheet" href="css/style-mobile.css" media="(max-width: 737px)">';
806 assert.equal(minify(input), input);
807 assert.equal(minify(input, { minifyCSS: null }), input);
808 assert.equal(minify(input, { minifyCSS: false }), input);
809 output = '<link rel="stylesheet" href="css/style-mobile.css" media="media CSS">';
810 assert.equal(minify(input, { minifyCSS: css }), output);
812 input = '<style media="(max-width: 737px)"></style>';
813 assert.equal(minify(input), input);
814 assert.equal(minify(input, { minifyCSS: null }), input);
815 assert.equal(minify(input, { minifyCSS: false }), input);
816 output = '<style media="media CSS">Normal CSS</style>';
817 assert.equal(minify(input, { minifyCSS: css }), output);
819 function js(text, inline) {
820 return inline ? 'Inline JS' : 'Normal JS';
823 input = '<script>\nalert(1); </script>';
824 assert.equal(minify(input), input);
825 assert.equal(minify(input, { minifyJS: null }), input);
826 assert.equal(minify(input, { minifyJS: false }), input);
827 output = '<script>Normal JS</script>';
828 assert.equal(minify(input, { minifyJS: js }), output);
830 input = '<p onload="alert(1);"></p>';
831 assert.equal(minify(input), input);
832 assert.equal(minify(input, { minifyJS: null }), input);
833 assert.equal(minify(input, { minifyJS: false }), input);
834 output = '<p onload="Inline JS"></p>';
835 assert.equal(minify(input, { minifyJS: js }), output);
841 input = '<a href="http://site.com/foo">bar</a>';
842 assert.equal(minify(input), input);
843 assert.equal(minify(input, { minifyURLs: null }), input);
844 assert.equal(minify(input, { minifyURLs: false }), input);
845 output = '<a href="URL">bar</a>';
846 assert.equal(minify(input, { minifyURLs: url }), output);
848 input = '<style>\n.foo { background: url("http://site.com/foo") } </style>';
849 assert.equal(minify(input), input);
850 assert.equal(minify(input, { minifyURLs: null }), input);
851 assert.equal(minify(input, { minifyURLs: false }), input);
852 assert.equal(minify(input, { minifyURLs: url }), input);
853 output = '<style>.foo{background:url(URL)}</style>';
854 assert.equal(minify(input, { minifyCSS: true, minifyURLs: url }), output);
857 QUnit.test('empty attributes', function(assert) {
860 input = '<p id="" class="" STYLE=" " title="\n" lang="" dir="">x</p>';
861 assert.equal(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
863 input = '<p onclick="" ondblclick=" " onmousedown="" ONMOUSEUP="" onmouseover=" " onmousemove="" onmouseout="" ' +
864 'onkeypress=\n\n "\n " onkeydown=\n"" onkeyup\n="">x</p>';
865 assert.equal(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
867 input = '<input onfocus="" onblur="" onchange=" " value=" boo ">';
868 assert.equal(minify(input, { removeEmptyAttributes: true }), '<input value=" boo ">');
870 input = '<input value="" name="foo">';
871 assert.equal(minify(input, { removeEmptyAttributes: true }), '<input name="foo">');
873 input = '<img src="" alt="">';
874 assert.equal(minify(input, { removeEmptyAttributes: true }), '<img src="" alt="">');
876 // preserve unrecognized attribute
877 // remove recognized attrs with unspecified values
878 input = '<div data-foo class id style title lang dir onfocus onblur onchange onclick ondblclick onmousedown onmouseup onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup></div>';
879 assert.equal(minify(input, { removeEmptyAttributes: true }), '<div data-foo></div>');
881 // additional remove attributes
882 input = '<img src="" alt="">';
883 assert.equal(minify(input, { removeEmptyAttributes: function(attrName, tag) { return tag === 'img' && attrName === 'src'; } }), '<img alt="">');
886 QUnit.test('cleaning class/style attributes', function(assert) {
889 input = '<p class=" foo bar ">foo bar baz</p>';
890 assert.equal(minify(input), '<p class="foo bar">foo bar baz</p>');
892 input = '<p class=" foo ">foo bar baz</p>';
893 assert.equal(minify(input), '<p class="foo">foo bar baz</p>');
894 assert.equal(minify(input, { removeAttributeQuotes: true }), '<p class=foo>foo bar baz</p>');
896 input = '<p class="\n \n foo \n\n\t \t\n ">foo bar baz</p>';
897 output = '<p class="foo">foo bar baz</p>';
898 assert.equal(minify(input), output);
900 input = '<p class="\n \n foo \n\n\t \t\n class1 class-23 ">foo bar baz</p>';
901 output = '<p class="foo class1 class-23">foo bar baz</p>';
902 assert.equal(minify(input), output);
904 input = '<p style=" color: red; background-color: rgb(100, 75, 200); "></p>';
905 output = '<p style="color: red; background-color: rgb(100, 75, 200);"></p>';
906 assert.equal(minify(input), output);
908 input = '<p style="font-weight: bold ; "></p>';
909 output = '<p style="font-weight: bold;"></p>';
910 assert.equal(minify(input), output);
913 QUnit.test('cleaning URI-based attributes', function(assert) {
916 input = '<a href=" http://example.com ">x</a>';
917 output = '<a href="http://example.com">x</a>';
918 assert.equal(minify(input), output);
920 input = '<a href=" \t\t \n \t ">x</a>';
921 output = '<a href="">x</a>';
922 assert.equal(minify(input), output);
924 input = '<img src=" http://example.com " title="bleh " longdesc=" http://example.com/longdesc \n\n \t ">';
925 output = '<img src="http://example.com" title="bleh " longdesc="http://example.com/longdesc">';
926 assert.equal(minify(input), output);
928 input = '<img src="" usemap=" http://example.com ">';
929 output = '<img src="" usemap="http://example.com">';
930 assert.equal(minify(input), output);
932 input = '<form action=" somePath/someSubPath/someAction?foo=bar&baz=qux "></form>';
933 output = '<form action="somePath/someSubPath/someAction?foo=bar&baz=qux"></form>';
934 assert.equal(minify(input), output);
936 input = '<BLOCKQUOTE cite=" \n\n\n http://www.mycom.com/tolkien/twotowers.html "><P>foobar</P></BLOCKQUOTE>';
937 output = '<blockquote cite="http://www.mycom.com/tolkien/twotowers.html"><p>foobar</p></blockquote>';
938 assert.equal(minify(input), output);
940 input = '<head profile=" http://gmpg.org/xfn/11 "></head>';
941 output = '<head profile="http://gmpg.org/xfn/11"></head>';
942 assert.equal(minify(input), output);
944 input = '<object codebase=" http://example.com "></object>';
945 output = '<object codebase="http://example.com"></object>';
946 assert.equal(minify(input), output);
948 input = '<span profile=" 1, 2, 3 ">foo</span>';
949 assert.equal(minify(input), input);
951 input = '<div action=" foo-bar-baz ">blah</div>';
952 assert.equal(minify(input), input);
955 QUnit.test('cleaning Number-based attributes', function(assert) {
958 input = '<a href="#" tabindex=" 1 ">x</a><button tabindex=" 2 ">y</button>';
959 output = '<a href="#" tabindex="1">x</a><button tabindex="2">y</button>';
960 assert.equal(minify(input), output);
962 input = '<input value="" maxlength=" 5 ">';
963 output = '<input value="" maxlength="5">';
964 assert.equal(minify(input), output);
966 input = '<select size=" 10 \t\t "><option>x</option></select>';
967 output = '<select size="10"><option>x</option></select>';
968 assert.equal(minify(input), output);
970 input = '<textarea rows=" 20 " cols=" 30 "></textarea>';
971 output = '<textarea rows="20" cols="30"></textarea>';
972 assert.equal(minify(input), output);
974 input = '<COLGROUP span=" 40 "><COL span=" 39 "></COLGROUP>';
975 output = '<colgroup span="40"><col span="39"></colgroup>';
976 assert.equal(minify(input), output);
978 input = '<tr><td colspan=" 2 ">x</td><td rowspan=" 3 "></td></tr>';
979 output = '<tr><td colspan="2">x</td><td rowspan="3"></td></tr>';
980 assert.equal(minify(input), output);
983 QUnit.test('cleaning other attributes', function(assert) {
986 input = '<a href="#" onclick=" window.prompt(\'boo\'); " onmouseover=" \n\n alert(123) \t \n\t ">blah</a>';
987 output = '<a href="#" onclick="window.prompt(\'boo\');" onmouseover="alert(123)">blah</a>';
988 assert.equal(minify(input), output);
990 input = '<body onload=" foo(); bar() ; "><p>x</body>';
991 output = '<body onload="foo(); bar() ;"><p>x</p></body>';
992 assert.equal(minify(input), output);
995 QUnit.test('removing redundant attributes (<form method="get" ...>)', function(assert) {
998 input = '<form method="get">hello world</form>';
999 assert.equal(minify(input, { removeRedundantAttributes: true }), '<form>hello world</form>');
1001 input = '<form method="post">hello world</form>';
1002 assert.equal(minify(input, { removeRedundantAttributes: true }), '<form method="post">hello world</form>');
1005 QUnit.test('removing redundant attributes (<input type="text" ...>)', function(assert) {
1008 input = '<input type="text">';
1009 assert.equal(minify(input, { removeRedundantAttributes: true }), '<input>');
1011 input = '<input type=" TEXT " value="foo">';
1012 assert.equal(minify(input, { removeRedundantAttributes: true }), '<input value="foo">');
1014 input = '<input type="checkbox">';
1015 assert.equal(minify(input, { removeRedundantAttributes: true }), '<input type="checkbox">');
1018 QUnit.test('removing redundant attributes (<a name="..." id="..." ...>)', function(assert) {
1021 input = '<a id="foo" name="foo">blah</a>';
1022 assert.equal(minify(input, { removeRedundantAttributes: true }), '<a id="foo">blah</a>');
1024 input = '<input id="foo" name="foo">';
1025 assert.equal(minify(input, { removeRedundantAttributes: true }), input);
1027 input = '<a name="foo">blah</a>';
1028 assert.equal(minify(input, { removeRedundantAttributes: true }), input);
1030 input = '<a href="..." name=" bar " id="bar" >blah</a>';
1031 assert.equal(minify(input, { removeRedundantAttributes: true }), '<a href="..." id="bar">blah</a>');
1034 QUnit.test('removing redundant attributes (<script src="..." charset="...">)', function(assert) {
1037 input = '<script type="text/javascript" charset="UTF-8">alert(222);</script>';
1038 output = '<script type="text/javascript">alert(222);</script>';
1039 assert.equal(minify(input, { removeRedundantAttributes: true }), output);
1041 input = '<script type="text/javascript" src="http://example.com" charset="UTF-8">alert(222);</script>';
1042 assert.equal(minify(input, { removeRedundantAttributes: true }), input);
1044 input = '<script CHARSET=" ... ">alert(222);</script>';
1045 output = '<script>alert(222);</script>';
1046 assert.equal(minify(input, { removeRedundantAttributes: true }), output);
1049 QUnit.test('removing redundant attributes (<... language="javascript" ...>)', function(assert) {
1052 input = '<script language="Javascript">x=2,y=4</script>';
1053 assert.equal(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4</script>');
1055 input = '<script LANGUAGE = " javaScript ">x=2,y=4</script>';
1056 assert.equal(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4</script>');
1059 QUnit.test('removing redundant attributes (<area shape="rect" ...>)', function(assert) {
1060 var input = '<area shape="rect" coords="696,25,958,47" href="#" title="foo">';
1061 var output = '<area coords="696,25,958,47" href="#" title="foo">';
1062 assert.equal(minify(input, { removeRedundantAttributes: true }), output);
1065 QUnit.test('removing redundant attributes (<... = "javascript: ..." ...>)', function(assert) {
1068 input = '<p onclick="javascript:alert(1)">x</p>';
1069 assert.equal(minify(input), '<p onclick="alert(1)">x</p>');
1071 input = '<p onclick="javascript:x">x</p>';
1072 assert.equal(minify(input, { removeAttributeQuotes: true }), '<p onclick=x>x</p>');
1074 input = '<p onclick=" JavaScript: x">x</p>';
1075 assert.equal(minify(input), '<p onclick="x">x</p>');
1077 input = '<p title="javascript:(function() { /* some stuff here */ })()">x</p>';
1078 assert.equal(minify(input), input);
1081 QUnit.test('removing javascript type attributes', function(assert) {
1084 input = '<script type="">alert(1)</script>';
1085 assert.equal(minify(input, { removeScriptTypeAttributes: false }), input);
1086 output = '<script>alert(1)</script>';
1087 assert.equal(minify(input, { removeScriptTypeAttributes: true }), output);
1089 input = '<script type="text/javascript">alert(1)</script>';
1090 assert.equal(minify(input, { removeScriptTypeAttributes: false }), input);
1091 output = '<script>alert(1)</script>';
1092 assert.equal(minify(input, { removeScriptTypeAttributes: true }), output);
1094 input = '<SCRIPT TYPE=" text/javascript ">alert(1)</script>';
1095 output = '<script>alert(1)</script>';
1096 assert.equal(minify(input, { removeScriptTypeAttributes: true }), output);
1098 input = '<script type="application/javascript;version=1.8">alert(1)</script>';
1099 output = '<script>alert(1)</script>';
1100 assert.equal(minify(input, { removeScriptTypeAttributes: true }), output);
1102 input = '<script type="text/vbscript">MsgBox("foo bar")</script>';
1103 output = '<script type="text/vbscript">MsgBox("foo bar")</script>';
1104 assert.equal(minify(input, { removeScriptTypeAttributes: true }), output);
1107 QUnit.test('removing type="text/css" attributes', function(assert) {
1110 input = '<style type="">.foo { color: red }</style>';
1111 assert.equal(minify(input, { removeStyleLinkTypeAttributes: false }), input);
1112 output = '<style>.foo { color: red }</style>';
1113 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
1115 input = '<style type="text/css">.foo { color: red }</style>';
1116 assert.equal(minify(input, { removeStyleLinkTypeAttributes: false }), input);
1117 output = '<style>.foo { color: red }</style>';
1118 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
1120 input = '<STYLE TYPE = " text/CSS ">body { font-size: 1.75em }</style>';
1121 output = '<style>body { font-size: 1.75em }</style>';
1122 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
1124 input = '<style type="text/plain">.foo { background: green }</style>';
1125 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), input);
1127 input = '<link rel="stylesheet" type="text/css" href="http://example.com">';
1128 output = '<link rel="stylesheet" href="http://example.com">';
1129 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
1131 input = '<link rel="alternate" type="application/atom+xml" href="data.xml">';
1132 assert.equal(minify(input, { removeStyleLinkTypeAttributes: true }), input);
1135 QUnit.test('removing attribute quotes', function(assert) {
1138 input = '<p title="blah" class="a23B-foo.bar_baz:qux" id="moo">foo</p>';
1139 assert.equal(minify(input, { removeAttributeQuotes: true }), '<p title=blah class=a23B-foo.bar_baz:qux id=moo>foo</p>');
1141 input = '<input value="hello world">';
1142 assert.equal(minify(input, { removeAttributeQuotes: true }), '<input value="hello world">');
1144 input = '<a href="#" title="foo#bar">x</a>';
1145 assert.equal(minify(input, { removeAttributeQuotes: true }), '<a href=# title=foo#bar>x</a>');
1147 input = '<a href="http://example.com/" title="blah">\nfoo\n\n</a>';
1148 assert.equal(minify(input, { removeAttributeQuotes: true }), '<a href=http://example.com/ title=blah>\nfoo\n\n</a>');
1150 input = '<a title="blah" href="http://example.com/">\nfoo\n\n</a>';
1151 assert.equal(minify(input, { removeAttributeQuotes: true }), '<a title=blah href=http://example.com/ >\nfoo\n\n</a>');
1153 input = '<a href="http://example.com/" title="">\nfoo\n\n</a>';
1154 assert.equal(minify(input, { removeAttributeQuotes: true, removeEmptyAttributes: true }), '<a href=http://example.com/ >\nfoo\n\n</a>');
1156 input = '<p class=foo|bar:baz></p>';
1157 assert.equal(minify(input, { removeAttributeQuotes: true }), '<p class=foo|bar:baz></p>');
1160 QUnit.test('preserving custom attribute-wrapping markup', function(assert) {
1161 var input, customAttrOptions;
1163 // With a single rule
1164 customAttrOptions = {
1165 customAttrSurround: [[/\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/]]
1168 input = '<input {{#if value}}checked="checked"{{/if}}>';
1169 assert.equal(minify(input, customAttrOptions), input);
1171 input = '<input checked="checked">';
1172 assert.equal(minify(input, customAttrOptions), input);
1174 // With multiple rules
1175 customAttrOptions = {
1176 customAttrSurround: [
1177 [/\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/],
1178 [/\{\{#unless\s+\w+\}\}/, /\{\{\/unless\}\}/]
1182 input = '<input {{#if value}}checked="checked"{{/if}}>';
1183 assert.equal(minify(input, customAttrOptions), input);
1185 input = '<input {{#unless value}}checked="checked"{{/unless}}>';
1186 assert.equal(minify(input, customAttrOptions), input);
1188 input = '<input {{#if value1}}data-attr="example" {{/if}}{{#unless value2}}checked="checked"{{/unless}}>';
1189 assert.equal(minify(input, customAttrOptions), input);
1191 input = '<input checked="checked">';
1192 assert.equal(minify(input, customAttrOptions), input);
1194 // With multiple rules and richer options
1195 customAttrOptions = {
1196 customAttrSurround: [
1197 [/\{\{#if\s+\w+\}\}/, /\{\{\/if\}\}/],
1198 [/\{\{#unless\s+\w+\}\}/, /\{\{\/unless\}\}/]
1200 collapseBooleanAttributes: true,
1201 removeAttributeQuotes: true
1204 input = '<input {{#if value}}checked="checked"{{/if}}>';
1205 assert.equal(minify(input, customAttrOptions), '<input {{#if value}}checked{{/if}}>');
1207 input = '<input {{#if value1}}checked="checked"{{/if}} {{#if value2}}data-attr="foo"{{/if}}/>';
1208 assert.equal(minify(input, customAttrOptions), '<input {{#if value1}}checked {{/if}}{{#if value2}}data-attr=foo{{/if}}>');
1210 customAttrOptions.keepClosingSlash = true;
1211 assert.equal(minify(input, customAttrOptions), '<input {{#if value1}}checked {{/if}}{{#if value2}}data-attr=foo {{/if}}/>');
1214 QUnit.test('preserving custom attribute-joining markup', function(assert) {
1216 var polymerConditionalAttributeJoin = /\?=/;
1217 var customAttrOptions = {
1218 customAttrAssign: [polymerConditionalAttributeJoin]
1220 input = '<div flex?="{{mode != cover}}"></div>';
1221 assert.equal(minify(input, customAttrOptions), input);
1222 input = '<div flex?="{{mode != cover}}" class="foo"></div>';
1223 assert.equal(minify(input, customAttrOptions), input);
1226 QUnit.test('collapsing whitespace', function(assert) {
1229 input = '<script type="text/javascript"> \n\t alert(1) \n\n\n \t </script>';
1230 output = '<script type="text/javascript">alert(1)</script>';
1231 assert.equal(minify(input, { collapseWhitespace: true }), output);
1233 input = '<p>foo</p> <p> bar</p>\n\n \n\t\t <div title="quz">baz </div>';
1234 output = '<p>foo</p><p>bar</p><div title="quz">baz</div>';
1235 assert.equal(minify(input, { collapseWhitespace: true }), output);
1237 input = '<p> foo bar</p>';
1238 output = '<p>foo bar</p>';
1239 assert.equal(minify(input, { collapseWhitespace: true }), output);
1241 input = '<p>foo\nbar</p>';
1242 output = '<p>foo bar</p>';
1243 assert.equal(minify(input, { collapseWhitespace: true }), output);
1245 input = '<p> foo <span> blah <i> 22</i> </span> bar <img src=""></p>';
1246 output = '<p>foo <span>blah <i>22</i> </span>bar <img src=""></p>';
1247 assert.equal(minify(input, { collapseWhitespace: true }), output);
1249 input = '<textarea> foo bar baz \n\n x \t y </textarea>';
1250 output = '<textarea> foo bar baz \n\n x \t y </textarea>';
1251 assert.equal(minify(input, { collapseWhitespace: true }), output);
1253 input = '<div><textarea></textarea> </div>';
1254 output = '<div><textarea></textarea></div>';
1255 assert.equal(minify(input, { collapseWhitespace: true }), output);
1257 input = '<div><pRe> $foo = "baz"; </pRe> </div>';
1258 output = '<div><pre> $foo = "baz"; </pre></div>';
1259 assert.equal(minify(input, { collapseWhitespace: true }), output);
1260 output = '<div><pRe>$foo = "baz";</pRe></div>';
1261 assert.equal(minify(input, { collapseWhitespace: true, caseSensitive: true }), output);
1263 input = '<script type="text/javascript">var = "hello";</script>\r\n\r\n\r\n' +
1264 '<style type="text/css">#foo { color: red; } </style>\r\n\r\n\r\n' +
1265 '<div>\r\n <div>\r\n <div><!-- hello -->\r\n <div>' +
1266 '<!--! hello -->\r\n <div>\r\n <div class="">\r\n\r\n ' +
1267 '<textarea disabled="disabled"> this is a textarea </textarea>\r\n ' +
1268 '</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>' +
1269 '<pre> \r\nxxxx</pre><span>x</span> <span>Hello</span> <b>billy</b> \r\n' +
1270 '<input type="text">\r\n<textarea></textarea>\r\n<pre></pre>';
1271 output = '<script type="text/javascript">var = "hello";</script>' +
1272 '<style type="text/css">#foo { color: red; }</style>' +
1274 '<!-- hello --><div><!--! hello --><div><div class="">' +
1275 '<textarea disabled="disabled"> this is a textarea </textarea>' +
1276 '</div></div></div></div></div></div>' +
1277 '<pre> \r\nxxxx</pre><span>x</span> <span>Hello</span> <b>billy</b> ' +
1278 '<input type="text"> <textarea></textarea><pre></pre>';
1279 assert.equal(minify(input, { collapseWhitespace: true }), output);
1281 input = '<pre title="some title..."> hello world </pre>';
1282 output = '<pre title="some title..."> hello world </pre>';
1283 assert.equal(minify(input, { collapseWhitespace: true }), output);
1285 input = '<pre title="some title..."><code> hello world </code></pre>';
1286 output = '<pre title="some title..."><code> hello world </code></pre>';
1287 assert.equal(minify(input, { collapseWhitespace: true }), output);
1289 input = '<script>alert("foo bar") </script>';
1290 output = '<script>alert("foo bar")</script>';
1291 assert.equal(minify(input, { collapseWhitespace: true }), output);
1293 input = '<style>alert("foo bar") </style>';
1294 output = '<style>alert("foo bar")</style>';
1295 assert.equal(minify(input, { collapseWhitespace: true }), output);
1298 QUnit.test('removing empty elements', function(assert) {
1301 assert.equal(minify('<p>x</p>', { removeEmptyElements: true }), '<p>x</p>');
1302 assert.equal(minify('<p></p>', { removeEmptyElements: true }), '');
1304 input = '<p>foo<span>bar</span><span></span></p>';
1305 output = '<p>foo<span>bar</span></p>';
1306 assert.equal(minify(input, { removeEmptyElements: true }), output);
1308 input = '<a href="http://example/com" title="hello world"></a>';
1310 assert.equal(minify(input, { removeEmptyElements: true }), output);
1312 input = '<iframe></iframe>';
1314 assert.equal(minify(input, { removeEmptyElements: true }), output);
1316 input = '<iframe src="page.html"></iframe>';
1317 assert.equal(minify(input, { removeEmptyElements: true }), input);
1319 input = '<iframe srcdoc="<h1>Foo</h1>"></iframe>';
1320 assert.equal(minify(input, { removeEmptyElements: true }), input);
1322 input = '<video></video>';
1324 assert.equal(minify(input, { removeEmptyElements: true }), output);
1326 input = '<video src="preview.ogg"></video>';
1327 assert.equal(minify(input, { removeEmptyElements: true }), input);
1329 input = '<audio autoplay></audio>';
1331 assert.equal(minify(input, { removeEmptyElements: true }), output);
1333 input = '<audio src="startup.mp3" autoplay></audio>';
1334 assert.equal(minify(input, { removeEmptyElements: true }), input);
1336 input = '<object type="application/x-shockwave-flash"></object>';
1338 assert.equal(minify(input, { removeEmptyElements: true }), output);
1340 input = '<object data="game.swf" type="application/x-shockwave-flash"></object>';
1341 assert.equal(minify(input, { removeEmptyElements: true }), input);
1343 input = '<applet archive="game.zip" width="250" height="150"></applet>';
1345 assert.equal(minify(input, { removeEmptyElements: true }), output);
1347 input = '<applet code="game.class" archive="game.zip" width="250" height="150"></applet>';
1348 assert.equal(minify(input, { removeEmptyElements: true }), input);
1350 input = '<textarea cols="10" rows="10"></textarea>';
1351 assert.equal(minify(input, { removeEmptyElements: true }), input);
1353 input = '<div>hello<span>world</span></div>';
1354 assert.equal(minify(input, { removeEmptyElements: true }), input);
1356 input = '<p>x<span title="<" class="blah-moo"></span></p>';
1357 output = '<p>x</p>';
1358 assert.equal(minify(input, { removeEmptyElements: true }), output);
1360 input = '<div>x<div>y <div>blah</div><div></div>foo</div>z</div>';
1361 output = '<div>x<div>y <div>blah</div>foo</div>z</div>';
1362 assert.equal(minify(input, { removeEmptyElements: true }), output);
1364 input = '<img src="">';
1365 assert.equal(minify(input, { removeEmptyElements: true }), input);
1367 input = '<p><!-- x --></p>';
1369 assert.equal(minify(input, { removeEmptyElements: true }), output);
1371 input = '<script src="foo.js"></script>';
1372 assert.equal(minify(input, { removeEmptyElements: true }), input);
1373 input = '<script></script>';
1374 assert.equal(minify(input, { removeEmptyElements: true }), '');
1376 input = '<div>after<span></span> </div>';
1377 output = '<div>after </div>';
1378 assert.equal(minify(input, { removeEmptyElements: true }), output);
1379 output = '<div>after</div>';
1380 assert.equal(minify(input, { collapseWhitespace: true, removeEmptyElements: true }), output);
1382 input = '<div>before <span></span></div>';
1383 output = '<div>before </div>';
1384 assert.equal(minify(input, { removeEmptyElements: true }), output);
1385 output = '<div>before</div>';
1386 assert.equal(minify(input, { collapseWhitespace: true, removeEmptyElements: true }), output);
1388 input = '<div>both <span></span> </div>';
1389 output = '<div>both </div>';
1390 assert.equal(minify(input, { removeEmptyElements: true }), output);
1391 output = '<div>both</div>';
1392 assert.equal(minify(input, { collapseWhitespace: true, removeEmptyElements: true }), output);
1394 input = '<div>unary <span></span><link></div>';
1395 output = '<div>unary <link></div>';
1396 assert.equal(minify(input, { removeEmptyElements: true }), output);
1397 output = '<div>unary<link></div>';
1398 assert.equal(minify(input, { collapseWhitespace: true, removeEmptyElements: true }), output);
1400 input = '<div>Empty <!-- NOT --> </div>';
1401 assert.equal(minify(input, { removeEmptyElements: true }), input);
1402 output = '<div>Empty<!-- NOT --></div>';
1403 assert.equal(minify(input, { collapseWhitespace: true, removeEmptyElements: true }), output);
1406 QUnit.test('collapsing boolean attributes', function(assert) {
1409 input = '<input disabled="disabled">';
1410 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<input disabled>');
1412 input = '<input CHECKED = "checked" readonly="readonly">';
1413 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<input checked readonly>');
1415 input = '<option name="blah" selected="selected">moo</option>';
1416 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<option name="blah" selected>moo</option>');
1418 input = '<input autofocus="autofocus">';
1419 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<input autofocus>');
1421 input = '<input required="required">';
1422 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<input required>');
1424 input = '<input multiple="multiple">';
1425 assert.equal(minify(input, { collapseBooleanAttributes: true }), '<input multiple>');
1427 input = '<div Allowfullscreen=foo Async=foo Autofocus=foo Autoplay=foo Checked=foo Compact=foo Controls=foo ' +
1428 'Declare=foo Default=foo Defaultchecked=foo Defaultmuted=foo Defaultselected=foo Defer=foo Disabled=foo ' +
1429 'Enabled=foo Formnovalidate=foo Hidden=foo Indeterminate=foo Inert=foo Ismap=foo Itemscope=foo ' +
1430 'Loop=foo Multiple=foo Muted=foo Nohref=foo Noresize=foo Noshade=foo Novalidate=foo Nowrap=foo Open=foo ' +
1431 'Pauseonexit=foo Readonly=foo Required=foo Reversed=foo Scoped=foo Seamless=foo Selected=foo Sortable=foo ' +
1432 'Truespeed=foo Typemustmatch=foo Visible=foo></div>';
1433 output = '<div allowfullscreen async autofocus autoplay checked compact controls declare default defaultchecked ' +
1434 'defaultmuted defaultselected defer disabled enabled formnovalidate hidden indeterminate inert ' +
1435 'ismap itemscope loop multiple muted nohref noresize noshade novalidate nowrap open pauseonexit readonly ' +
1436 'required reversed scoped seamless selected sortable truespeed typemustmatch visible></div>';
1437 assert.equal(minify(input, { collapseBooleanAttributes: true }), output);
1438 output = '<div Allowfullscreen Async Autofocus Autoplay Checked Compact Controls Declare Default Defaultchecked ' +
1439 'Defaultmuted Defaultselected Defer Disabled Enabled Formnovalidate Hidden Indeterminate Inert ' +
1440 'Ismap Itemscope Loop Multiple Muted Nohref Noresize Noshade Novalidate Nowrap Open Pauseonexit Readonly ' +
1441 'Required Reversed Scoped Seamless Selected Sortable Truespeed Typemustmatch Visible></div>';
1442 assert.equal(minify(input, { collapseBooleanAttributes: true, caseSensitive: true }), output);
1445 QUnit.test('collapsing enumerated attributes', function(assert) {
1446 assert.equal(minify('<div draggable="auto"></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1447 assert.equal(minify('<div draggable="true"></div>', { collapseBooleanAttributes: true }), '<div draggable="true"></div>');
1448 assert.equal(minify('<div draggable="false"></div>', { collapseBooleanAttributes: true }), '<div draggable="false"></div>');
1449 assert.equal(minify('<div draggable="foo"></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1450 assert.equal(minify('<div draggable></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1451 assert.equal(minify('<div Draggable="auto"></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1452 assert.equal(minify('<div Draggable="true"></div>', { collapseBooleanAttributes: true }), '<div draggable="true"></div>');
1453 assert.equal(minify('<div Draggable="false"></div>', { collapseBooleanAttributes: true }), '<div draggable="false"></div>');
1454 assert.equal(minify('<div Draggable="foo"></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1455 assert.equal(minify('<div Draggable></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1456 assert.equal(minify('<div draggable="Auto"></div>', { collapseBooleanAttributes: true }), '<div draggable></div>');
1459 QUnit.test('keeping trailing slashes in tags', function(assert) {
1460 assert.equal(minify('<img src="test"/>', { keepClosingSlash: true }), '<img src="test"/>');
1461 // https://github.com/kangax/html-minifier/issues/233
1462 assert.equal(minify('<img src="test"/>', { keepClosingSlash: true, removeAttributeQuotes: true }), '<img src=test />');
1463 assert.equal(minify('<img src="test" id=""/>', { keepClosingSlash: true, removeAttributeQuotes: true, removeEmptyAttributes: true }), '<img src=test />');
1464 assert.equal(minify('<img title="foo" src="test"/>', { keepClosingSlash: true, removeAttributeQuotes: true }), '<img title=foo src=test />');
1467 QUnit.test('removing optional tags', function(assert) {
1471 assert.equal(minify(input, { removeOptionalTags: true }), input);
1475 assert.equal(minify(input, { removeOptionalTags: true }), output);
1477 input = '<body></body>';
1479 assert.equal(minify(input, { removeOptionalTags: true }), output);
1480 assert.equal(minify(input, { removeOptionalTags: true, removeEmptyElements: true }), output);
1482 input = '<html><head></head><body></body></html>';
1484 assert.equal(minify(input, { removeOptionalTags: true }), output);
1485 assert.equal(minify(input, { removeOptionalTags: true, removeEmptyElements: true }), output);
1487 input = ' <html></html>';
1489 assert.equal(minify(input, { removeOptionalTags: true }), output);
1491 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1493 input = '<html> </html>';
1495 assert.equal(minify(input, { removeOptionalTags: true }), output);
1497 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1499 input = '<html></html> ';
1501 assert.equal(minify(input, { removeOptionalTags: true }), output);
1503 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1505 input = ' <html><body></body></html>';
1507 assert.equal(minify(input, { removeOptionalTags: true }), output);
1509 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1511 input = '<html> <body></body></html>';
1513 assert.equal(minify(input, { removeOptionalTags: true }), output);
1515 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1517 input = '<html><body> </body></html>';
1519 assert.equal(minify(input, { removeOptionalTags: true }), output);
1521 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1523 input = '<html><body></body> </html>';
1525 assert.equal(minify(input, { removeOptionalTags: true }), output);
1527 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1529 input = '<html><body></body></html> ';
1531 assert.equal(minify(input, { removeOptionalTags: true }), output);
1533 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1535 input = '<html><head><title>hello</title></head><body><p>foo<span>bar</span></p></body></html>';
1536 assert.equal(minify(input), input);
1537 output = '<title>hello</title><p>foo<span>bar</span>';
1538 assert.equal(minify(input, { removeOptionalTags: true }), output);
1540 input = '<html lang=""><head><title>hello</title></head><body style=""><p>foo<span>bar</span></p></body></html>';
1541 output = '<html lang=""><title>hello</title><body style=""><p>foo<span>bar</span>';
1542 assert.equal(minify(input, { removeOptionalTags: true }), output);
1543 output = '<title>hello</title><p>foo<span>bar</span>';
1544 assert.equal(minify(input, { removeOptionalTags: true, removeEmptyAttributes: true }), output);
1546 input = '<html><head><title>a</title><link href="b.css" rel="stylesheet"/></head><body><a href="c.html"></a><div class="d"><input value="e"/></div></body></html>';
1547 output = '<title>a</title><link href="b.css" rel="stylesheet"><a href="c.html"></a><div class="d"><input value="e"></div>';
1548 assert.equal(minify(input, { removeOptionalTags: true }), output);
1550 input = '<!DOCTYPE html><html><head><title>Blah</title></head><body><div><p>This is some text in a div</p><details>Followed by some details</details></div><div><p>This is some more text in a div</p></div></body></html>';
1551 output = '<!DOCTYPE html><title>Blah</title><div><p>This is some text in a div<details>Followed by some details</details></div><div><p>This is some more text in a div</div>';
1552 assert.equal(minify(input, { removeOptionalTags: true }), output);
1554 input = '<!DOCTYPE html><html><head><title>Blah</title></head><body><noscript><p>This is some text in a noscript</p><details>Followed by some details</details></noscript><noscript><p>This is some more text in a noscript</p></noscript></body></html>';
1555 output = '<!DOCTYPE html><title>Blah</title><body><noscript><p>This is some text in a noscript<details>Followed by some details</details></noscript><noscript><p>This is some more text in a noscript</p></noscript>';
1556 assert.equal(minify(input, { removeOptionalTags: true }), output);
1558 input = '<md-list-item ui-sref=".app-config"><md-icon md-font-icon="mdi-settings"></md-icon><p translate>Configure</p></md-list-item>';
1559 assert.equal(minify(input, { removeOptionalTags: true }), input);
1562 QUnit.test('removing optional tags in tables', function(assert) {
1566 '<thead><tr><th>foo</th><th>bar</th> <th>baz</th></tr></thead> ' +
1567 '<tbody><tr><td>boo</td><td>moo</td><td>loo</td></tr> </tbody>' +
1568 '<tfoot><tr><th>baz</th> <th>qux</th><td>boo</td></tr></tfoot>' +
1570 assert.equal(minify(input), input);
1572 output = '<table>' +
1573 '<thead><tr><th>foo<th>bar</th> <th>baz</thead> ' +
1574 '<tr><td>boo<td>moo<td>loo</tr> ' +
1575 '<tfoot><tr><th>baz</th> <th>qux<td>boo' +
1577 assert.equal(minify(input, { removeOptionalTags: true }), output);
1579 output = '<table>' +
1580 '<thead><tr><th>foo<th>bar<th>baz' +
1581 '<tbody><tr><td>boo<td>moo<td>loo' +
1582 '<tfoot><tr><th>baz<th>qux<td>boo' +
1584 assert.equal(minify(input, { collapseWhitespace: true, removeOptionalTags: true }), output);
1585 assert.equal(minify(output, { collapseWhitespace: true, removeOptionalTags: true }), output);
1588 '<caption>foo</caption>' +
1590 '<colgroup><col span="2"><col></colgroup>' +
1592 '<tbody><tr><th>bar</th><td>baz</td><th>qux</th></tr></tbody>' +
1594 assert.equal(minify(input), input);
1596 output = '<table>' +
1597 '<caption>foo</caption>' +
1599 '<col span="2"><col></colgroup>' +
1601 '<tr><th>bar<td>baz<th>qux' +
1603 assert.equal(minify(input, { removeOptionalTags: true }), output);
1604 assert.equal(minify(output, { removeOptionalTags: true }), output);
1606 output = '<table>' +
1608 '<col span="2"><col>' +
1609 '<tr><th>bar<td>baz<th>qux' +
1611 assert.equal(minify(input, { removeComments: true, removeOptionalTags: true }), output);
1616 assert.equal(minify(input), input);
1618 output = '<table><tbody></table>';
1619 assert.equal(minify(input, { removeOptionalTags: true }), output);
1622 QUnit.test('removing optional tags in options', function(assert) {
1625 input = '<select><option>foo</option><option>bar</option></select>';
1626 output = '<select><option>foo<option>bar</select>';
1627 assert.equal(minify(input, { removeOptionalTags: true }), output);
1629 input = '<select>\n' +
1630 ' <option>foo</option>\n' +
1631 ' <option>bar</option>\n' +
1633 assert.equal(minify(input, { removeOptionalTags: true }), input);
1634 output = '<select><option>foo<option>bar</select>';
1635 assert.equal(minify(input, { removeOptionalTags: true, collapseWhitespace: true }), output);
1636 output = '<select> <option>foo</option> <option>bar</option> </select>';
1637 assert.equal(minify(input, { removeOptionalTags: true, collapseWhitespace: true, conservativeCollapse: true }), output);
1639 // example from htmldog.com
1640 input = '<select name="catsndogs">' +
1641 '<optgroup label="Cats">' +
1642 '<option>Tiger</option><option>Leopard</option><option>Lynx</option>' +
1644 '<optgroup label="Dogs">' +
1645 '<option>Grey Wolf</option><option>Red Fox</option><option>Fennec</option>' +
1649 output = '<select name="catsndogs">' +
1650 '<optgroup label="Cats">' +
1651 '<option>Tiger<option>Leopard<option>Lynx' +
1652 '<optgroup label="Dogs">' +
1653 '<option>Grey Wolf<option>Red Fox<option>Fennec' +
1656 assert.equal(minify(input, { removeOptionalTags: true }), output);
1659 QUnit.test('custom components', function(assert) {
1660 var input = '<custom-component>Oh, my.</custom-component>';
1661 var output = '<custom-component>Oh, my.</custom-component>';
1662 assert.equal(minify(input), output);
1665 QUnit.test('HTML4: anchor with inline elements', function(assert) {
1666 var input = '<a href="#"><span>Well, look at me! I\'m a span!</span></a>';
1667 assert.equal(minify(input, { html5: false }), input);
1670 QUnit.test('HTML5: anchor with inline elements', function(assert) {
1671 var input = '<a href="#"><span>Well, look at me! I\'m a span!</span></a>';
1672 assert.equal(minify(input, { html5: true }), input);
1675 QUnit.test('HTML4: anchor with block elements', function(assert) {
1676 var input = '<a href="#"><div>Well, look at me! I\'m a div!</div></a>';
1677 var output = '<a href="#"></a><div>Well, look at me! I\'m a div!</div>';
1678 assert.equal(minify(input, { html5: false }), output);
1681 QUnit.test('HTML5: anchor with block elements', function(assert) {
1682 var input = '<a href="#"><div>Well, look at me! I\'m a div!</div></a>';
1683 var output = '<a href="#"><div>Well, look at me! I\'m a div!</div></a>';
1684 assert.equal(minify(input, { html5: true }), output);
1687 QUnit.test('HTML5: enabled by default', function(assert) {
1688 var input = '<a href="#"><div>Well, look at me! I\'m a div!</div></a>';
1689 assert.equal(minify(input, { html5: true }), minify(input));
1692 QUnit.test('phrasing content', function(assert) {
1695 input = '<p>a<div>b</div>';
1696 output = '<p>a</p><div>b</div>';
1697 assert.equal(minify(input, { html5: true }), output);
1698 output = '<p>a<div>b</div></p>';
1699 assert.equal(minify(input, { html5: false }), output);
1701 input = '<label>a<div>b</div>c</label>';
1702 assert.equal(minify(input, { html5: true }), input);
1705 // https://github.com/kangax/html-minifier/issues/888
1706 QUnit.test('ul/ol should be phrasing content', function(assert) {
1709 input = '<p>a<ul><li>item</li></ul>';
1710 output = '<p>a</p><ul><li>item</li></ul>';
1711 assert.equal(minify(input, { html5: true }), output);
1713 output = '<p>a<ul><li>item</ul>';
1714 assert.equal(minify(input, { html5: true, removeOptionalTags: true }), output);
1716 output = '<p>a<ul><li>item</li></ul></p>';
1717 assert.equal(minify(input, { html5: false }), output);
1719 input = '<p>a<ol><li>item</li></ol></p>';
1720 output = '<p>a</p><ol><li>item</li></ol><p></p>';
1721 assert.equal(minify(input, { html5: true }), output);
1723 output = '<p>a<ol><li>item</ol><p>';
1724 assert.equal(minify(input, { html5: true, removeOptionalTags: true }), output);
1726 output = '<p>a</p><ol><li>item</li></ol>';
1727 assert.equal(minify(input, { html5: true, removeEmptyElements: true }), output);
1730 QUnit.test('phrasing content with Web Components', function(assert) {
1731 var input = '<span><phrasing-element></phrasing-element></span>';
1732 var output = '<span><phrasing-element></phrasing-element></span>';
1733 assert.equal(minify(input, { html5: true }), output);
1736 // https://github.com/kangax/html-minifier/issues/10
1737 QUnit.test('Ignore custom fragments', function(assert) {
1739 var reFragments = [/<\?[^?]+\?>/, /<%[^%]+%>/, /\{\{[^}]*\}\}/];
1741 input = 'This is the start. <% ... %>\r\n<%= ... %>\r\n<? ... ?>\r\n<!-- This is the middle, and a comment. -->\r\nNo comment, but middle.\r\n{{ ... }}\r\n<?php ... ?>\r\n<?xml ... ?>\r\nHello, this is the end!';
1742 output = 'This is the start. <% ... %> <%= ... %> <? ... ?> No comment, but middle. {{ ... }} <?php ... ?> <?xml ... ?> Hello, this is the end!';
1743 assert.equal(minify(input, {}), input);
1744 assert.equal(minify(input, { removeComments: true, collapseWhitespace: true }), output);
1745 assert.equal(minify(input, {
1746 removeComments: true,
1747 collapseWhitespace: true,
1748 ignoreCustomFragments: reFragments
1751 output = 'This is the start. <% ... %>\n<%= ... %>\n<? ... ?>\nNo comment, but middle. {{ ... }}\n<?php ... ?>\n<?xml ... ?>\nHello, this is the end!';
1752 assert.equal(minify(input, {
1753 removeComments: true,
1754 collapseWhitespace: true,
1755 preserveLineBreaks: true
1758 output = 'This is the start. <% ... %>\n<%= ... %>\n<? ... ?>\nNo comment, but middle.\n{{ ... }}\n<?php ... ?>\n<?xml ... ?>\nHello, this is the end!';
1759 assert.equal(minify(input, {
1760 removeComments: true,
1761 collapseWhitespace: true,
1762 preserveLineBreaks: true,
1763 ignoreCustomFragments: reFragments
1766 input = '{{ if foo? }}\r\n <div class="bar">\r\n ...\r\n </div>\r\n{{ end \n}}';
1767 output = '{{ if foo? }}<div class="bar">...</div>{{ end }}';
1768 assert.equal(minify(input, {}), input);
1769 assert.equal(minify(input, { collapseWhitespace: true }), output);
1770 assert.equal(minify(input, { collapseWhitespace: true, ignoreCustomFragments: [] }), output);
1772 output = '{{ if foo? }} <div class="bar">...</div> {{ end \n}}';
1773 assert.equal(minify(input, { collapseWhitespace: true, ignoreCustomFragments: reFragments }), output);
1775 output = '{{ if foo? }}\n<div class="bar">\n...\n</div>\n{{ end \n}}';
1776 assert.equal(minify(input, {
1777 collapseWhitespace: true,
1778 preserveLineBreaks: true,
1779 ignoreCustomFragments: reFragments
1782 input = '<a class="<% if foo? %>bar<% end %> {{ ... }}"></a>';
1783 assert.equal(minify(input, {}), input);
1784 assert.equal(minify(input, { ignoreCustomFragments: reFragments }), input);
1786 input = '<img src="{% static "images/logo.png" %}">';
1787 output = '<img src="{% static "images/logo.png" %}">';
1788 assert.equal(minify(input, { ignoreCustomFragments: [/\{%[^%]*?%\}/g] }), output);
1790 input = '<p{% if form.name.errors %}class=\'error\'{% endif %}>' +
1791 '{{ form.name.label_tag }}' +
1793 ' <label>{{ label }}</label> ' +
1794 '{% if form.name.errors %}' +
1795 '{% for error in form.name.errors %}' +
1796 '<span class=\'error_msg\' style=\'color:#ff0000\'>{{ error }}</span>' +
1800 assert.equal(minify(input, {
1801 ignoreCustomFragments: [
1805 quoteCharacter: '\''
1807 output = '<p {% if form.name.errors %} class=\'error\' {% endif %}>' +
1808 '{{ form.name.label_tag }}' +
1810 ' <label>{{ label }}</label> ' +
1811 '{% if form.name.errors %}' +
1812 '{% for error in form.name.errors %}' +
1813 '<span class=\'error_msg\' style=\'color:#ff0000\'>{{ error }}</span>' +
1817 assert.equal(minify(input, {
1818 ignoreCustomFragments: [
1822 quoteCharacter: '\'',
1823 collapseWhitespace: true
1826 input = '<a href="/legal.htm"<?php echo e(Request::path() == \'/\' ? \' rel="nofollow"\':\'\'); ?>>Legal Notices</a>';
1827 assert.equal(minify(input, {
1828 ignoreCustomFragments: [
1829 /<\?php[\s\S]*?\?>/g
1833 input = '<input type="checkbox"<%= (model.isChecked ? \'checked="checked"\' : \'\') %>>';
1834 assert.equal(minify(input, {
1835 ignoreCustomFragments: [
1842 'data-yashareDescription="{{shorted(text, 300)}}"' +
1843 '{{END IF}}></div>';
1844 assert.equal(minify(input, {
1845 ignoreCustomFragments: [
1851 input = '<img class="{% foo %} {% bar %}">';
1852 assert.equal(minify(input, {
1853 ignoreCustomFragments: [
1857 // trimCustomFragments withOUT collapseWhitespace, does
1858 // not break the "{% foo %} {% bar %}" test
1859 assert.equal(minify(input, {
1860 ignoreCustomFragments: [
1863 trimCustomFragments: true
1865 // trimCustomFragments WITH collapseWhitespace, changes output
1866 output = '<img class="{% foo %}{% bar %}">';
1867 assert.equal(minify(input, {
1868 ignoreCustomFragments: [
1871 collapseWhitespace: true,
1872 trimCustomFragments: true
1875 input = '<img class="titi.<%=tsItem_[0]%>">';
1876 assert.equal(minify(input), input);
1877 assert.equal(minify(input, {
1878 collapseWhitespace: true
1881 input = '<table id="<?php echo $this->escapeHtmlAttr($this->table_id); ?>"></table>';
1882 assert.equal(minify(input), input);
1883 assert.equal(minify(input, {
1884 collapseWhitespace: true
1887 input = '<!--{{comment}}-->{{if a}}<div>b</div>{{/if}}';
1888 assert.equal(minify(input), input);
1889 output = '{{if a}}<div>b</div>{{/if}}';
1890 assert.equal(minify(input, {
1891 removeComments: true,
1892 ignoreCustomFragments: [
1897 // https://github.com/kangax/html-minifier/issues/722
1898 input = '<? echo "foo"; ?> <span>bar</span>';
1899 assert.equal(minify(input), input);
1900 assert.equal(minify(input, {
1901 collapseWhitespace: true
1903 output = '<? echo "foo"; ?><span>bar</span>';
1904 assert.equal(minify(input, {
1905 collapseWhitespace: true,
1906 trimCustomFragments: true
1909 input = ' <? echo "foo"; ?> bar';
1910 assert.equal(minify(input), input);
1911 output = '<? echo "foo"; ?> bar';
1912 assert.equal(minify(input, {
1913 collapseWhitespace: true
1915 output = '<? echo "foo"; ?>bar';
1916 assert.equal(minify(input, {
1917 collapseWhitespace: true,
1918 trimCustomFragments: true
1921 input = '<span>foo</span> <? echo "bar"; ?> baz';
1922 assert.equal(minify(input), input);
1923 assert.equal(minify(input, {
1924 collapseWhitespace: true
1926 output = '<span>foo</span><? echo "bar"; ?>baz';
1927 assert.equal(minify(input, {
1928 collapseWhitespace: true,
1929 trimCustomFragments: true
1932 input = '<span>foo</span> <? echo "bar"; ?> <? echo "baz"; ?> <span>foo</span>';
1933 assert.equal(minify(input), input);
1934 assert.equal(minify(input, {
1935 collapseWhitespace: true
1937 output = '<span>foo</span><? echo "bar"; ?><? echo "baz"; ?><span>foo</span>';
1938 assert.equal(minify(input, {
1939 collapseWhitespace: true,
1940 trimCustomFragments: true
1943 input = 'foo <WC@bar> baz moo </WC@bar> loo';
1944 assert.equal(minify(input, {
1945 collapseWhitespace: true,
1946 ignoreCustomFragments: [
1947 /<(WC@[\s\S]*?)>(.*?)<\/\1>/
1950 output = 'foo<wc @bar>baz moo</wc>loo';
1951 assert.equal(minify(input, {
1952 collapseWhitespace: true
1955 input = '<link href="<?php echo \'http://foo/\' ?>">';
1956 assert.equal(minify(input), input);
1957 assert.equal(minify(input, { removeAttributeQuotes: true }), input);
1959 input = '<pre>\nfoo\n<? bar ?>\nbaz\n</pre>';
1960 assert.equal(minify(input), input);
1961 assert.equal(minify(input, { collapseWhitespace: true }), input);
1964 QUnit.test('bootstrap\'s span > button > span', function(assert) {
1965 var input = '<span class="input-group-btn">' +
1966 '\n <button class="btn btn-default" type="button">' +
1967 '\n <span class="glyphicon glyphicon-search"></span>' +
1970 var output = '<span class=input-group-btn><button class="btn btn-default" type=button><span class="glyphicon glyphicon-search"></span></button></span>';
1971 assert.equal(minify(input, { collapseWhitespace: true, removeAttributeQuotes: true }), output);
1974 QUnit.test('caseSensitive', function(assert) {
1975 var input = '<div mixedCaseAttribute="value"></div>';
1976 var caseSensitiveOutput = '<div mixedCaseAttribute="value"></div>';
1977 var caseInSensitiveOutput = '<div mixedcaseattribute="value"></div>';
1978 assert.equal(minify(input), caseInSensitiveOutput);
1979 assert.equal(minify(input, { caseSensitive: true }), caseSensitiveOutput);
1982 QUnit.test('source & track', function(assert) {
1983 var input = '<audio controls="controls">' +
1984 '<source src="foo.wav">' +
1985 '<source src="far.wav">' +
1986 '<source src="foobar.wav">' +
1987 '<track kind="captions" src="sampleCaptions.vtt" srclang="en">' +
1989 assert.equal(minify(input), input);
1990 assert.equal(minify(input, { removeOptionalTags: true }), input);
1993 QUnit.test('mixed html and svg', function(assert) {
1994 var input = '<html><body>\n' +
1995 ' <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\n' +
1996 ' width="612px" height="502.174px" viewBox="0 65.326 612 502.174" enable-background="new 0 65.326 612 502.174"\n' +
1997 ' xml:space="preserve" class="logo">' +
1999 ' <ellipse class="ground" cx="283.5" cy="487.5" rx="259" ry="80"/>' +
2000 ' <polygon points="100,10 40,198 190,78 10,78 160,198"\n' +
2001 ' style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" />\n' +
2002 ' <filter id="pictureFilter">\n' +
2003 ' <feGaussianBlur stdDeviation="15" />\n' +
2007 var output = '<html><body>' +
2008 '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="612px" height="502.174px" viewBox="0 65.326 612 502.174" enable-background="new 0 65.326 612 502.174" xml:space="preserve" class="logo">' +
2009 '<ellipse class="ground" cx="283.5" cy="487.5" rx="259" ry="80"/>' +
2010 '<polygon points="100,10 40,198 190,78 10,78 160,198" style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;"/>' +
2011 '<filter id="pictureFilter"><feGaussianBlur stdDeviation="15"/></filter>' +
2014 // Should preserve case-sensitivity and closing slashes within svg tags
2015 assert.equal(minify(input, { collapseWhitespace: true }), output);
2018 QUnit.test('nested quotes', function(assert) {
2021 input = '<div data=\'{"test":"\\"test\\""}\'></div>';
2022 assert.equal(minify(input), input);
2023 assert.equal(minify(input, { quoteCharacter: '\'' }), input);
2025 output = '<div data="{"test":"\\"test\\""}"></div>';
2026 assert.equal(minify(input, { quoteCharacter: '"' }), output);
2029 QUnit.test('script minification', function(assert) {
2032 input = '<script></script>(function(){ var foo = 1; var bar = 2; alert(foo + " " + bar); })()';
2034 assert.equal(minify(input, { minifyJS: true }), input);
2036 input = '<script>(function(){ var foo = 1; var bar = 2; alert(foo + " " + bar); })()</script>';
2037 output = '<script>alert("1 2")</script>';
2039 assert.equal(minify(input, { minifyJS: true }), output);
2041 input = '<script type="text/JavaScript">(function(){ var foo = 1; var bar = 2; alert(foo + " " + bar); })()</script>';
2042 output = '<script type="text/JavaScript">alert("1 2")</script>';
2044 assert.equal(minify(input, { minifyJS: true }), output);
2046 input = '<script type="application/javascript;version=1.8">(function(){ var foo = 1; var bar = 2; alert(foo + " " + bar); })()</script>';
2047 output = '<script type="application/javascript;version=1.8">alert("1 2")</script>';
2049 assert.equal(minify(input, { minifyJS: true }), output);
2051 input = '<script type=" application/javascript ; charset=utf-8 ">(function(){ var foo = 1; var bar = 2; alert(foo + " " + bar); })()</script>';
2052 output = '<script type="application/javascript;charset=utf-8">alert("1 2")</script>';
2054 assert.equal(minify(input, { minifyJS: true }), output);
2056 input = '<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\'gtm.start\':new Date().getTime(),event:\'gtm.js\'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=\'//www.googletagmanager.com/gtm.js?id=\'+i+dl;f.parentNode.insertBefore(j,f);})(window,document,\'script\',\'dataLayer\',\'GTM-67NT\');</script>';
2057 output = '<script>!function(w,d,s,l,i){w[l]=w[l]||[],w[l].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=!0,j.src="//www.googletagmanager.com/gtm.js?id=GTM-67NT",f.parentNode.insertBefore(j,f)}(window,document,"script","dataLayer")</script>';
2059 assert.equal(minify(input, { minifyJS: { mangle: false } }), output);
2061 input = '<script>\n' +
2063 ' Platform.Mobile.Bootstrap.init(function () {\n' +
2064 ' Platform.Mobile.Core.Navigation.go("Login", {\n' +
2070 output = '<script>Platform.Mobile.Bootstrap.init(function(){Platform.Mobile.Core.Navigation.go("Login",{error:""})})</script>';
2072 assert.equal(minify(input, { minifyJS: true }), output);
2075 QUnit.test('minification of scripts with different mimetypes', function(assert) {
2078 input = '<script type="">function f(){ return 1 }</script>';
2079 output = '<script type="">function f(){return 1}</script>';
2080 assert.equal(minify(input, { minifyJS: true }), output);
2082 input = '<script type="text/javascript">function f(){ return 1 }</script>';
2083 output = '<script type="text/javascript">function f(){return 1}</script>';
2084 assert.equal(minify(input, { minifyJS: true }), output);
2086 input = '<script foo="bar">function f(){ return 1 }</script>';
2087 output = '<script foo="bar">function f(){return 1}</script>';
2088 assert.equal(minify(input, { minifyJS: true }), output);
2090 input = '<script type="text/ecmascript">function f(){ return 1 }</script>';
2091 output = '<script type="text/ecmascript">function f(){return 1}</script>';
2092 assert.equal(minify(input, { minifyJS: true }), output);
2094 input = '<script type="application/javascript">function f(){ return 1 }</script>';
2095 output = '<script type="application/javascript">function f(){return 1}</script>';
2096 assert.equal(minify(input, { minifyJS: true }), output);
2098 input = '<script type="boo">function f(){ return 1 }</script>';
2099 assert.equal(minify(input, { minifyJS: true }), input);
2101 input = '<script type="text/html"><!-- ko if: true -->\n\n\n<div></div>\n\n\n<!-- /ko --></script>';
2102 assert.equal(minify(input, { minifyJS: true }), input);
2105 QUnit.test('minification of scripts with custom fragments', function(assert) {
2108 input = '<script><?php ?></script>';
2109 assert.equal(minify(input, { minifyJS: true }), input);
2110 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), input);
2111 assert.equal(minify(input, {
2112 collapseWhitespace: true,
2114 preserveLineBreaks: true
2117 input = '<script>\n<?php ?></script>';
2118 assert.equal(minify(input, { minifyJS: true }), input);
2119 output = '<script> <?php ?></script>';
2120 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2121 assert.equal(minify(input, {
2122 collapseWhitespace: true,
2124 preserveLineBreaks: true
2127 input = '<script><?php ?>\n</script>';
2128 assert.equal(minify(input, { minifyJS: true }), input);
2129 output = '<script><?php ?> </script>';
2130 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2131 assert.equal(minify(input, {
2132 collapseWhitespace: true,
2134 preserveLineBreaks: true
2137 input = '<script>\n<?php ?>\n</script>';
2138 assert.equal(minify(input, { minifyJS: true }), input);
2139 output = '<script> <?php ?> </script>';
2140 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2141 assert.equal(minify(input, {
2142 collapseWhitespace: true,
2144 preserveLineBreaks: true
2147 input = '<script>// <% ... %></script>';
2148 output = '<script></script>';
2149 assert.equal(minify(input, { minifyJS: true }), output);
2150 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2151 assert.equal(minify(input, {
2152 collapseWhitespace: true,
2154 preserveLineBreaks: true
2157 input = '<script>// \n<% ... %></script>';
2158 output = '<script> \n<% ... %></script>';
2159 assert.equal(minify(input, { minifyJS: true }), output);
2160 output = '<script> <% ... %></script>';
2161 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2162 output = '<script>\n<% ... %></script>';
2163 assert.equal(minify(input, {
2164 collapseWhitespace: true,
2166 preserveLineBreaks: true
2169 input = '<script>// <% ... %>\n</script>';
2170 output = '<script></script>';
2171 assert.equal(minify(input, { minifyJS: true }), output);
2172 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2173 assert.equal(minify(input, {
2174 collapseWhitespace: true,
2176 preserveLineBreaks: true
2179 input = '<script>// \n<% ... %>\n</script>';
2180 output = '<script> \n<% ... %>\n</script>';
2181 assert.equal(minify(input, { minifyJS: true }), output);
2182 output = '<script> <% ... %> </script>';
2183 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2184 output = '<script>\n<% ... %>\n</script>';
2185 assert.equal(minify(input, {
2186 collapseWhitespace: true,
2188 preserveLineBreaks: true
2191 input = '<script>function f(){ return <?php ?> }</script>';
2192 output = '<script>function f(){return <?php ?> }</script>';
2193 assert.equal(minify(input, { minifyJS: true }), output);
2194 output = '<script>function f(){return <?php ?> }</script>';
2195 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2197 input = '<script>function f(){ return "<?php ?>" }</script>';
2198 output = '<script>function f(){return"<?php ?>"}</script>';
2199 assert.equal(minify(input, { minifyJS: true }), output);
2200 assert.equal(minify(input, { collapseWhitespace: true, minifyJS: true }), output);
2203 QUnit.test('event minification', function(assert) {
2206 input = '<div only="alert(a + b)" one=";return false;"></div>';
2207 assert.equal(minify(input, { minifyJS: true }), input);
2209 input = '<div onclick="alert(a + b)"></div>';
2210 output = '<div onclick="alert(a+b)"></div>';
2211 assert.equal(minify(input, { minifyJS: true }), output);
2213 input = '<a href="/" onclick="this.href = getUpdatedURL (this.href);return true;">test</a>';
2214 output = '<a href="/" onclick="return this.href=getUpdatedURL(this.href),!0">test</a>';
2215 assert.equal(minify(input, { minifyJS: true }), output);
2217 input = '<a onclick="try{ dcsMultiTrack(\'DCS.dcsuri\',\'USPS\',\'WT.ti\') }catch(e){}"> foobar</a>';
2218 output = '<a onclick=\'try{dcsMultiTrack("DCS.dcsuri","USPS","WT.ti")}catch(e){}\'> foobar</a>';
2219 assert.equal(minify(input, { minifyJS: { mangle: false } }), output);
2220 assert.equal(minify(input, { minifyJS: { mangle: false }, quoteCharacter: '\'' }), output);
2222 input = '<a onclick="try{ dcsMultiTrack(\'DCS.dcsuri\',\'USPS\',\'WT.ti\') }catch(e){}"> foobar</a>';
2223 output = '<a onclick="try{dcsMultiTrack("DCS.dcsuri","USPS","WT.ti")}catch(e){}"> foobar</a>';
2224 assert.equal(minify(input, { minifyJS: { mangle: false }, quoteCharacter: '"' }), output);
2226 input = '<a onClick="_gaq.push([\'_trackEvent\', \'FGF\', \'banner_click\']);"></a>';
2227 output = '<a onclick=\'_gaq.push(["_trackEvent","FGF","banner_click"])\'></a>';
2228 assert.equal(minify(input, { minifyJS: true }), output);
2229 assert.equal(minify(input, { minifyJS: true, quoteCharacter: '\'' }), output);
2231 input = '<a onClick="_gaq.push([\'_trackEvent\', \'FGF\', \'banner_click\']);"></a>';
2232 output = '<a onclick="_gaq.push(["_trackEvent","FGF","banner_click"])"></a>';
2233 assert.equal(minify(input, { minifyJS: true, quoteCharacter: '"' }), output);
2235 input = '<button type="button" onclick=";return false;" id="appbar-guide-button"></button>';
2236 output = '<button type="button" onclick="return!1" id="appbar-guide-button"></button>';
2237 assert.equal(minify(input, { minifyJS: true }), output);
2239 input = '<button type="button" onclick=";return false;" ng-click="a(1 + 2)" data-click="a(1 + 2)"></button>';
2240 output = '<button type="button" onclick="return!1" ng-click="a(1 + 2)" data-click="a(1 + 2)"></button>';
2241 assert.equal(minify(input, { minifyJS: true }), output);
2242 assert.equal(minify(input, { minifyJS: true, customEventAttributes: [] }), input);
2243 output = '<button type="button" onclick=";return false;" ng-click="a(3)" data-click="a(1 + 2)"></button>';
2244 assert.equal(minify(input, { minifyJS: true, customEventAttributes: [/^ng-/] }), output);
2245 output = '<button type="button" onclick="return!1" ng-click="a(3)" data-click="a(1 + 2)"></button>';
2246 assert.equal(minify(input, { minifyJS: true, customEventAttributes: [/^on/, /^ng-/] }), output);
2248 input = '<div onclick="<?= b ?>"></div>';
2249 assert.equal(minify(input, { minifyJS: true }), input);
2251 input = '<div onclick="alert(a + <?= b ?>)"></div>';
2252 output = '<div onclick="alert(a+ <?= b ?>)"></div>';
2253 assert.equal(minify(input, { minifyJS: true }), output);
2255 input = '<div onclick="alert(a + \'<?= b ?>\')"></div>';
2256 output = '<div onclick=\'alert(a+"<?= b ?>")\'></div>';
2257 assert.equal(minify(input, { minifyJS: true }), output);
2260 QUnit.test('escaping closing script tag', function(assert) {
2261 var input = '<script>window.jQuery || document.write(\'<script src="jquery.js"><\\/script>\')</script>';
2262 var output = '<script>window.jQuery||document.write(\'<script src="jquery.js"><\\/script>\')</script>';
2263 assert.equal(minify(input, { minifyJS: true }), output);
2266 QUnit.test('style minification', function(assert) {
2269 input = '<style></style>div#foo { background-color: red; color: white }';
2270 assert.equal(minify(input, { minifyCSS: true }), input);
2272 input = '<style>div#foo { background-color: red; color: white }</style>';
2273 output = '<style>div#foo{background-color:red;color:#fff}</style>';
2274 assert.equal(minify(input), input);
2275 assert.equal(minify(input, { minifyCSS: true }), output);
2277 input = '<style>div > p.foo + span { border: 10px solid black }</style>';
2278 output = '<style>div>p.foo+span{border:10px solid #000}</style>';
2279 assert.equal(minify(input, { minifyCSS: true }), output);
2281 input = '<div style="background: url(images/<% image %>);"></div>';
2282 assert.equal(minify(input), input);
2283 output = '<div style="background:url(images/<% image %>)"></div>';
2284 assert.equal(minify(input, { minifyCSS: true }), output);
2285 assert.equal(minify(input, {
2286 collapseWhitespace: true,
2290 input = '<div style="background: url(\'images/<% image %>\')"></div>';
2291 assert.equal(minify(input), input);
2292 output = '<div style="background:url(images/<% image %>)"></div>';
2293 assert.equal(minify(input, { minifyCSS: true }), output);
2294 assert.equal(minify(input, {
2295 collapseWhitespace: true,
2299 input = '<style>\np {\n background: url(images/<% image %>);\n}\n</style>';
2300 assert.equal(minify(input), input);
2301 output = '<style>p{background:url(images/<% image %>)}</style>';
2302 assert.equal(minify(input, { minifyCSS: true }), output);
2303 assert.equal(minify(input, {
2304 collapseWhitespace: true,
2308 input = '<style>p { background: url("images/<% image %>") }</style>';
2309 assert.equal(minify(input), input);
2310 output = '<style>p{background:url(images/<% image %>)}</style>';
2311 assert.equal(minify(input, { minifyCSS: true }), output);
2312 assert.equal(minify(input, {
2313 collapseWhitespace: true,
2317 input = '<link rel="stylesheet" href="css/style-mobile.css" media="(max-width: 737px)">';
2318 assert.equal(minify(input), input);
2319 output = '<link rel="stylesheet" href="css/style-mobile.css" media="(max-width:737px)">';
2320 assert.equal(minify(input, { minifyCSS: true }), output);
2321 output = '<link rel=stylesheet href=css/style-mobile.css media=(max-width:737px)>';
2322 assert.equal(minify(input, {
2324 removeAttributeQuotes: true
2327 input = '<style media="(max-width: 737px)"></style>';
2328 assert.equal(minify(input), input);
2329 output = '<style media="(max-width:737px)"></style>';
2330 assert.equal(minify(input, { minifyCSS: true }), output);
2331 output = '<style media=(max-width:737px)></style>';
2332 assert.equal(minify(input, {
2334 removeAttributeQuotes: true
2338 QUnit.test('style attribute minification', function(assert) {
2339 var input = '<div style="color: red; background-color: yellow; font-family: Verdana, Arial, sans-serif;"></div>';
2340 var output = '<div style="color:red;background-color:#ff0;font-family:Verdana,Arial,sans-serif"></div>';
2341 assert.equal(minify(input, { minifyCSS: true }), output);
2344 QUnit.test('url attribute minification', function(assert) {
2347 input = '<link rel="stylesheet" href="http://website.com/style.css"><form action="http://website.com/folder/folder2/index.html"><a href="http://website.com/folder/file.html">link</a></form>';
2348 output = '<link rel="stylesheet" href="/style.css"><form action="folder2/"><a href="file.html">link</a></form>';
2349 assert.equal(minify(input, { minifyURLs: 'http://website.com/folder/' }), output);
2350 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/folder/' } }), output);
2352 input = '<link rel="canonical" href="http://website.com/">';
2353 assert.equal(minify(input, { minifyURLs: 'http://website.com/' }), input);
2354 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/' } }), input);
2356 input = '<style>body { background: url(\'http://website.com/bg.png\') }</style>';
2357 assert.equal(minify(input, { minifyURLs: 'http://website.com/' }), input);
2358 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/' } }), input);
2359 output = '<style>body{background:url(http://website.com/bg.png)}</style>';
2360 assert.equal(minify(input, { minifyCSS: true }), output);
2361 output = '<style>body{background:url(bg.png)}</style>';
2362 assert.equal(minify(input, {
2364 minifyURLs: 'http://website.com/'
2366 assert.equal(minify(input, {
2368 minifyURLs: { site: 'http://website.com/' }
2371 input = '<style>body { background: url("http://website.com/foo bar/bg.png") }</style>';
2372 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/foo bar/' } }), input);
2373 output = '<style>body{background:url("http://website.com/foo bar/bg.png")}</style>';
2374 assert.equal(minify(input, { minifyCSS: true }), output);
2375 output = '<style>body{background:url(bg.png)}</style>';
2376 assert.equal(minify(input, {
2378 minifyURLs: { site: 'http://website.com/foo bar/' }
2381 input = '<style>body { background: url("http://website.com/foo bar/(baz)/bg.png") }</style>';
2382 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/' } }), input);
2383 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/foo%20bar/' } }), input);
2384 assert.equal(minify(input, { minifyURLs: { site: 'http://website.com/foo%20bar/(baz)/' } }), input);
2385 output = '<style>body{background:url("foo%20bar/(baz)/bg.png")}</style>';
2386 assert.equal(minify(input, {
2388 minifyURLs: { site: 'http://website.com/' }
2390 output = '<style>body{background:url("(baz)/bg.png")}</style>';
2391 assert.equal(minify(input, {
2393 minifyURLs: { site: 'http://website.com/foo%20bar/' }
2395 output = '<style>body{background:url(bg.png)}</style>';
2396 assert.equal(minify(input, {
2398 minifyURLs: { site: 'http://website.com/foo%20bar/(baz)/' }
2401 input = '<img src="http://cdn.site.com/foo.png">';
2402 output = '<img src="//cdn.site.com/foo.png">';
2403 assert.equal(minify(input, { minifyURLs: { site: 'http://site.com/' } }), output);
2406 QUnit.test('srcset attribute minification', function(assert) {
2408 input = '<source srcset="http://site.com/foo.gif ,http://site.com/bar.jpg 1x, baz moo 42w,' +
2409 '\n\n\n\n\n\t http://site.com/zo om.png 1.00x">';
2410 output = '<source srcset="http://site.com/foo.gif, http://site.com/bar.jpg, baz moo 42w, http://site.com/zo om.png">';
2411 assert.equal(minify(input), output);
2412 output = '<source srcset="foo.gif, bar.jpg, baz%20moo 42w, zo%20om.png">';
2413 assert.equal(minify(input, { minifyURLs: { site: 'http://site.com/' } }), output);
2416 QUnit.test('valueless attributes', function(assert) {
2417 var input = '<br foo>';
2418 assert.equal(minify(input), input);
2421 QUnit.test('newlines becoming whitespaces', function(assert) {
2422 var input = 'test\n\n<input>\n\ntest';
2423 var output = 'test <input> test';
2424 assert.equal(minify(input, { collapseWhitespace: true }), output);
2427 QUnit.test('conservative collapse', function(assert) {
2430 input = '<b> foo \n\n</b>';
2431 output = '<b> foo </b>';
2432 assert.equal(minify(input, {
2433 collapseWhitespace: true,
2434 conservativeCollapse: true
2437 input = '<html>\n\n<!--test-->\n\n</html>';
2438 output = '<html> </html>';
2439 assert.equal(minify(input, {
2440 removeComments: true,
2441 collapseWhitespace: true,
2442 conservativeCollapse: true
2445 input = '<p>\u00A0</p>';
2446 assert.equal(minify(input, { collapseWhitespace: true }), input);
2447 assert.equal(minify(input, {
2448 collapseWhitespace: true,
2449 conservativeCollapse: true
2452 input = '<p> \u00A0</p>';
2453 output = '<p>\u00A0</p>';
2454 assert.equal(minify(input, { collapseWhitespace: true }), output);
2455 assert.equal(minify(input, {
2456 collapseWhitespace: true,
2457 conservativeCollapse: true
2460 input = '<p>\u00A0 </p>';
2461 output = '<p>\u00A0</p>';
2462 assert.equal(minify(input, { collapseWhitespace: true }), output);
2463 assert.equal(minify(input, {
2464 collapseWhitespace: true,
2465 conservativeCollapse: true
2468 input = '<p> \u00A0 </p>';
2469 output = '<p>\u00A0</p>';
2470 assert.equal(minify(input, { collapseWhitespace: true }), output);
2471 assert.equal(minify(input, {
2472 collapseWhitespace: true,
2473 conservativeCollapse: true
2476 input = '<p> \u00A0\u00A0 \u00A0 </p>';
2477 output = '<p>\u00A0\u00A0 \u00A0</p>';
2478 assert.equal(minify(input, { collapseWhitespace: true }), output);
2479 assert.equal(minify(input, {
2480 collapseWhitespace: true,
2481 conservativeCollapse: true
2484 input = '<p>foo \u00A0\u00A0 \u00A0 </p>';
2485 output = '<p>foo \u00A0\u00A0 \u00A0</p>';
2486 assert.equal(minify(input, { collapseWhitespace: true }), output);
2487 assert.equal(minify(input, {
2488 collapseWhitespace: true,
2489 conservativeCollapse: true
2492 input = '<p> \u00A0\u00A0 \u00A0 bar</p>';
2493 output = '<p>\u00A0\u00A0 \u00A0 bar</p>';
2494 assert.equal(minify(input, { collapseWhitespace: true }), output);
2495 assert.equal(minify(input, {
2496 collapseWhitespace: true,
2497 conservativeCollapse: true
2500 input = '<p>foo \u00A0\u00A0 \u00A0 bar</p>';
2501 output = '<p>foo \u00A0\u00A0 \u00A0 bar</p>';
2502 assert.equal(minify(input, { collapseWhitespace: true }), output);
2503 assert.equal(minify(input, {
2504 collapseWhitespace: true,
2505 conservativeCollapse: true
2508 input = '<p> \u00A0foo\u00A0\t</p>';
2509 output = '<p>\u00A0foo\u00A0</p>';
2510 assert.equal(minify(input, { collapseWhitespace: true }), output);
2511 assert.equal(minify(input, {
2512 collapseWhitespace: true,
2513 conservativeCollapse: true
2517 input = '<p> \u00A0\nfoo\u00A0\t</p>';
2518 output = '<p>\u00A0 foo\u00A0</p>';
2519 assert.equal(minify(input, { collapseWhitespace: true }), output);
2520 assert.equal(minify(input, {
2521 collapseWhitespace: true,
2522 conservativeCollapse: true
2526 input = '<p> \u00A0foo \u00A0\t</p>';
2527 output = '<p>\u00A0foo \u00A0</p>';
2528 assert.equal(minify(input, { collapseWhitespace: true }), output);
2529 assert.equal(minify(input, {
2530 collapseWhitespace: true,
2531 conservativeCollapse: true
2534 input = '<p> \u00A0\nfoo \u00A0\t</p>';
2535 output = '<p>\u00A0 foo \u00A0</p>';
2536 assert.equal(minify(input, { collapseWhitespace: true }), output);
2537 assert.equal(minify(input, {
2538 collapseWhitespace: true,
2539 conservativeCollapse: true
2543 QUnit.test('collapse preseving a line break', function(assert) {
2546 input = '\n\n\n<!DOCTYPE html> \n<html lang="en" class="no-js">\n' +
2547 ' <head>\n <meta charset="utf-8">\n <meta http-equiv="X-UA-Compatible" content="IE=edge">\n\n\n\n' +
2548 '\t<!-- Copyright Notice -->\n' +
2549 ' <title>Carbon</title>\n\n\t<meta name="title" content="Carbon">\n\t\n\n' +
2550 '\t<meta name="description" content="A front-end framework.">\n' +
2551 ' <meta name="apple-mobile-web-app-capable" content="yes">\n' +
2552 ' <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">\n' +
2553 ' <meta name="viewport" content="width=device-width, initial-scale=1">\n\n' +
2554 '<link href="stylesheets/application.css" rel="stylesheet">\n' +
2555 ' <script src="scripts/application.js"></script>\n' +
2556 ' <link href="images/icn-32x32.png" rel="shortcut icon">\n' +
2557 ' <link href="images/icn-152x152.png" rel="apple-touch-icon">\n </head>\n <body><p>\n test test\n\ttest\n\n</p></body>\n</html>';
2558 output = '\n<!DOCTYPE html>\n<html lang="en" class="no-js">\n' +
2559 '<head>\n<meta charset="utf-8">\n<meta http-equiv="X-UA-Compatible" content="IE=edge">\n' +
2560 '<!-- Copyright Notice -->\n' +
2561 '<title>Carbon</title>\n<meta name="title" content="Carbon">\n' +
2562 '<meta name="description" content="A front-end framework.">\n' +
2563 '<meta name="apple-mobile-web-app-capable" content="yes">\n' +
2564 '<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">\n' +
2565 '<meta name="viewport" content="width=device-width,initial-scale=1">\n' +
2566 '<link href="stylesheets/application.css" rel="stylesheet">\n' +
2567 '<script src="scripts/application.js"></script>\n' +
2568 '<link href="images/icn-32x32.png" rel="shortcut icon">\n' +
2569 '<link href="images/icn-152x152.png" rel="apple-touch-icon">\n</head>\n<body><p>\ntest test test\n</p></body>\n</html>';
2570 assert.equal(minify(input, {
2571 collapseWhitespace: true,
2572 preserveLineBreaks: true
2574 assert.equal(minify(input, {
2575 collapseWhitespace: true,
2576 conservativeCollapse: true,
2577 preserveLineBreaks: true
2579 output = '\n<!DOCTYPE html>\n<html lang="en" class="no-js">\n' +
2580 '<head>\n<meta charset="utf-8">\n<meta http-equiv="X-UA-Compatible" content="IE=edge">\n' +
2581 '<title>Carbon</title>\n<meta name="title" content="Carbon">\n' +
2582 '<meta name="description" content="A front-end framework.">\n' +
2583 '<meta name="apple-mobile-web-app-capable" content="yes">\n' +
2584 '<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">\n' +
2585 '<meta name="viewport" content="width=device-width,initial-scale=1">\n' +
2586 '<link href="stylesheets/application.css" rel="stylesheet">\n' +
2587 '<script src="scripts/application.js"></script>\n' +
2588 '<link href="images/icn-32x32.png" rel="shortcut icon">\n' +
2589 '<link href="images/icn-152x152.png" rel="apple-touch-icon">\n</head>\n<body><p>\ntest test test\n</p></body>\n</html>';
2590 assert.equal(minify(input, {
2591 collapseWhitespace: true,
2592 conservativeCollapse: true,
2593 preserveLineBreaks: true,
2594 removeComments: true
2597 input = '<div> text <span>\n text</span> \n</div>';
2598 output = '<div>text <span>\ntext</span>\n</div>';
2599 assert.equal(minify(input, {
2600 collapseWhitespace: true,
2601 preserveLineBreaks: true
2604 input = '<div> text \n </div>';
2605 output = '<div>text\n</div>';
2606 assert.equal(minify(input, {
2607 collapseWhitespace: true,
2608 preserveLineBreaks: true
2610 output = '<div> text\n</div>';
2611 assert.equal(minify(input, {
2612 collapseWhitespace: true,
2613 conservativeCollapse: true,
2614 preserveLineBreaks: true
2617 input = '<div>\ntext </div>';
2618 output = '<div>\ntext</div>';
2619 assert.equal(minify(input, {
2620 collapseWhitespace: true,
2621 preserveLineBreaks: true
2623 output = '<div>\ntext </div>';
2624 assert.equal(minify(input, {
2625 collapseWhitespace: true,
2626 conservativeCollapse: true,
2627 preserveLineBreaks: true
2630 input = 'This is the start. <% ... %>\r\n<%= ... %>\r\n<? ... ?>\r\n<!-- This is the middle, and a comment. -->\r\nNo comment, but middle.\r\n<?= ... ?>\r\n<?php ... ?>\r\n<?xml ... ?>\r\nHello, this is the end!';
2631 output = 'This is the start. <% ... %>\n<%= ... %>\n<? ... ?>\nNo comment, but middle.\n<?= ... ?>\n<?php ... ?>\n<?xml ... ?>\nHello, this is the end!';
2632 assert.equal(minify(input, {
2633 removeComments: true,
2634 collapseWhitespace: true,
2635 preserveLineBreaks: true
2639 QUnit.test('collapse inline tag whitespace', function(assert) {
2642 input = '<button>a</button> <button>b</button>';
2643 assert.equal(minify(input, {
2644 collapseWhitespace: true
2647 output = '<button>a</button><button>b</button>';
2648 assert.equal(minify(input, {
2649 collapseWhitespace: true,
2650 collapseInlineTagWhitespace: true
2653 input = '<p>where <math> <mi>R</mi> </math> is the Rici tensor.</p>';
2654 output = '<p>where <math><mi>R</mi></math> is the Rici tensor.</p>';
2655 assert.equal(minify(input, {
2656 collapseWhitespace: true
2659 output = '<p>where<math><mi>R</mi></math>is the Rici tensor.</p>';
2660 assert.equal(minify(input, {
2661 collapseWhitespace: true,
2662 collapseInlineTagWhitespace: true
2666 QUnit.test('ignore custom comments', function(assert) {
2669 input = '<!--! test -->';
2670 assert.equal(minify(input), input);
2671 assert.equal(minify(input, { removeComments: true }), input);
2672 assert.equal(minify(input, { ignoreCustomComments: false }), input);
2673 assert.equal(minify(input, {
2674 removeComments: true,
2675 ignoreCustomComments: []
2677 assert.equal(minify(input, {
2678 removeComments: true,
2679 ignoreCustomComments: false
2682 input = '<!-- htmlmin:ignore -->test<!-- htmlmin:ignore -->';
2684 assert.equal(minify(input), output);
2685 assert.equal(minify(input, { removeComments: true }), output);
2686 assert.equal(minify(input, { ignoreCustomComments: false }), output);
2687 assert.equal(minify(input, {
2688 removeComments: true,
2689 ignoreCustomComments: []
2691 assert.equal(minify(input, {
2692 removeComments: true,
2693 ignoreCustomComments: false
2696 input = '<!-- ko if: someExpressionGoesHere --><li>test</li><!-- /ko -->';
2697 assert.equal(minify(input, {
2698 removeComments: true,
2699 // ignore knockout comments
2700 ignoreCustomComments: [
2706 input = '<!--#include virtual="/cgi-bin/counter.pl" -->';
2707 assert.equal(minify(input, {
2708 removeComments: true,
2709 // ignore Apache SSI includes
2710 ignoreCustomComments: [
2716 QUnit.test('processScripts', function(assert) {
2717 var input = '<script type="text/ng-template"><!--test--><div> <span> foobar </span> \n\n</div></script>';
2718 var output = '<script type="text/ng-template"><div><span>foobar</span></div></script>';
2719 assert.equal(minify(input, {
2720 collapseWhitespace: true,
2721 removeComments: true,
2722 processScripts: ['text/ng-template']
2726 QUnit.test('ignore', function(assert) {
2729 input = '<!-- htmlmin:ignore --><div class="blah" style="color: red">\n test <span> <input disabled/> foo </span>\n\n </div><!-- htmlmin:ignore -->' +
2730 '<div class="blah" style="color: red">\n test <span> <input disabled/> foo </span>\n\n </div>';
2731 output = '<div class="blah" style="color: red">\n test <span> <input disabled/> foo </span>\n\n </div>' +
2732 '<div class="blah" style="color: red">test <span><input disabled="disabled"> foo</span></div>';
2733 assert.equal(minify(input, { collapseWhitespace: true }), output);
2735 input = '<!-- htmlmin:ignore --><!-- htmlmin:ignore -->';
2736 assert.equal(minify(input), '');
2738 input = '<p>.....</p><!-- htmlmin:ignore -->' +
2739 '@for( $i = 0 ; $i < $criterions->count() ; $i++ )' +
2740 '<h1>{{ $criterions[$i]->value }}</h1>' +
2742 '<!-- htmlmin:ignore --><p>....</p>';
2743 output = '<p>.....</p>' +
2744 '@for( $i = 0 ; $i < $criterions->count() ; $i++ )' +
2745 '<h1>{{ $criterions[$i]->value }}</h1>' +
2748 assert.equal(minify(input, { removeComments: true }), output);
2750 input = '<!-- htmlmin:ignore --> <p class="logged"|cond="$is_logged === true" id="foo"> bar</p> <!-- htmlmin:ignore -->';
2751 output = ' <p class="logged"|cond="$is_logged === true" id="foo"> bar</p> ';
2752 assert.equal(minify(input), output);
2754 input = '<!-- htmlmin:ignore --><body <?php body_class(); ?>><!-- htmlmin:ignore -->';
2755 output = '<body <?php body_class(); ?>>';
2756 assert.equal(minify(input, { ignoreCustomFragments: [/<\?php[\s\S]*?\?>/] }), output);
2758 input = 'a\n<!-- htmlmin:ignore -->b<!-- htmlmin:ignore -->';
2760 assert.equal(minify(input, { collapseWhitespace: true }), output);
2762 input = '<p>foo <!-- htmlmin:ignore --><span>\n\tbar\n</span><!-- htmlmin:ignore -->.</p>';
2763 output = '<p>foo <span>\n\tbar\n</span>.</p>';
2764 assert.equal(minify(input, { collapseWhitespace: true }), output);
2767 QUnit.test('meta viewport', function(assert) {
2770 input = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
2771 output = '<meta name="viewport" content="width=device-width,initial-scale=1">';
2772 assert.equal(minify(input), output);
2774 input = '<meta name="viewport" content="initial-scale=1, maximum-scale=1.0">';
2775 output = '<meta name="viewport" content="initial-scale=1,maximum-scale=1">';
2776 assert.equal(minify(input), output);
2778 input = '<meta name="viewport" content="width= 500 , initial-scale=1">';
2779 output = '<meta name="viewport" content="width=500,initial-scale=1">';
2780 assert.equal(minify(input), output);
2782 input = '<meta name="viewport" content="width=device-width, initial-scale=1.0001, maximum-scale=3.140000">';
2783 output = '<meta name="viewport" content="width=device-width,initial-scale=1.0001,maximum-scale=3.14">';
2784 assert.equal(minify(input), output);
2787 QUnit.test('downlevel-revealed conditional comments', function(assert) {
2788 var input = '<![if !IE]><link href="non-ie.css" rel="stylesheet"><![endif]>';
2789 assert.equal(minify(input), input);
2790 assert.equal(minify(input, { removeComments: true }), input);
2793 QUnit.test('noscript', function(assert) {
2796 input = '<SCRIPT SRC="x"></SCRIPT><NOSCRIPT>x</NOSCRIPT>';
2797 assert.equal(minify(input), '<script src="x"></script><noscript>x</noscript>');
2799 input = '<noscript>\n<!-- anchor linking to external file -->\n' +
2800 '<a href="#" onclick="javascript:">External Link</a>\n</noscript>';
2801 assert.equal(minify(input, { removeComments: true, collapseWhitespace: true, removeEmptyAttributes: true }),
2802 '<noscript><a href="#">External Link</a></noscript>');
2805 QUnit.test('max line length', function(assert) {
2807 var options = { maxLineLength: 25 };
2809 input = '123456789012345678901234567890';
2810 assert.equal(minify(input, options), input);
2812 input = '<div data-attr="foo"></div>';
2813 assert.equal(minify(input, options), '<div data-attr="foo">\n</div>');
2815 input = '<code> hello world \n world hello </code>';
2816 assert.equal(minify(input, options), '<code>\n hello world \n world hello \n</code>');
2818 assert.equal(minify('<p title="</p>">x</p>'), '<p title="</p>">x</p>');
2819 assert.equal(minify('<p title=" <!-- hello world --> ">x</p>'), '<p title=" <!-- hello world --> ">x</p>');
2820 assert.equal(minify('<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>'), '<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>');
2821 assert.equal(minify('<p foo-bar=baz>xxx</p>'), '<p foo-bar="baz">xxx</p>');
2822 assert.equal(minify('<p foo:bar=baz>xxx</p>'), '<p foo:bar="baz">xxx</p>');
2824 input = '<div><div><div><div><div><div><div><div><div><div>' +
2825 'i\'m 10 levels deep' +
2826 '</div></div></div></div></div></div></div></div></div></div>';
2827 assert.equal(minify(input), input);
2829 assert.equal(minify('<script>alert(\'<!--\')</script>', options), '<script>alert(\'<!--\')\n</script>');
2830 input = '<script>\nalert(\'<!-- foo -->\')\n</script>';
2831 assert.equal(minify('<script>alert(\'<!-- foo -->\')</script>', options), input);
2832 assert.equal(minify(input, options), input);
2833 assert.equal(minify('<script>alert(\'-->\')</script>', options), '<script>alert(\'-->\')\n</script>');
2835 assert.equal(minify('<a title="x"href=" ">foo</a>', options), '<a title="x" href="">foo\n</a>');
2836 assert.equal(minify('<p id=""class=""title="">x', options), '<p id="" class="" \ntitle="">x</p>');
2837 assert.equal(minify('<p x="x\'"">x</p>', options), '<p x="x\'">x</p>', 'trailing quote should be ignored');
2838 assert.equal(minify('<a href="#"><p>Click me</p></a>', options), '<a href="#"><p>Click me\n</p></a>');
2839 input = '<span><button>Hit me\n</button></span>';
2840 assert.equal(minify('<span><button>Hit me</button></span>', options), input);
2841 assert.equal(minify(input, options), input);
2842 assert.equal(minify('<object type="image/svg+xml" data="image.svg"><div>[fallback image]</div></object>', options),
2843 '<object \ntype="image/svg+xml" \ndata="image.svg"><div>\n[fallback image]</div>\n</object>'
2846 assert.equal(minify('<ng-include src="x"></ng-include>', options), '<ng-include src="x">\n</ng-include>');
2847 assert.equal(minify('<ng:include src="x"></ng:include>', options), '<ng:include src="x">\n</ng:include>');
2848 assert.equal(minify('<ng-include src="\'views/partial-notification.html\'"></ng-include><div ng-view=""></div>', options),
2849 '<ng-include \nsrc="\'views/partial-notification.html\'">\n</ng-include><div \nng-view=""></div>'
2851 assert.equal(minify('<some-tag-1></some-tag-1><some-tag-2></some-tag-2>', options),
2852 '<some-tag-1>\n</some-tag-1>\n<some-tag-2>\n</some-tag-2>'
2854 assert.equal(minify('[\']["]', options), '[\']["]');
2855 assert.equal(minify('<a href="test.html"><div>hey</div></a>', options), '<a href="test.html">\n<div>hey</div></a>');
2856 assert.equal(minify(':) <a href="http://example.com">link</a>', options), ':) <a \nhref="http://example.com">\nlink</a>');
2857 assert.equal(minify(':) <a href="http://example.com">\nlink</a>', options), ':) <a \nhref="http://example.com">\nlink</a>');
2858 assert.equal(minify(':) <a href="http://example.com">\n\nlink</a>', options), ':) <a \nhref="http://example.com">\n\nlink</a>');
2860 assert.equal(minify('<a href>ok</a>', options), '<a href>ok</a>');
2863 QUnit.test('custom attribute collapse', function(assert) {
2866 input = '<div data-bind="\n' +
2868 'fadeIn: selected(),\n' +
2869 'fadeOut: !selected()\n' +
2871 'visible: function () {\n' +
2872 'return pageWeAreOn() == \'home\';\n' +
2875 output = '<div data-bind="css: {fadeIn: selected(),fadeOut: !selected()},visible: function () {return pageWeAreOn() == \'home\';}">foo</div>';
2877 assert.equal(minify(input), input);
2878 assert.equal(minify(input, { customAttrCollapse: /data-bind/ }), output);
2880 input = '<div style="' +
2882 'font-size: 100em;' +
2884 output = '<div style="color: red;font-size: 100em;">bar</div>';
2885 assert.equal(minify(input, { customAttrCollapse: /style/ }), output);
2888 'class="fragment square" ' +
2889 'ng-hide="square1.hide" ' +
2890 'ng-class="{ \n\n' +
2891 '\'bounceInDown\': !square1.hide, ' +
2892 '\'bounceOutDown\': square1.hide ' +
2896 output = '<div class="fragment square" ng-hide="square1.hide" ng-class="{\'bounceInDown\': !square1.hide, \'bounceOutDown\': square1.hide }"> </div>';
2897 assert.equal(minify(input, { customAttrCollapse: /ng-class/ }), output);
2900 QUnit.test('custom attribute collapse with empty attribute value', function(assert) {
2901 var input = '<div ng-some\n\n></div>';
2902 var output = '<div ng-some></div>';
2903 assert.equal(minify(input, { customAttrCollapse: /.+/ }), output);
2906 QUnit.test('custom attribute collapse with newlines, whitespace, and carriage returns', function(assert) {
2907 var input = '<div ng-class="{ \n\r' +
2908 ' value:true, \n\r' +
2909 ' value2:false \n\r' +
2911 var output = '<div ng-class="{value:true,value2:false}"></div>';
2912 assert.equal(minify(input, { customAttrCollapse: /ng-class/ }), output);
2915 QUnit.test('do not escape attribute value', function(assert) {
2918 input = '<div data=\'{\n' +
2919 '\t"element": "<div class=\\"test\\"></div>\n"' +
2921 assert.equal(minify(input), input);
2922 assert.equal(minify(input, { preventAttributesEscaping: true }), input);
2924 input = '<div foo bar=\'\' baz="" moo=1 loo=\'2\' haa="3"></div>';
2925 assert.equal(minify(input, { preventAttributesEscaping: true }), input);
2926 output = '<div foo bar="" baz="" moo="1" loo="2" haa="3"></div>';
2927 assert.equal(minify(input), output);
2930 QUnit.test('quoteCharacter is single quote', function(assert) {
2931 assert.equal(minify('<div class=\'bar\'>foo</div>', { quoteCharacter: '\'' }), '<div class=\'bar\'>foo</div>');
2932 assert.equal(minify('<div class="bar">foo</div>', { quoteCharacter: '\'' }), '<div class=\'bar\'>foo</div>');
2935 QUnit.test('quoteCharacter is not single quote or double quote', function(assert) {
2936 assert.equal(minify('<div class=\'bar\'>foo</div>', { quoteCharacter: 'm' }), '<div class="bar">foo</div>');
2937 assert.equal(minify('<div class="bar">foo</div>', { quoteCharacter: 'm' }), '<div class="bar">foo</div>');
2940 QUnit.test('remove space between attributes', function(assert) {
2943 collapseBooleanAttributes: true,
2944 keepClosingSlash: true,
2945 removeAttributeQuotes: true,
2946 removeTagWhitespace: true
2949 input = '<input data-attr="example" value="hello world!" checked="checked">';
2950 output = '<input data-attr=example value="hello world!"checked>';
2951 assert.equal(minify(input, options), output);
2953 input = '<input checked="checked" value="hello world!" data-attr="example">';
2954 output = '<input checked value="hello world!"data-attr=example>';
2955 assert.equal(minify(input, options), output);
2957 input = '<input checked="checked" data-attr="example" value="hello world!">';
2958 output = '<input checked data-attr=example value="hello world!">';
2959 assert.equal(minify(input, options), output);
2961 input = '<input data-attr="example" value="hello world!" checked="checked"/>';
2962 output = '<input data-attr=example value="hello world!"checked/>';
2963 assert.equal(minify(input, options), output);
2965 input = '<input checked="checked" value="hello world!" data-attr="example"/>';
2966 output = '<input checked value="hello world!"data-attr=example />';
2967 assert.equal(minify(input, options), output);
2969 input = '<input checked="checked" data-attr="example" value="hello world!"/>';
2970 output = '<input checked data-attr=example value="hello world!"/>';
2971 assert.equal(minify(input, options), output);
2974 QUnit.test('markups from Angular 2', function(assert) {
2976 input = '<template ngFor #hero [ngForOf]="heroes">\n' +
2977 ' <hero-detail *ngIf="hero" [hero]="hero"></hero-detail>\n' +
2979 '<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">\n' +
2980 ' <div class="form-group">\n' +
2981 ' <label for="name">Name</label>\n' +
2982 ' <input class="form-control" required ngControl="firstName"\n' +
2983 ' [(ngModel)]="currentHero.firstName">\n' +
2985 ' <button type="submit" [disabled]="!theForm.form.valid">Submit</button>\n' +
2987 output = '<template ngFor #hero [ngForOf]="heroes">\n' +
2988 ' <hero-detail *ngIf="hero" [hero]="hero"></hero-detail>\n' +
2990 '<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">\n' +
2991 ' <div class="form-group">\n' +
2992 ' <label for="name">Name</label>\n' +
2993 ' <input class="form-control" required ngControl="firstName" [(ngModel)]="currentHero.firstName">\n' +
2995 ' <button type="submit" [disabled]="!theForm.form.valid">Submit</button>\n' +
2997 assert.equal(minify(input, { caseSensitive: true }), output);
2998 output = '<template ngFor #hero [ngForOf]=heroes>' +
2999 '<hero-detail *ngIf=hero [hero]=hero></hero-detail>' +
3001 '<form (ngSubmit)=onSubmit(theForm) #theForm=ngForm>' +
3002 '<div class=form-group>' +
3003 '<label for=name>Name</label>' +
3004 ' <input class=form-control required ngControl=firstName [(ngModel)]=currentHero.firstName>' +
3006 '<button type=submit [disabled]=!theForm.form.valid>Submit</button>' +
3008 assert.equal(minify(input, {
3009 caseSensitive: true,
3010 collapseBooleanAttributes: true,
3011 collapseWhitespace: true,
3012 removeAttributeQuotes: true,
3013 removeComments: true,
3014 removeEmptyAttributes: true,
3015 removeOptionalTags: true,
3016 removeRedundantAttributes: true,
3017 removeScriptTypeAttributes: true,
3018 removeStyleLinkTypeAttributes: true,
3019 removeTagWhitespace: true,
3020 useShortDoctype: true
3024 QUnit.test('auto-generated tags', function(assert) {
3028 assert.equal(minify(input, { includeAutoGeneratedTags: false }), input);
3030 input = '<p id=""class=""title="">x';
3031 output = '<p id="" class="" title="">x';
3032 assert.equal(minify(input, { includeAutoGeneratedTags: false }), output);
3033 output = '<p id="" class="" title="">x</p>';
3034 assert.equal(minify(input), output);
3035 assert.equal(minify(input, { includeAutoGeneratedTags: true }), output);
3037 input = '<body onload=" foo(); bar() ; "><p>x</body>';
3038 output = '<body onload="foo(); bar() ;"><p>x</body>';
3039 assert.equal(minify(input, { includeAutoGeneratedTags: false }), output);
3041 input = '<a href="#"><div>Well, look at me! I\'m a div!</div></a>';
3042 output = '<a href="#"><div>Well, look at me! I\'m a div!</div>';
3043 assert.equal(minify(input, { html5: false, includeAutoGeneratedTags: false }), output);
3044 assert.equal(minify('<p id=""class=""title="">x', {
3046 includeAutoGeneratedTags: false
3047 }), '<p id="" class="" \ntitle="">x');
3050 assert.equal(minify(input, { includeAutoGeneratedTags: false }), input);
3051 assert.equal(minify(input, {
3052 includeAutoGeneratedTags: false,
3053 removeOptionalTags: true
3057 assert.equal(minify(input, { includeAutoGeneratedTags: false }), input);
3059 assert.equal(minify(input, {
3060 includeAutoGeneratedTags: false,
3061 removeOptionalTags: true
3064 input = '<select><option>foo<option>bar</select>';
3065 assert.equal(minify(input, { includeAutoGeneratedTags: false }), input);
3066 output = '<select><option>foo</option><option>bar</option></select>';
3067 assert.equal(minify(input, { includeAutoGeneratedTags: true }), output);
3069 input = '<datalist><option label="A" value="1"><option label="B" value="2"></datalist>';
3070 assert.equal(minify(input, { includeAutoGeneratedTags: false }), input);
3071 output = '<datalist><option label="A" value="1"></option><option label="B" value="2"></option></datalist>';
3072 assert.equal(minify(input, { includeAutoGeneratedTags: true }), output);
3075 QUnit.test('sort attributes', function(assert) {
3078 input = '<link href="foo">' +
3079 '<link rel="bar" href="baz">' +
3080 '<link type="text/css" href="app.css" rel="stylesheet" async>';
3081 assert.equal(minify(input), input);
3082 assert.equal(minify(input, { sortAttributes: false }), input);
3083 output = '<link href="foo">' +
3084 '<link href="baz" rel="bar">' +
3085 '<link href="app.css" rel="stylesheet" async type="text/css">';
3086 assert.equal(minify(input, { sortAttributes: true }), output);
3088 input = '<link href="foo">' +
3089 '<link rel="bar" href="baz">' +
3090 '<script type="text/html"><link type="text/css" href="app.css" rel="stylesheet" async></script>';
3091 assert.equal(minify(input), input);
3092 assert.equal(minify(input, { sortAttributes: false }), input);
3093 output = '<link href="foo">' +
3094 '<link href="baz" rel="bar">' +
3095 '<script type="text/html"><link type="text/css" href="app.css" rel="stylesheet" async></script>';
3096 assert.equal(minify(input, { sortAttributes: true }), output);
3097 output = '<link href="foo">' +
3098 '<link href="baz" rel="bar">' +
3099 '<script type="text/html"><link href="app.css" rel="stylesheet" async type="text/css"></script>';
3100 assert.equal(minify(input, {
3104 sortAttributes: true
3107 input = '<link type="text/css" href="foo.css">' +
3108 '<link rel="stylesheet" type="text/abc" href="bar.css">' +
3109 '<link href="baz.css">';
3110 output = '<link href="foo.css" type="text/css">' +
3111 '<link href="bar.css" type="text/abc" rel="stylesheet">' +
3112 '<link href="baz.css">';
3113 assert.equal(minify(input, { sortAttributes: true }), output);
3114 output = '<link href="foo.css">' +
3115 '<link href="bar.css" rel="stylesheet" type="text/abc">' +
3116 '<link href="baz.css">';
3117 assert.equal(minify(input, {
3118 removeStyleLinkTypeAttributes: true,
3119 sortAttributes: true
3122 input = '<a foo moo></a>' +
3124 '<a baz bar foo></a>' +
3125 '<a baz foo moo></a>' +
3127 assert.equal(minify(input), input);
3128 assert.equal(minify(input, { sortAttributes: false }), input);
3129 output = '<a foo moo></a>' +
3131 '<a foo bar baz></a>' +
3132 '<a foo baz moo></a>' +
3134 assert.equal(minify(input, { sortAttributes: true }), output);
3136 input = '<span nav_sv_fo_v_column <#=(j === 0) ? \'nav_sv_fo_v_first\' : \'\' #> foo_bar></span>';
3137 assert.equal(minify(input, {
3138 ignoreCustomFragments: [/<#[\s\S]*?#>/]
3140 assert.equal(minify(input, {
3141 ignoreCustomFragments: [/<#[\s\S]*?#>/],
3142 sortAttributes: false
3144 output = '<span foo_bar nav_sv_fo_v_column <#=(j === 0) ? \'nav_sv_fo_v_first\' : \'\' #> ></span>';
3145 assert.equal(minify(input, {
3146 ignoreCustomFragments: [/<#[\s\S]*?#>/],
3147 sortAttributes: true
3150 input = '<a 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z></a>';
3151 assert.equal(minify(input, { sortAttributes: true }), input);
3154 QUnit.test('sort style classes', function(assert) {
3157 input = '<a class="foo moo"></a>' +
3158 '<b class="bar foo"></b>' +
3159 '<i class="baz bar foo"></i>' +
3160 '<s class="baz foo moo"></s>' +
3161 '<u class="moo baz"></u>';
3162 assert.equal(minify(input), input);
3163 assert.equal(minify(input, { sortClassName: false }), input);
3164 output = '<a class="foo moo"></a>' +
3165 '<b class="foo bar"></b>' +
3166 '<i class="foo bar baz"></i>' +
3167 '<s class="foo baz moo"></s>' +
3168 '<u class="baz moo"></u>';
3169 assert.equal(minify(input, { sortClassName: true }), output);
3171 input = '<a class="moo <!-- htmlmin:ignore -->bar<!-- htmlmin:ignore --> foo baz"></a>';
3172 output = '<a class="moo bar foo baz"></a>';
3173 assert.equal(minify(input), output);
3174 assert.equal(minify(input, { sortClassName: false }), output);
3175 output = '<a class="baz foo moo bar"></a>';
3176 assert.equal(minify(input, { sortClassName: true }), output);
3178 input = '<div class="nav_sv_fo_v_column <#=(j === 0) ? \'nav_sv_fo_v_first\' : \'\' #> foo_bar"></div>';
3179 assert.equal(minify(input, {
3180 ignoreCustomFragments: [/<#[\s\S]*?#>/]
3182 assert.equal(minify(input, {
3183 ignoreCustomFragments: [/<#[\s\S]*?#>/],
3184 sortClassName: false
3186 assert.equal(minify(input, {
3187 ignoreCustomFragments: [/<#[\s\S]*?#>/],
3191 input = '<a class="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z"></a>';
3192 assert.equal(minify(input, { sortClassName: false }), input);
3193 assert.equal(minify(input, { sortClassName: true }), input);
3195 input = '<a class="add sort keys createSorter"></a>';
3196 assert.equal(minify(input, { sortClassName: false }), input);
3197 output = '<a class="add createSorter keys sort"></a>';
3198 assert.equal(minify(input, { sortClassName: true }), output);
3200 input = '<span class="sprite sprite-{{sprite}}"></span>';
3201 assert.equal(minify(input, {
3202 collapseWhitespace: true,
3203 ignoreCustomFragments: [/{{.*?}}/],
3204 removeAttributeQuotes: true,
3208 input = '<span class="{{sprite}}-sprite sprite"></span>';
3209 assert.equal(minify(input, {
3210 collapseWhitespace: true,
3211 ignoreCustomFragments: [/{{.*?}}/],
3212 removeAttributeQuotes: true,
3216 input = '<span class="sprite-{{sprite}}-sprite"></span>';
3217 assert.equal(minify(input, {
3218 collapseWhitespace: true,
3219 ignoreCustomFragments: [/{{.*?}}/],
3220 removeAttributeQuotes: true,
3224 input = '<span class="{{sprite}}"></span>';
3225 assert.equal(minify(input, {
3226 collapseWhitespace: true,
3227 ignoreCustomFragments: [/{{.*?}}/],
3228 removeAttributeQuotes: true,
3232 input = '<span class={{sprite}}></span>';
3233 output = '<span class="{{sprite}}"></span>';
3234 assert.equal(minify(input, {
3235 collapseWhitespace: true,
3236 ignoreCustomFragments: [/{{.*?}}/],
3237 removeAttributeQuotes: true,
3241 input = '<div class></div>';
3242 assert.equal(minify(input, { sortClassName: false }), input);
3243 assert.equal(minify(input, { sortClassName: true }), input);
3246 QUnit.test('decode entity characters', function(assert) {
3249 input = '<!-- ≠ -->';
3250 assert.equal(minify(input), input);
3251 assert.equal(minify(input, { decodeEntities: false }), input);
3252 assert.equal(minify(input, { decodeEntities: true }), input);
3254 input = '<script type="text/html">:</script>';
3255 assert.equal(minify(input), input);
3256 assert.equal(minify(input, { decodeEntities: false }), input);
3257 assert.equal(minify(input, { decodeEntities: true }), input);
3258 output = '<script type="text/html">:</script>';
3259 assert.equal(minify(input, { decodeEntities: true, processScripts: ['text/html'] }), output);
3261 input = '<div style="font: "monospace"">foo$</div>';
3262 assert.equal(minify(input), input);
3263 assert.equal(minify(input, { decodeEntities: false }), input);
3264 output = '<div style=\'font: "monospace"\'>foo$</div>';
3265 assert.equal(minify(input, { decodeEntities: true }), output);
3266 output = '<div style="font:"">foo$</div>';
3267 assert.equal(minify(input, { minifyCSS: true }), output);
3268 assert.equal(minify(input, { decodeEntities: false, minifyCSS: true }), output);
3269 output = '<div style="font:monospace">foo$</div>';
3270 assert.equal(minify(input, { decodeEntities: true, minifyCSS: true }), output);
3272 input = '<a href="/?foo=1&bar=<2>">baz<moo>©</a>';
3273 assert.equal(minify(input), input);
3274 assert.equal(minify(input, { decodeEntities: false }), input);
3275 output = '<a href="/?foo=1&bar=<2>">baz<moo>\u00a9</a>';
3276 assert.equal(minify(input, { decodeEntities: true }), output);
3278 input = '<? & ?>&<pre><? & ?>&</pre>';
3279 assert.equal(minify(input), input);
3280 assert.equal(minify(input, { collapseWhitespace: false, decodeEntities: false }), input);
3281 assert.equal(minify(input, { collapseWhitespace: true, decodeEntities: false }), input);
3282 output = '<? & ?>&<pre><? & ?>&</pre>';
3283 assert.equal(minify(input, { collapseWhitespace: false, decodeEntities: true }), output);
3284 assert.equal(minify(input, { collapseWhitespace: true, decodeEntities: true }), output);
3287 QUnit.test('tests from PHPTAL', function(assert) {
3289 // trailing </p> removed by minifier, but not by PHPTAL
3290 ['<p>foo bar baz', '<p>foo \t bar\n\n\n baz</p>'],
3291 ['<p>foo bar<pre> \tfoo\t \nbar </pre>', '<p>foo \t\n bar</p><pre> \tfoo\t \nbar </pre>'],
3292 ['<p>foo <a href="">bar </a>baz', '<p>foo <a href=""> bar </a> baz </p>'],
3293 ['<p>foo <a href="">bar </a>baz', ' <p>foo <a href=""> bar </a>baz </p>'],
3294 ['<p>foo<a href=""> bar </a>baz', ' <p> foo<a href=""> bar </a>baz </p> '],
3295 ['<p>foo <a href="">bar</a> baz', ' <p> foo <a href="">bar</a> baz</p>'],
3296 ['<p>foo<br>', '<p>foo <br/></p>'],
3297 // PHPTAL remove whitespace after 'foo' - problematic if <span> is used as icon font
3298 ['<p>foo <span></span>', '<p>foo <span></span></p>'],
3299 ['<p>foo <span></span>', '<p>foo <span></span> </p>'],
3300 // comments removed by minifier, but not by PHPTAL
3301 ['<p>foo', '<p>foo <!-- --> </p>'],
3302 ['<div>a<div>b</div>c<div>d</div>e</div>', '<div>a <div>b</div> c <div> d </div> e </div>'],
3303 // unary slashes removed by minifier, but not by PHPTAL
3304 ['<div><img></div>', '<div> <img/> </div>'],
3305 ['<div>x <img></div>', '<div> x <img/> </div>'],
3306 ['<div>x <img> y</div>', '<div> x <img/> y </div>'],
3307 ['<div><img> y</div>', '<div><img/> y </div>'],
3308 ['<div><button>Z</button></div>', '<div> <button>Z</button> </div>'],
3309 ['<div>x <button>Z</button></div>', '<div> x <button>Z</button> </div>'],
3310 ['<div>x <button>Z</button> y</div>', '<div> x <button>Z</button> y </div>'],
3311 ['<div><button>Z</button> y</div>', '<div><button>Z</button> y </div>'],
3312 ['<div><button>Z</button></div>', '<div> <button> Z </button> </div>'],
3313 ['<div>x <button>Z</button></div>', '<div> x <button> Z </button> </div>'],
3314 ['<div>x <button>Z</button> y</div>', '<div> x <button> Z </button> y </div>'],
3315 ['<div><button>Z</button> y</div>', '<div><button> Z </button> y </div>'],
3316 ['<script>//foo\nbar()</script>', '<script>//foo\nbar()</script>'],
3317 // optional tags removed by minifier, but not by PHPTAL
3318 // parser cannot handle <script/>
3320 '<title></title><link><script>" ";</script><script></script><meta><style></style>',
3322 '<head > <title > </title > <link /> <script >" ";</script> <script>\n</script>\n' +
3323 ' <meta /> <style\n' +
3327 ['<div><p>test 123<p>456<ul><li>x</ul></div>', '<div> <p> test 123 </p> <p> 456 </p> <ul> <li>x</li> </ul> </div>'],
3328 ['<div><p>test 123<pre> 456 </pre><p>x</div>', '<div> <p> test 123 </p> <pre> 456 </pre> <p> x </p> </div>'],
3329 /* minifier does not assume <li> as "display: inline"
3330 ['<div><ul><li><a>a </a></li><li>b </li><li>c</li></ul></div>', '<div> <ul> <li> <a> a </a> </li> <li> b </li> <li> c </li> </ul> </div>'],*/
3331 ['<table>x<tr>x<td>foo</td>x</tr>x</table>', '<table> x <tr> x <td> foo </td> x </tr> x </table>'],
3332 ['<select>x<option></option>x<optgroup>x<option></option>x</optgroup>x</select>', '<select> x <option> </option> x <optgroup> x <option> </option> x </optgroup> x </select> '],
3333 // closing slash and optional attribute quotes removed by minifier, but not by PHPTAL
3334 // attribute ordering differences between minifier and PHPTAL
3335 ['<img alt=x height=5 src=foo width=10>', '<img width="10" height="5" src="foo" alt="x" />'],
3336 ['<img alpha=1 beta=2 gamma=3>', '<img gamma="3" alpha="1" beta="2" />'],
3337 ['<pre>\n\n\ntest</pre>', '<pre>\n\n\ntest</pre>'],
3338 /* single line-break preceding <pre> is redundant, assuming <pre> is block element
3339 ['<pre>test</pre>', '<pre>\ntest</pre>'],*/
3340 // closing slash and optional attribute quotes removed by minifier, but not by PHPTAL
3341 // attribute ordering differences between minifier and PHPTAL
3342 // redundant inter-attribute spacing removed by minifier, but not by PHPTAL
3343 ['<meta content="text/plain;charset=UTF-8"http-equiv=Content-Type>', '<meta http-equiv=\'Content-Type\' content=\'text/plain;charset=UTF-8\'/>'],
3344 /* minifier does not optimise <meta/> in HTML5 mode
3345 ['<meta charset=utf-8>', '<meta http-equiv=\'Content-Type\' content=\'text/plain;charset=UTF-8\'/>'],*/
3346 /* minifier does not optimise <script/> in HTML5 mode
3348 '<script></script><style></style>',
3349 '<script type=\'text/javascript ;charset=utf-8\'\n' +
3350 'language=\'javascript\'></script><style type=\'text/css\'></style>'
3352 // minifier removes more javascript type attributes than PHPTAL
3353 ['<script></script><script type=text/hack></script>', '<script type="text/javascript;e4x=1"></script><script type="text/hack"></script>']
3354 /* trim "title" attribute value in <a>
3356 '<title>Foo</title><p><a title="x"href=test>x </a>xu</p><br>foo',
3357 '<html> <head> <title> Foo </title> </head>\n' +
3360 '<a title=" x " href=" test "> x </a> xu\n' +
3363 'foo</body> </html> <!-- bla -->'
3365 ].forEach(function(tokens) {
3366 assert.equal(minify(tokens[1], {
3367 collapseBooleanAttributes: true,
3368 collapseWhitespace: true,
3369 removeAttributeQuotes: true,
3370 removeComments: true,
3371 removeEmptyAttributes: true,
3372 removeOptionalTags: true,
3373 removeRedundantAttributes: true,
3374 removeScriptTypeAttributes: true,
3375 removeStyleLinkTypeAttributes: true,
3376 removeTagWhitespace: true,
3377 sortAttributes: true,
3378 useShortDoctype: true
3383 QUnit.test('canCollapseWhitespace and canTrimWhitespace hooks', function(assert) {
3384 function canCollapseAndTrimWhitespace(tagName, attrs, defaultFn) {
3385 if ((attrs || []).some(function(attr) { return attr.name === 'class' && attr.value === 'leaveAlone'; })) {
3388 return defaultFn(tagName, attrs);
3391 var input = '<div class="leaveAlone"><span> </span> foo bar</div>';
3392 var output = '<div class="leaveAlone"><span> </span> foo bar</div>';
3394 assert.equal(minify(input, {
3395 collapseWhitespace: true,
3396 canTrimWhitespace: canCollapseAndTrimWhitespace,
3397 canCollapseWhitespace: canCollapseAndTrimWhitespace
3400 // Regression test: Previously the first </div> would clear the internal
3401 // stackNo{Collapse,Trim}Whitespace, so that ' foo bar' turned into ' foo bar'
3402 input = '<div class="leaveAlone"><div></div><span> </span> foo bar</div>';
3403 output = '<div class="leaveAlone"><div></div><span> </span> foo bar</div>';
3405 assert.equal(minify(input, {
3406 collapseWhitespace: true,
3407 canTrimWhitespace: canCollapseAndTrimWhitespace,
3408 canCollapseWhitespace: canCollapseAndTrimWhitespace
3411 // Make sure that the stack does get reset when leaving the element for which
3412 // the hooks returned false:
3413 input = '<div class="leaveAlone"></div><div> foo bar </div>';
3414 output = '<div class="leaveAlone"></div><div>foo bar</div>';
3416 assert.equal(minify(input, {
3417 collapseWhitespace: true,
3418 canTrimWhitespace: canCollapseAndTrimWhitespace,
3419 canCollapseWhitespace: canCollapseAndTrimWhitespace