├── test
├── .eslintrc
├── .jshintrc
├── benchmark.js
├── test.html
└── main.js
├── .travis.yml
├── html-uglify.png
├── .eslintrc
├── .editorconfig
├── .gitignore
├── LICENSE
├── package.json
├── README.md
└── lib
└── main.js
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 5
4 | - 4
5 | - 0.12
6 |
--------------------------------------------------------------------------------
/html-uglify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DV/html-uglify/master/html-uglify.png
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint:recommended",
3 | "env": {
4 | "node": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 |
8 | [*.{js,json}]
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "strict": true,
4 | "expr": true,
5 | "predef": [
6 | "before",
7 | "beforeEach",
8 | "after",
9 | "afterEach",
10 | "describe",
11 | "it"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/test/benchmark.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var Benchmark = require('benchmark');
5 | var HTMLUglify = require('../lib/main.js');
6 |
7 | var suite = new Benchmark.Suite();
8 | var htmlUglify = new HTMLUglify();
9 |
10 | console.log('Running benchmark');
11 |
12 | var html = fs.readFileSync('./test/test.html');
13 |
14 | suite
15 | .add('#process', function() {
16 | htmlUglify.process(html);
17 | })
18 | .on('cycle', function(event) {
19 | console.log(String(event.target));
20 | })
21 | .run({ 'async': true });
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ############
2 | # Node #
3 | ############
4 |
5 | # Logs
6 | logs
7 | *.log
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directory
30 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
31 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, RebelMail
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html-uglify",
3 | "version": "1.2.1",
4 | "description": "Uglifies an HTML file and its associated CSS for compression. Great for HTML emails!",
5 | "main": "lib/main.js",
6 | "scripts": {
7 | "test": "mocha test/main.js",
8 | "bench": "node test/benchmark.js"
9 | },
10 | "keywords": [
11 | "html",
12 | "uglify",
13 | "compress",
14 | "css",
15 | "shrink",
16 | "email"
17 | ],
18 | "author": "RebelMail",
19 | "license": "ISC",
20 | "dependencies": {
21 | "cheerio": "^0.19.0",
22 | "hashids": "^1.0.2",
23 | "postcss-safe-parser": "^1.0.4",
24 | "postcss-selector-parser": "^1.3.0"
25 | },
26 | "devDependencies": {
27 | "benchmark": "^2.0.0",
28 | "chai": "^3.4.1",
29 | "mocha": "^2.3.4"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "https://github.com/RebelMail/html-uglify.git"
34 | },
35 | "bugs": {
36 | "url": "https://github.com/RebelMail/html-uglify/issues"
37 | },
38 | "homepage": "https://github.com/RebelMail/html-uglify"
39 | }
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # html-uglify
2 |
3 | [](https://travis-ci.org/Rebelmail/html-uglify)
4 | [](http://badge.fury.io/js/html-uglify)
5 |
6 | 
7 |
8 | Uglify your HTML and CSS for purposes of compression and obfuscation.
9 |
10 | Great for HTML emails.
11 |
12 | ```javascript
13 | var HTMLUglify = require('html-uglify');
14 | var htmlUglify = new HTMLUglify({ salt: 'your-custom-salt', whitelist: ['#noform', '#withform', '.someclass'] });
15 | var uglified = htmlUglify.process(htmlString);
16 | ```
17 |
18 | ## Installation
19 |
20 | ```sh
21 | npm install html-uglify --save
22 | ```
23 |
24 | ## Usage
25 |
26 | You pass an html string to `.process` and it returns the uglified html.
27 |
28 | ```javascript
29 | var HTMLUglify = require('html-uglify');
30 | var htmlUglify = new HTMLUglify({ salt: 'your-custom-salt', whitelist: [] });
31 | var htmlString = "
Hello
";
32 |
33 | var uglified = htmlUglify.process(htmlString);
34 | ```
35 |
36 | ## Contributing
37 |
38 | 1. Fork it
39 | 2. Create your feature branch
40 | 3. Commit your changes (`git commit -am 'Added some feature'`)
41 | 4. Push to the branch (`git push origin my-new-feature`)
42 | 5. Create new Pull Request
43 |
44 | ## Running tests
45 |
46 | ```sh
47 | npm install
48 | npm test
49 | ```
50 |
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/lib/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cheerio = require('cheerio');
4 | var Hashids = require('hashids');
5 | var postcssSafeParser = require('postcss-safe-parser');
6 | var postcssSelectorParser = require('postcss-selector-parser');
7 |
8 | var ALPHABET = 'abcdefghijklmnopqrstuvwxyz';
9 | var VERSION = require('../package.json').version;
10 |
11 | function HTMLUglify(config) {
12 | this.version = VERSION;
13 |
14 | config = config || {};
15 |
16 | var salt = config.salt || 'use the force harry';
17 | this.hashids = new Hashids(salt, 0, ALPHABET);
18 | this.whitelist = config.whitelist || [];
19 | }
20 |
21 | HTMLUglify.prototype.checkForStandardPointer = function(lookups, type, value) {
22 | return lookups[type] && lookups[type][value];
23 | };
24 |
25 | HTMLUglify.prototype.checkForAttributePointer = function(lookups, type, value) {
26 | var typeLookups = lookups[type] || {};
27 | var keys = Object.keys(typeLookups);
28 | var pointer;
29 |
30 | keys.some(function(key) {
31 | if (value.indexOf(key) !== -1) {
32 | pointer = value.replace(key, typeLookups[key]);
33 | return true;
34 | }
35 | return false;
36 | });
37 |
38 | return pointer;
39 | };
40 |
41 | HTMLUglify.prototype.generatePointer = function(lookups) {
42 | var idCount = Object.keys(lookups['id'] || {}).length;
43 | var classCount = Object.keys(lookups['class'] || {}).length;
44 | var counter = idCount + classCount;
45 |
46 | return this.hashids.encode(counter);
47 | };
48 |
49 | HTMLUglify.prototype.pointer = function(type, value, lookups) {
50 | return this.checkForStandardPointer(lookups, type, value) ||
51 | this.checkForAttributePointer(lookups, type, value) ||
52 | this.generatePointer(lookups);
53 | };
54 |
55 | HTMLUglify.prototype.insertLookup = function(type, value, pointer, lookups) {
56 | if (!lookups[type]) {
57 | lookups[type] = {};
58 | }
59 | lookups[type][value] = pointer;
60 | };
61 |
62 | HTMLUglify.prototype.createLookup = function(type, value, lookups) {
63 | var pointer;
64 | if (value && !this.isWhitelisted(type, value)) {
65 | pointer = this.pointer(type, value, lookups);
66 | this.insertLookup(type, value, pointer, lookups);
67 | }
68 | return pointer;
69 | };
70 |
71 | HTMLUglify.prototype.isWhitelisted = function(type, value) {
72 | switch(type) {
73 | case 'class':
74 | value = ['.', value].join('');
75 | break;
76 | case 'id':
77 | value = ['#', value].join('');
78 | break;
79 | default:
80 | break;
81 | }
82 |
83 | return this.whitelist.indexOf(value) >= 0;
84 | };
85 |
86 | HTMLUglify.prototype.pointerizeClass = function($element, lookups) {
87 | var self = this;
88 | var value = $element.attr('class');
89 |
90 | if (value) {
91 | var splitClasses = value.split(/\s+/);
92 |
93 | splitClasses.forEach(function(value) {
94 | var pointer = self.createLookup('class', value, lookups);
95 | if (pointer) {
96 | $element.removeClass(value);
97 | $element.addClass(pointer);
98 | }
99 | });
100 | }
101 | };
102 |
103 | HTMLUglify.prototype.pointerizeIdAndFor = function(type, $element, lookups) {
104 | var value = $element.attr(type);
105 |
106 | var pointer = this.createLookup('id', value, lookups);
107 | if (pointer) {
108 | $element.attr(type, pointer);
109 | }
110 | };
111 |
112 | HTMLUglify.prototype.processRules = function(rules, lookups) {
113 | var self = this;
114 |
115 | rules.forEach(function(rule) {
116 | // go deeper inside media rule to find css rules
117 | if (rule.type === 'atrule' && (rule.name === 'media' || rule.name === 'supports')) {
118 | self.processRules(rule.nodes, lookups);
119 | } else if (rule.type === 'rule') {
120 | postcssSelectorParser(function(selectors) {
121 | selectors.eachInside(function(selector) {
122 | var pointer;
123 |
124 | if ((selector.type === 'id')
125 | || (selector.type === 'attribute' && selector.attribute === 'id')
126 | || (selector.type === 'attribute' && selector.attribute === 'for')) {
127 | pointer = self.createLookup('id', selector.value, lookups);
128 | } else if ((selector.type === 'class')
129 | || (selector.type === 'attribute' && selector.attribute === 'class')) {
130 | pointer = self.createLookup('class', selector.value, lookups);
131 | }
132 |
133 | if (pointer) {
134 | selector.value = pointer;
135 | }
136 | });
137 |
138 | rule.selector = String(selectors);
139 | }).process(rule.selector);
140 | }
141 | });
142 | };
143 |
144 | HTMLUglify.prototype.rewriteElements = function($, lookups) {
145 | var self = this;
146 |
147 | lookups = lookups || {};
148 |
149 | $('*[id]').each(function() {
150 | self.pointerizeIdAndFor('id', $(this), lookups);
151 | });
152 |
153 | $('*[for]').each(function() {
154 | self.pointerizeIdAndFor('for', $(this), lookups);
155 | });
156 |
157 | $('*[class]').each(function() {
158 | self.pointerizeClass($(this), lookups);
159 | });
160 |
161 | return $;
162 | };
163 |
164 | HTMLUglify.prototype.rewriteStyles = function($, lookups) {
165 | var self = this;
166 |
167 | lookups = lookups || {};
168 |
169 | $('style').each(function() {
170 | var $style = $(this);
171 | var ast = postcssSafeParser($style.text());
172 | self.processRules(ast.nodes, lookups);
173 | $style.text(ast.toString());
174 | });
175 |
176 | return $;
177 | };
178 |
179 | HTMLUglify.prototype.process = function(html) {
180 | var lookups = {};
181 |
182 | var $ = cheerio.load(html);
183 | $ = this.rewriteStyles($, lookups);
184 | $ = this.rewriteElements($, lookups);
185 |
186 | return $.html();
187 | };
188 |
189 | module.exports = HTMLUglify;
190 |
--------------------------------------------------------------------------------
/test/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var assert = require('chai').assert;
4 | var cheerio = require('cheerio');
5 | var HTMLUglify = require('../lib/main.js');
6 |
7 | var htmlUglify = new HTMLUglify();
8 |
9 | describe('HTMLUglify', function() {
10 | describe('#isWhitelisted', function() {
11 | var whitelist;
12 | var htmlUglify;
13 |
14 | beforeEach(function() {
15 | whitelist = ['#theid', '.theclass', '#★', '.★'];
16 | htmlUglify = new HTMLUglify({whitelist: whitelist});
17 | });
18 | it('returns true if id is in whitelist', function() {
19 | var whitelisted = htmlUglify.isWhitelisted('id', 'theid');
20 | assert.isTrue(whitelisted);
21 | });
22 | it('returns false if id is in the whitelist but only checking for classes', function() {
23 | var whitelisted = htmlUglify.isWhitelisted('class', 'theid');
24 | assert.isFalse(whitelisted);
25 | });
26 | it('returns true if class is in whitelist', function() {
27 | var whitelisted = htmlUglify.isWhitelisted('class', 'theclass');
28 | assert.isTrue(whitelisted);
29 | });
30 | it('returns true if id is in whitelist for a unicode character', function() {
31 | var whitelisted = htmlUglify.isWhitelisted('id', '★');
32 | assert.isTrue(whitelisted);
33 | });
34 | it('returns true if class is in whitelist for a unicode character', function() {
35 | var whitelisted = htmlUglify.isWhitelisted('class', '★');
36 | assert.isTrue(whitelisted);
37 | });
38 | });
39 | describe('#checkForStandardPointer', function() {
40 | it('returns undefined when name not found', function() {
41 | var lookups = {
42 | 'class': { 'something': 'zzz' }
43 | };
44 | var value = 'other';
45 | var pointer = htmlUglify.checkForStandardPointer(lookups, 'class', value);
46 |
47 | assert.isUndefined(pointer);
48 | });
49 | it('returns pointer when found', function() {
50 | var lookups = {
51 | 'class': { 'something': 'zzz' }
52 | };
53 | var value = 'something';
54 | var pointer = htmlUglify.checkForStandardPointer(lookups, 'class', value);
55 |
56 | assert.equal(pointer, 'zzz');
57 | });
58 | });
59 | describe('#checkForAttributePointer', function() {
60 | it('returns undefined when not found', function() {
61 | var lookups = {
62 | 'class': { 'something': 'zzz' }
63 | };
64 | var value = 'other';
65 | var pointer = htmlUglify.checkForAttributePointer(lookups, 'class', value);
66 |
67 | assert.isUndefined(pointer);
68 | });
69 | it('returns the pointer when value contains same string as an existing lookup', function() {
70 | var lookups = {
71 | 'class': { 'something': 'zzz' }
72 | };
73 | var value = 'somethingElse';
74 | var pointer = htmlUglify.checkForAttributePointer(lookups, 'class', value);
75 |
76 | assert.equal(pointer, 'zzzElse');
77 | });
78 | });
79 | describe('#generatePointer', function() {
80 | it('returns xz for counter 0 lookups', function() {
81 | var lookups = {};
82 | var pointer = htmlUglify.generatePointer(lookups);
83 | assert.equal(pointer, 'xz');
84 | });
85 | it('returns wk for 1 lookups', function() {
86 | var lookups = { 'id': { 'a': 'xz' } };
87 | var pointer = htmlUglify.generatePointer(lookups);
88 | assert.equal(pointer, 'wk');
89 | });
90 | it('returns en for 2 lookups', function() {
91 | var lookups = { 'id': { 'a': 'xz' }, 'class': { 'b': 'wk' } };
92 | var pointer = htmlUglify.generatePointer(lookups);
93 | assert.equal(pointer, 'en');
94 | });
95 | });
96 | describe('#pointer', function() {
97 | it('generates a new pointer', function() {
98 | var lookups = {};
99 | var pointer = htmlUglify.pointer('class', 'newClass', {});
100 | assert.equal(pointer, 'xz', lookups);
101 | });
102 | it('generates a new pointer given a different one exists', function() {
103 | var lookups = {
104 | 'class': { 'otherClass': 'wk' }
105 | };
106 | var pointer = htmlUglify.pointer('class', 'newClass', lookups);
107 | assert.equal(pointer, 'wk', lookups);
108 | });
109 | it('generates a new pointer given a different one exists in a different attribute', function() {
110 | var lookups = {
111 | 'id': { 'someId': 'wk' }
112 | };
113 | var pointer = htmlUglify.pointer('class', 'newClass', lookups);
114 | assert.equal(pointer, 'wk', lookups);
115 | });
116 | it('finds an existing class pointer', function() {
117 | var lookups = {
118 | 'class': { 'someClass': 'xz' }
119 | };
120 | var pointer = htmlUglify.pointer('class', 'someClass', lookups);
121 | assert.equal(pointer, 'xz', lookups);
122 | });
123 | it('finds an existing id pointer', function() {
124 | var lookups = {
125 | 'id': { 'someId': 'en' }
126 | };
127 | var pointer = htmlUglify.pointer('id', 'someId', lookups);
128 | assert.equal(pointer, 'en');
129 | });
130 | it('finds a more complex existing pointer', function() {
131 | var lookups = {
132 | class: {
133 | test: 'xz',
134 | testOther: 'wk',
135 | otratest: 'en'
136 | }
137 | };
138 | var pointer = htmlUglify.pointer('class', 'test', lookups);
139 |
140 | assert.equal(pointer, 'xz');
141 | });
142 | });
143 |
144 | describe('#rewriteStyles', function() {
145 | it('rewrites an id given lookups', function() {
146 | var lookups = { 'id=abe': 'xz' };
147 | var html = '';
148 | var $ = cheerio.load(html);
149 | var results = htmlUglify.rewriteStyles($, lookups).html();
150 | assert.equal(results, '');
151 | });
152 | it('rewrites an id', function() {
153 | var lookups = { };
154 | var html = '';
155 | var $ = cheerio.load(html);
156 | var results = htmlUglify.rewriteStyles($, lookups).html();
157 | assert.equal(results, '');
158 | });
159 | it('rewrites an id with the same name as the element', function() {
160 | var lookups = {'id': {'label': 'ab' }};
161 | var html = '';
162 | var $ = cheerio.load(html);
163 | var results = htmlUglify.rewriteStyles($, lookups).html();
164 | assert.equal(results, '');
165 | });
166 | it('rewrites a for= given lookups', function() {
167 | var lookups = { 'id': {'email': 'ab'} };
168 | var html = '';
169 | var $ = cheerio.load(html);
170 | var results = htmlUglify.rewriteStyles($, lookups).html();
171 | assert.equal(results, "");
172 | });
173 | it('does rewrites a for=', function() {
174 | var lookups = {};
175 | var html = '';
176 | var $ = cheerio.load(html);
177 | var results = htmlUglify.rewriteStyles($, lookups).html();
178 | assert.equal(results, "");
179 | });
180 | it('rewrites a for= with quotes given lookups', function() {
181 | var lookups = { 'id': {'email': 'ab'} };
182 | var html = '';
183 | var $ = cheerio.load(html);
184 | var results = htmlUglify.rewriteStyles($, lookups).html();
185 | assert.equal(results, '');
186 | });
187 | it('rewrites a for= with the same name as the element', function() {
188 | var lookups = { 'id': { 'label': 'ab' }};
189 | var html = '';
190 | var $ = cheerio.load(html);
191 | var results = htmlUglify.rewriteStyles($, lookups).html();
192 | assert.equal(results, '');
193 | });
194 | it('rewrites an id= given lookups', function() {
195 | var lookups = { 'id': {'email': 'ab'} };
196 | var html = '';
197 | var $ = cheerio.load(html);
198 | var results = htmlUglify.rewriteStyles($, lookups).html();
199 | assert.equal(results, '');
200 | });
201 | it('rewrites an id= with quotes given lookups', function() {
202 | var lookups = { 'id': { 'email': 'ab' } };
203 | var html = '';
204 | var $ = cheerio.load(html);
205 | var results = htmlUglify.rewriteStyles($, lookups).html();
206 | assert.equal(results, '');
207 | });
208 | it('rewrites an id= with quotes and with the same name as the element', function() {
209 | var lookups = { 'id': {'label': 'ab'} };
210 | var html = '';
211 | var $ = cheerio.load(html);
212 | var results = htmlUglify.rewriteStyles($, lookups).html();
213 | assert.equal(results, '');
214 | });
215 | it('rewrites a class given lookups', function() {
216 | var lookups = { 'class': { 'email': 'ab' }};
217 | var html = '';
218 | var $ = cheerio.load(html);
219 | var results = htmlUglify.rewriteStyles($, lookups).html();
220 | assert.equal(results, '');
221 | });
222 | it('rewrites a class with the same name as the element', function() {
223 | var lookups = { 'class': { 'label': 'ab' }};
224 | var html = '';
225 | var $ = cheerio.load(html);
226 | var results = htmlUglify.rewriteStyles($, lookups).html();
227 | assert.equal(results, '');
228 | });
229 | it('rewrites a class= given lookups', function() {
230 | var lookups = { 'class': { 'email': 'ab' }};
231 | var html = '';
232 | var $ = cheerio.load(html);
233 | var results = htmlUglify.rewriteStyles($, lookups).html();
234 | assert.equal(results, "");
235 | });
236 | it('rewrites multi-selector rule', function() {
237 | var lookups = { 'class': { 'email': 'ab' }};
238 | var html = '';
239 | var $ = cheerio.load(html);
240 | var results = htmlUglify.rewriteStyles($, lookups).html();
241 | assert.equal(results, '');
242 | });
243 | it('rewrites css media queries', function() {
244 | var lookups = { 'id': { 'abe': 'wz' }};
245 |
246 | var html = '';
247 | var $ = cheerio.load(html);
248 | var results = htmlUglify.rewriteStyles($, lookups).html();
249 | assert.equal(results, '');
250 | });
251 | it('rewrites nested css media queries', function() {
252 | var lookups = { 'id': { 'abe': 'wz' }};
253 |
254 | var html = '';
255 | var $ = cheerio.load(html);
256 | var results = htmlUglify.rewriteStyles($, lookups).html();
257 | assert.equal(results, '');
258 | });
259 | it('handles malformed syntax', function() {
260 | var html = '';
261 | var $ = cheerio.load(html);
262 | var results = htmlUglify.rewriteStyles($).html();
263 | assert.equal(results, '');
264 | });
265 | });
266 |
267 | describe('#pointerizeClass', function() {
268 | var $element;
269 |
270 | beforeEach(function() {
271 | var html = '';
272 | var $ = cheerio.load(html);
273 |
274 | $element = $('p').first();
275 | });
276 |
277 | it('works with empty lookups', function() {
278 | var lookups = {};
279 | htmlUglify.pointerizeClass($element, lookups);
280 | assert.deepEqual(lookups, { class: { one: 'xz', two: 'wk' } });
281 | });
282 | it('works with single lookup', function() {
283 | var lookups = { class: { one: 'ab' } };
284 | htmlUglify.pointerizeClass($element, lookups);
285 | assert.deepEqual(lookups, { class: { one: 'ab', two: 'wk' } });
286 | });
287 | it('works with whitelist', function() {
288 | var lookups = {};
289 | htmlUglify.whitelist = [ '.two' ];
290 | htmlUglify.pointerizeClass($element, lookups);
291 | assert.deepEqual(lookups, { class: { one: 'xz' } });
292 | });
293 | });
294 | describe('#pointerizeIdAndFor', function() {
295 | var $element;
296 |
297 | beforeEach(function() {
298 | var html = '';
299 | var $ = cheerio.load(html);
300 |
301 | $element = $('p').first();
302 | });
303 |
304 | it('works with empty lookups', function() {
305 | var lookups = {};
306 | htmlUglify.pointerizeIdAndFor('id', $element, lookups);
307 | assert.deepEqual(lookups, { id: { one: 'xz' } });
308 | });
309 | it('works with existing lookup', function() {
310 | var lookups = { class: { one: 'ab' } };
311 | htmlUglify.pointerizeClass($element, lookups);
312 | assert.deepEqual(lookups, { class: { one: 'ab' } });
313 | });
314 | it('works with whitelist', function() {
315 | var lookups = {};
316 | htmlUglify.whitelist = [ '#one' ];
317 | htmlUglify.pointerizeClass($element, lookups);
318 | assert.deepEqual(lookups, {});
319 | });
320 | });
321 | describe('#rewriteElements', function() {
322 | it('rewrites an id', function() {
323 | var html = 'Header
';
324 | var $ = cheerio.load(html);
325 | var results = htmlUglify.rewriteElements($).html();
326 | assert.equal(results, 'Header
');
327 | });
328 | it('rewrites a class', function() {
329 | var html = 'Header
';
330 | var $ = cheerio.load(html);
331 | var results = htmlUglify.rewriteElements($).html();
332 | assert.equal(results, 'Header
');
333 | });
334 | it('rewrites a multiple classes', function() {
335 | var html = 'Header
';
336 | var $ = cheerio.load(html);
337 | var results = htmlUglify.rewriteElements($).html();
338 | assert.equal(results, 'Header
');
339 | });
340 | it('rewrites a multiple classes with more than one space between them', function() {
341 | var html = 'Header
';
342 | var $ = cheerio.load(html);
343 | var results = htmlUglify.rewriteElements($).html();
344 | assert.equal(results, 'Header
');
345 | });
346 | it('rewrites a for', function() {
347 | var html = '