├── v8 └── test.js ├── tests ├── index.html ├── lint-tests.html ├── lint_test.js ├── qunit.css ├── minify_test.js └── qunit.js ├── README.md ├── package.json ├── src ├── license.txt ├── htmllint.js ├── htmlparser.js └── htmlminifier.js ├── master.css ├── master.js └── index.html /v8/test.js: -------------------------------------------------------------------------------- 1 | load('../src/htmlparser.js'); 2 | load('../src/htmlminifier.js'); 3 | 4 | var input = read('test.html'), 5 | t1 = new Date(), 6 | output = minify(input); 7 | 8 | print('minified in: ' + (new Date() - t1) + 'ms'); -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 | 6 |6 | npm install html-minifier 7 |8 | 9 | See [corresponding blog post](http://perfectionkills.com/experimenting-with-html-minifier/) for all the gory details of [how it works](http://perfectionkills.com/experimenting-with-html-minifier/#how_it_works), [description of each option](http://perfectionkills.com/experimenting-with-html-minifier/#options), [testing results](http://perfectionkills.com/experimenting-with-html-minifier/#field_testing) and [conclusions](http://perfectionkills.com/experimenting-with-html-minifier/#cost_and_benefits). 10 | 11 | [Test suite is available online](http://kangax.github.com/html-minifier/tests/index.html). -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-minifier", 3 | "description": "HTML minifier with lint-like capabilities.", 4 | "version": "0.4.5", 5 | "keywords": ["html", "minifier", "lint"], 6 | "url" : "http://github.com/kangax/html-minifier", 7 | "maintainers": [{ 8 | "name": "Juriy Zaytsev", 9 | "email": "kangax@gmail.com", 10 | "web": "http://perfectionkills.com" 11 | }], 12 | "contributors": [{ 13 | "name": "Gilmore Davidson", 14 | "web": "https://github.com/gilmoreorless" 15 | }, 16 | { 17 | "name": "Hugo Wetterberg", 18 | "email": "hugo@wetterberg.nu" 19 | }], 20 | "licenses": [{ 21 | "type": "MIT", 22 | "url": "https://github.com/kangax/html-minifier/blob/gh-pages/src/license.txt" 23 | }], 24 | "repository": "git://github.com/kangax/html-minifier", 25 | "engines": { 26 | "node": ">=0.4.8" 27 | }, 28 | "directories": { 29 | "src": "./src" 30 | }, 31 | "main": "./src/htmlminifier.js" 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Juriy "kangax" Zaytsev 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /tests/lint_test.js: -------------------------------------------------------------------------------- 1 | (function(global){ 2 | 3 | var minify, QUnit, 4 | test, equal, ok, 5 | input, output, HTMLLint, lint; 6 | 7 | if (typeof require === 'function') { 8 | QUnit = require('./qunit'); 9 | minify = require('../src/htmlminifier').minify; 10 | HTMLLint = require('../src/htmllint').HTMLLint; 11 | } else { 12 | QUnit = global.QUnit; 13 | minify = global.minify; 14 | HTMLLint = global.HTMLLint; 15 | } 16 | 17 | test = QUnit.test; 18 | equal = QUnit.equal; 19 | ok = QUnit.ok; 20 | 21 | QUnit.module('', { 22 | setup: function() { 23 | lint = new HTMLLint(); 24 | } 25 | }); 26 | 27 | test('lint exists', function() { 28 | ok(typeof lint !== 'undefined'); 29 | }); 30 | 31 | test('lint is instance of HTMLLint', function() { 32 | ok(lint instanceof HTMLLint); 33 | }); 34 | 35 | test('lint API', function() { 36 | equal(0, lint.log.length, '`log` property exists'); 37 | equal("function", typeof lint.populate, '`populate` method exists'); 38 | equal("function", typeof lint.test, '`test` method exists'); 39 | equal("function", typeof lint.testElement, '`testElement` method exists'); 40 | equal("function", typeof lint.testAttribute, '`testAttribute` method exists'); 41 | }); 42 | 43 | test('deprecated element (font)', function(){ 44 | minify('foo', { lint: lint }); 45 | var log = lint.log.join(''); 46 | 47 | ok(log.indexOf('font') > -1); 48 | ok(log.indexOf('deprecated') > -1); 49 | ok(log.indexOf('element') > -1); 50 | }); 51 | 52 | }(this)); -------------------------------------------------------------------------------- /master.css: -------------------------------------------------------------------------------- 1 | body { font-family: "Cambria", Georgia, Times, "Times New Roman", serif; margin-top: 0; padding-top: 0; } 2 | textarea { height: 30em; } 3 | h1 { margin-top: 0.5em; font-size: 1.25em; } 4 | button { font-weight: bold; width: 100px; } 5 | 6 | #outer-wrapper { overflow: hidden; } 7 | #wrapper { width: 65%; float: left; } 8 | #input { width: 99%; height: 18em; } 9 | #output { width: 99%; height: 18em; margin-bottom: 2em; } 10 | #options { float: right; width: 33%; padding-left: 1em; margin-top: 3em; } 11 | #options ul { list-style: none; padding: 0.5em; overflow: hidden; background: #ffe; margin-top: 0; } 12 | #options ul li { float: left; clear: both; padding-bottom: 0.5em; } 13 | #options ul li div { margin-left: 1.75em; } 14 | #options label, #options input { float: left; } 15 | #options input { margin-right: 0.5em; } 16 | #stats { margin-bottom: 2em; overflow: hidden; margin-top: 0; } 17 | #todo { font-family: monospace; margin-bottom: 2em; } 18 | #warning { background: #fcc; padding: 0.25em; display: inline-block; margin-top: 0; font-size: 0.85em; } 19 | #lint-report { font-family: monospace; } 20 | #report { margin-bottom: 5em; } 21 | #report ul { margin: 0.5em; padding-left: 1em; list-style: none; } 22 | 23 | .success { color: green; } 24 | .failure { color: red; } 25 | .quiet { font-size: 0.85em; color: #888; } 26 | .short { display: inline-block; width: 20em; margin-top: 0.25em; } 27 | 28 | .controls span { margin-right: 0.5em; margin-left: 1em; } 29 | .controls a { margin-left: 0.1em; } 30 | .controls a:focus, .controls a:hover { text-decoration: none; } 31 | 32 | .deprecated-element, .deprecated-attribute { color: red; } 33 | .presentational-element, .presentational-attribute, .inaccessible-attribute { color: #FF8C00; } 34 | 35 | .unsafe { color: #f33; } -------------------------------------------------------------------------------- /tests/qunit.css: -------------------------------------------------------------------------------- 1 | ol#qunit-tests { 2 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 3 | margin:0; 4 | padding:0; 5 | list-style-position:inside; 6 | 7 | font-size: smaller; 8 | } 9 | ol#qunit-tests li{ 10 | padding:0.4em 0.5em 0.4em 2.5em; 11 | border-bottom:1px solid #fff; 12 | font-size:small; 13 | list-style-position:inside; 14 | } 15 | ol#qunit-tests li ol{ 16 | box-shadow: inset 0px 2px 13px #999; 17 | -moz-box-shadow: inset 0px 2px 13px #999; 18 | -webkit-box-shadow: inset 0px 2px 13px #999; 19 | margin-top:0.5em; 20 | margin-left:0; 21 | padding:0.5em; 22 | background-color:#fff; 23 | border-radius:15px; 24 | -moz-border-radius: 15px; 25 | -webkit-border-radius: 15px; 26 | } 27 | ol#qunit-tests li li{ 28 | border-bottom:none; 29 | margin:0.5em; 30 | background-color:#fff; 31 | list-style-position: inside; 32 | padding:0.4em 0.5em 0.4em 0.5em; 33 | } 34 | 35 | ol#qunit-tests li li.pass{ 36 | border-left:26px solid #C6E746; 37 | background-color:#fff; 38 | color:#5E740B; 39 | } 40 | ol#qunit-tests li li.fail{ 41 | border-left:26px solid #EE5757; 42 | background-color:#fff; 43 | color:#710909; 44 | } 45 | ol#qunit-tests li.pass{ 46 | background-color:#D2E0E6; 47 | color:#528CE0; 48 | } 49 | ol#qunit-tests li.fail{ 50 | background-color:#EE5757; 51 | color:#000; 52 | } 53 | ol#qunit-tests li strong { 54 | cursor:pointer; 55 | } 56 | h1#qunit-header{ 57 | background-color:#0d3349; 58 | margin:0; 59 | padding:0.5em 0 0.5em 1em; 60 | color:#fff; 61 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 62 | border-top-right-radius:15px; 63 | border-top-left-radius:15px; 64 | -moz-border-radius-topright:15px; 65 | -moz-border-radius-topleft:15px; 66 | -webkit-border-top-right-radius:15px; 67 | -webkit-border-top-left-radius:15px; 68 | text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px; 69 | } 70 | h2#qunit-banner{ 71 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 72 | height:5px; 73 | margin:0; 74 | padding:0; 75 | } 76 | h2#qunit-banner.qunit-pass{ 77 | background-color:#C6E746; 78 | } 79 | h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar { 80 | background-color:#EE5757; 81 | } 82 | #qunit-testrunner-toolbar { 83 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 84 | padding:0; 85 | /*width:80%;*/ 86 | padding:0em 0 0.5em 2em; 87 | font-size: small; 88 | } 89 | h2#qunit-userAgent { 90 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 91 | background-color:#2b81af; 92 | margin:0; 93 | padding:0; 94 | color:#fff; 95 | font-size: small; 96 | padding:0.5em 0 0.5em 2.5em; 97 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 98 | } 99 | p#qunit-testresult{ 100 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 101 | margin:0; 102 | font-size: small; 103 | color:#2b81af; 104 | border-bottom-right-radius:15px; 105 | border-bottom-left-radius:15px; 106 | -moz-border-radius-bottomright:15px; 107 | -moz-border-radius-bottomleft:15px; 108 | -webkit-border-bottom-right-radius:15px; 109 | -webkit-border-bottom-left-radius:15px; 110 | background-color:#D2E0E6; 111 | padding:0.5em 0.5em 0.5em 2.5em; 112 | } 113 | strong b.fail{ 114 | color:#710909; 115 | } 116 | strong b.pass{ 117 | color:#5E740B; 118 | } -------------------------------------------------------------------------------- /master.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | function byId(id) { 4 | return document.getElementById(id); 5 | } 6 | 7 | function escapeHTML(str) { 8 | return (str + '').replace(/&/g, '&').replace(//g, '>'); 9 | } 10 | 11 | function getOptions() { 12 | return { 13 | removeComments: byId('remove-comments').checked, 14 | removeCommentsFromCDATA: byId('remove-comments-from-cdata').checked, 15 | removeCDATASectionsFromCDATA: byId('remove-cdata-sections-from-cdata').checked, 16 | collapseWhitespace: byId('collapse-whitespace').checked, 17 | collapseBooleanAttributes: byId('collapse-boolean-attributes').checked, 18 | removeAttributeQuotes: byId('remove-attribute-quotes').checked, 19 | removeRedundantAttributes: byId('remove-redundant-attributes').checked, 20 | useShortDoctype: byId('use-short-doctype').checked, 21 | removeEmptyAttributes: byId('remove-empty-attributes').checked, 22 | removeEmptyElements: byId('remove-empty-elements').checked, 23 | removeOptionalTags: byId('remove-optional-tags').checked, 24 | removeScriptTypeAttributes: byId('remove-script-type-attributes').checked, 25 | removeStyleLinkTypeAttributes: byId('remove-style-link-type-attributes').checked, 26 | lint: byId('use-htmllint').checked ? new HTMLLint() : null 27 | }; 28 | } 29 | 30 | function commify(str) { 31 | return String(str) 32 | .split('').reverse().join('') 33 | .replace(/(...)(?!$)/g, '$1,') 34 | .split('').reverse().join(''); 35 | } 36 | 37 | byId('minify-btn').onclick = function() { 38 | try { 39 | var options = getOptions(), 40 | lint = options.lint, 41 | originalValue = byId('input').value, 42 | minifiedValue = minify(originalValue, options), 43 | diff = originalValue.length - minifiedValue.length, 44 | savings = originalValue.length ? ((100 * diff) / originalValue.length).toFixed(2) : 0; 45 | 46 | byId('output').value = minifiedValue; 47 | 48 | byId('stats').innerHTML = 49 | '' + 50 | 'Original size: ' + commify(originalValue.length) + '' + 51 | '. Minified size: ' + commify(minifiedValue.length) + '' + 52 | '. Savings: ' + commify(diff) + ' (' + savings + '%).' + 53 | ''; 54 | 55 | if (lint) { 56 | lint.populate(byId('report')); 57 | } 58 | } 59 | catch(err) { 60 | byId('output').value = ''; 61 | byId('stats').innerHTML = '' + escapeHTML(err) + ''; 62 | } 63 | }; 64 | 65 | function setCheckedAttrOnCheckboxes(attrValue) { 66 | var checkboxes = byId('options').getElementsByTagName('input'); 67 | for (var i = checkboxes.length; i--; ) { 68 | checkboxes[i].checked = attrValue; 69 | } 70 | } 71 | 72 | byId('select-all').onclick = function() { 73 | setCheckedAttrOnCheckboxes(true); 74 | return false; 75 | }; 76 | 77 | byId('select-none').onclick = function() { 78 | setCheckedAttrOnCheckboxes(false); 79 | return false; 80 | }; 81 | 82 | byId('select-safe').onclick = function() { 83 | setCheckedAttrOnCheckboxes(true); 84 | var inputEls = byId('options').getElementsByTagName('input'); 85 | inputEls[10].checked = false; 86 | inputEls[11].checked = false; 87 | return false; 88 | }; 89 | 90 | })(); 91 | 92 | var _gaq = _gaq || []; 93 | _gaq.push(['_setAccount', 'UA-1128111-22']); 94 | _gaq.push(['_trackPageview']); 95 | 96 | (function() { 97 | var ga = document.createElement('script'); 98 | ga.type = 'text/javascript'; 99 | ga.async = true; 100 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 101 | document.getElementsByTagName('head')[0].appendChild(ga); 102 | })(); -------------------------------------------------------------------------------- /src/htmllint.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * HTMLLint (to be used in conjunction with HTMLMinifier) 3 | * 4 | * Copyright (c) 2010 Juriy "kangax" Zaytsev 5 | * Licensed under the MIT license. 6 | * 7 | */ 8 | 9 | (function(global){ 10 | 11 | function isPresentationalElement(tag) { 12 | return (/^(?:b|i|big|small|hr|blink|marquee)$/).test(tag); 13 | } 14 | function isDeprecatedElement(tag) { 15 | return (/^(?:applet|basefont|center|dir|font|isindex|menu|s|strike|u)$/).test(tag); 16 | } 17 | function isEventAttribute(attrName) { 18 | return (/^on[a-z]+/).test(attrName); 19 | } 20 | function isStyleAttribute(attrName) { 21 | return ('style' === attrName.toLowerCase()); 22 | } 23 | function isDeprecatedAttribute(tag, attrName) { 24 | return ( 25 | (attrName === 'align' && 26 | (/^(?:caption|applet|iframe|img|imput|object|legend|table|hr|div|h[1-6]|p)$/).test(tag)) || 27 | (attrName === 'alink' && tag === 'body') || 28 | (attrName === 'alt' && tag === 'applet') || 29 | (attrName === 'archive' && tag === 'applet') || 30 | (attrName === 'background' && tag === 'body') || 31 | (attrName === 'bgcolor' && (/^(?:table|t[rdh]|body)$/).test(tag)) || 32 | (attrName === 'border' && (/^(?:img|object)$/).test(tag)) || 33 | (attrName === 'clear' && tag === 'br') || 34 | (attrName === 'code' && tag === 'applet') || 35 | (attrName === 'codebase' && tag === 'applet') || 36 | (attrName === 'color' && (/^(?:base(?:font)?)$/).test(tag)) || 37 | (attrName === 'compact' && (/^(?:dir|[dou]l|menu)$/).test(tag)) || 38 | (attrName === 'face' && (/^base(?:font)?$/).test(tag)) || 39 | (attrName === 'height' && (/^(?:t[dh]|applet)$/).test(tag)) || 40 | (attrName === 'hspace' && (/^(?:applet|img|object)$/).test(tag)) || 41 | (attrName === 'language' && tag === 'script') || 42 | (attrName === 'link' && tag === 'body') || 43 | (attrName === 'name' && tag === 'applet') || 44 | (attrName === 'noshade' && tag === 'hr') || 45 | (attrName === 'nowrap' && (/^t[dh]$/).test(tag)) || 46 | (attrName === 'object' && tag === 'applet') || 47 | (attrName === 'prompt' && tag === 'isindex') || 48 | (attrName === 'size' && (/^(?:hr|font|basefont)$/).test(tag)) || 49 | (attrName === 'start' && tag === 'ol') || 50 | (attrName === 'text' && tag === 'body') || 51 | (attrName === 'type' && (/^(?:li|ol|ul)$/).test(tag)) || 52 | (attrName === 'value' && tag === 'li') || 53 | (attrName === 'version' && tag === 'html') || 54 | (attrName === 'vlink' && tag === 'body') || 55 | (attrName === 'vspace' && (/^(?:applet|img|object)$/).test(tag)) || 56 | (attrName === 'width' && (/^(?:hr|td|th|applet|pre)$/).test(tag)) 57 | ); 58 | } 59 | function isInaccessibleAttribute(attrName, attrValue) { 60 | return ( 61 | attrName === 'href' && 62 | (/^\s*javascript\s*:\s*void\s*(\s+0|\(\s*0\s*\))\s*$/i).test(attrValue) 63 | ); 64 | } 65 | 66 | function Lint() { 67 | this.log = [ ]; 68 | this._lastElement = null; 69 | this._isElementRepeated = false; 70 | } 71 | 72 | Lint.prototype.testElement = function(tag) { 73 | if (isDeprecatedElement(tag)) { 74 | this.log.push( 75 | '
<' +
76 | tag + '> element<' +
81 | tag + '> element<br> sequence. Try replacing it with styling.<' + tag + '> element<', tag, '> element<', tag, '> element<', tag, '> element) sequence. Try replacing it with styling.15 | Minifier is very draft and is not yet thoroughly tested. Use at your own risk. 16 |
17 | 18 |19 | 20 |
21 | 22 |<script language="Javascript" ...><form method="get" ...><input type="text" ...><script src="..." charset="..."><a id="..." name="..."><... onclick="javascript:..." ...>
74 | 162 | HTMLMinifier is made by kangax, 163 | using tweaked version of HTML parser by John Resig 164 | (which, in its turn, is based on work of Erik Arvidsson). 165 | Source and bugtracker are hosted on Github. 166 |
167 | within a )
301 | if (options.collapseWhitespace) {
302 | if (!_canTrimWhitespace(tag, attrs)) {
303 | stackNoTrimWhitespace.push(tag);
304 | }
305 | if (!_canCollapseWhitespace(tag, attrs)) {
306 | stackNoCollapseWhitespace.push(tag);
307 | }
308 | }
309 |
310 | buffer.push('<', tag);
311 |
312 | lint && lint.testElement(tag);
313 |
314 | for ( var i = 0, len = attrs.length; i < len; i++ ) {
315 | lint && lint.testAttribute(tag, attrs[i].name.toLowerCase(), attrs[i].escaped);
316 | buffer.push(normalizeAttribute(attrs[i], attrs, tag, options));
317 | }
318 |
319 | buffer.push('>');
320 | },
321 | end: function( tag ) {
322 | // check if current tag is in a whitespace stack
323 | if (options.collapseWhitespace) {
324 | if (stackNoTrimWhitespace.length &&
325 | tag == stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
326 | stackNoTrimWhitespace.pop();
327 | }
328 | if (stackNoCollapseWhitespace.length &&
329 | tag == stackNoCollapseWhitespace[stackNoCollapseWhitespace.length - 1]) {
330 | stackNoCollapseWhitespace.pop();
331 | }
332 | }
333 |
334 | var isElementEmpty = currentChars === '' && tag === currentTag;
335 | if ((options.removeEmptyElements && isElementEmpty && canRemoveElement(tag))) {
336 | // remove last "element" from buffer, return
337 | buffer.splice(buffer.lastIndexOf('<'));
338 | return;
339 | }
340 | else if (options.removeOptionalTags && isOptionalTag(tag)) {
341 | // noop, leave start tag in buffer
342 | return;
343 | }
344 | else {
345 | // push end tag to buffer
346 | buffer.push('', tag.toLowerCase(), '>');
347 | results.push.apply(results, buffer);
348 | }
349 | // flush buffer
350 | buffer.length = 0;
351 | currentChars = '';
352 | },
353 | chars: function( text ) {
354 | if (currentTag === 'script' || currentTag === 'style') {
355 | if (options.removeCommentsFromCDATA) {
356 | text = removeComments(text, currentTag);
357 | }
358 | if (options.removeCDATASectionsFromCDATA) {
359 | text = removeCDATASections(text);
360 | }
361 | }
362 | if (options.collapseWhitespace) {
363 | if (!stackNoTrimWhitespace.length && _canTrimWhitespace(currentTag, currentAttrs)) {
364 | text = trimWhitespace(text);
365 | }
366 | if (!stackNoCollapseWhitespace.length && _canCollapseWhitespace(currentTag, currentAttrs)) {
367 | text = collapseWhitespace(text);
368 | }
369 | }
370 | currentChars = text;
371 | lint && lint.testChars(text);
372 | buffer.push(text);
373 | },
374 | comment: function( text ) {
375 | if (options.removeComments) {
376 | if (isConditionalComment(text)) {
377 | text = '';
378 | }
379 | else {
380 | text = '';
381 | }
382 | }
383 | else {
384 | text = '';
385 | }
386 | buffer.push(text);
387 | },
388 | doctype: function(doctype) {
389 | buffer.push(options.useShortDoctype ? '' : collapseWhitespace(doctype));
390 | }
391 | });
392 |
393 | results.push.apply(results, buffer)
394 | var str = results.join('');
395 | log('minified in: ' + (new Date() - t) + 'ms');
396 | return str;
397 | }
398 |
399 | // for CommonJS enviroments, export everything
400 | if ( typeof exports !== "undefined" ) {
401 | exports.minify = minify;
402 | } else {
403 | global.minify = minify;
404 | }
405 |
406 | }(this));
--------------------------------------------------------------------------------
/tests/minify_test.js:
--------------------------------------------------------------------------------
1 | (function(global){
2 |
3 | var minify, QUnit,
4 | test, equal, ok,
5 | input, output;
6 |
7 | if (typeof require === 'function') {
8 | QUnit = require('./qunit');
9 | minify = require('../src/htmlminifier').minify;
10 | } else {
11 | QUnit = global.QUnit;
12 | minify = global.minify;
13 | }
14 |
15 | test = QUnit.test;
16 | equal = QUnit.equal;
17 | ok = QUnit.ok;
18 |
19 | test('parsing non-trivial markup', function() {
20 | equal(minify('x
'), 'x
');
21 | equal(minify('x
'), 'x
');
22 | equal(minify('x
'), 'x
');
23 | equal(minify('xxx
'), 'xxx
');
24 | equal(minify('xxx
'), 'xxx
');
25 |
26 | input = ''+
27 | 'i\'m 10 levels deep'+
28 | '';
29 |
30 | equal(minify(input), input);
31 |
32 | equal(minify('