diff --git a/README.md b/README.md index e217479..3b6bbfc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -normalize -========= - -Turn ugly -``` +## Normalize +Turn ugly +``` html

hi @@ -14,8 +12,8 @@ Turn ugly

``` -into beautiful -``` +into beautiful ✨ +``` html

hi @@ -26,12 +24,20 @@ into beautiful

``` With just a touch of -``` +``` javascript normalize(element, config); ``` -Where config is optional, having pleasant defaults, but extendable for your amusement. +Where config is optional, having pleasant defaults, but extendable for your amusement! + +## Installation +``` html + + + + +``` -##API +## API Docs coming soon! Peruse the source for now, it is decently tiny, and the built in config explains most of it. Depends upon the delightful jQuery. diff --git a/normalize.js b/normalize.js index 871ec6c..48b905b 100644 --- a/normalize.js +++ b/normalize.js @@ -1,25 +1,25 @@ -(function(){ +(function () { - $.normalize = function(html, customOpt){ + $.normalize = function (html, customOpt) { var html, root$, wrapped, opt; opt = html.opt || (customOpt ? prepareOptTags($.extend(true, baseOpt, customOpt)) - : defaultOpt); - if(!html.opt){ + : defaultOpt); + if (!html.opt) { // first call unstableList.length = 0; // drop state from previous run (in case there has been error) - root$ = $('
'+html+'
'); + root$ = $('
' + html + '
'); } // initial recursion - (html.$ || root$).contents().each(function(){ - if(this.nodeType === this.TEXT_NODE) { - this.textContent = this.textContent.replace(/^[ \n]+|[ \n]+$/g, ' '); + (html.$ || root$).contents().each(function () { + if (this.nodeType === this.TEXT_NODE) { + this.textContent = this.textContent.replace(/^[ \n]+|[ \n]+$/g, ' '); return; } - var a = {$: $(this), opt: opt}; + var a = { $: $(this), opt: opt }; initTag(a); $.normalize(a); }); - if(root$){ + if (root$) { stateMachine(); return root$.html(); } @@ -27,30 +27,30 @@ var baseOpt = { hierarchy: ['div', 'pre', 'ol', 'ul', 'li', - 'h1', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', // block - 'b', 'code', 'i', 'span', 's', 'sub', 'sup', 'u', // inline - 'br'] // empty - ,tags: { - 'a': {attrs:{'href':1}, exclude:{'a':1}}, - 'b': {exclude:{'b':1,'p':1}}, - 'br': {empty: 1}, - 'i': {exclude:{'i':1,'p':1}}, - 'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}}, - 's': {space:1}, - 'u': {exclude:{'u':1,'p':1},space:1}, + 'h1', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', // block + 'b', 'code', 'i', 'span', 's', 'sub', 'sup', 'u', // inline + 'br'] // empty + , tags: { + 'a': { attrs: { 'href': 1 }, exclude: { 'a': 1 } }, + 'b': { exclude: { 'b': 1, 'p': 1 } }, + 'br': { empty: 1 }, + 'i': { exclude: { 'i': 1, 'p': 1 } }, + 'span': { exclude: { 'p': 1, 'ul': 1, 'ol': 1, 'li': 1, 'br': 1 } }, + 's': { space: 1 }, + 'u': { exclude: { 'u': 1, 'p': 1 }, space: 1 }, } - ,convert: { + , convert: { 'em': 'i', 'strong': 'b', 'strike': 's', } - ,attrs: { - 'id':1 - ,'class':1 - ,'style':1 + , attrs: { + 'id': 1 + , 'class': 1 + , 'style': 1 } - ,blockTag: function(a){ + , blockTag: function (a) { return a.opt.tags[a.tag].order < a.opt.tags.a.order; } - ,mutate: [exclude, moveSpaceUp, next, parentOrderWrap] + , mutate: [exclude, moveSpaceUp, next, parentOrderWrap] } var defaultOpt = prepareOptTags($.extend(true, {}, baseOpt)); @@ -58,8 +58,8 @@ var unstableList = []; function addUnstable(a) { // NOT ES5 - if(!a.tag) { throw Error("not tag in ", a) } - if(a.unstable) return; + if (!a.tag) { throw Error("not tag in ", a) } + if (a.unstable) return; unstableList.push(a); a.unstable = true; } @@ -67,7 +67,7 @@ function initTag(a) { // initial handling (container, convert, attributes): a.tag = tag(a.$); - if(empty(a)) { + if (empty(a)) { return; } parseAndRemoveAttrs(a); @@ -81,12 +81,12 @@ } function stateMachine() { - if(unstableList.length===0) + if (unstableList.length === 0) return; var a, i = -1; while (a = unstableList.pop()) { // PERF: running index is probably faster than shift (mutates array) a.unstable = false; - $(a.opt.mutate).each(function(i,fn){ + $(a.opt.mutate).each(function (i, fn) { return fn && fn(a, addUnstable); }); } @@ -94,58 +94,58 @@ function prepareOptTags(opt) { var name, tag, tags = opt.tags; - for(name in tags) { - if(opt.hierarchy.indexOf(name)===-1) - throw Error('tag "'+name+'" is missing hierachy definition'); + for (name in tags) { + if (opt.hierarchy.indexOf(name) === -1) + throw Error('tag "' + name + '" is missing hierachy definition'); } - opt.hierarchy.forEach(function(name){ - if(!tags[name]){ - tags[name] = {attrs: opt.attrs}; + opt.hierarchy.forEach(function (name) { + if (!tags[name]) { + tags[name] = { attrs: opt.attrs }; } - (tag=tags[name]).attrs = $.extend(tag.attrs||{}, opt.attrs); + (tag = tags[name]).attrs = $.extend(tag.attrs || {}, opt.attrs); tag.name = name; // not used, debug help (REMOVE later?) // order tag.order = opt.hierarchy.indexOf(name) - if(tag.order === -1) { - throw Error("Order of '"+name+"' not defined in hierarchy"); - } + if (tag.order === -1) { + throw Error("Order of '" + name + "' not defined in hierarchy"); + } }); return opt; } // GENERAL UTILS - function get(o, args){ // path argments as separate string parameters - if(typeof args === 'string') + function get(o, args) { // path argments as separate string parameters + if (typeof args === 'string') return o[args[0]]; var i = 0, l = args.length, u; - while((o = o[args[i++]]) != null && i < l){}; + while ((o = o[args[i++]]) != null && i < l) { }; return i < l ? u : o; } - function has(obj,prop){ + function has(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } // ELEMENT UTILS - function tag(e){ - return (($(e)[0]||{}).nodeName||'').toLowerCase(); + function tag(e) { + return (($(e)[0] || {}).nodeName || '').toLowerCase(); } - function joint(e, d){ - d = (d? 'next' : 'previous') + 'Sibling'; - return $(($(e)[0]||{})[d]); + function joint(e, d) { + d = (d ? 'next' : 'previous') + 'Sibling'; + return $(($(e)[0] || {})[d]); } // create key val attributes object from elements attributes - function attrsAsObj(e, filterCb){ + function attrsAsObj(e, filterCb) { var attrObj = {}; - (e = $(e)) && e.length && $(e[0].attributes||[]).each(function(value,name){ - name = name.nodeName||name.name; + (e = $(e)) && e.length && $(e[0].attributes || []).each(function (value, name) { + name = name.nodeName || name.name; value = e.attr(name); - value = filterCb? filterCb(value,name,e) : value; - if(value !== undefined && value !== false) + value = filterCb ? filterCb(value, name, e) : value; + if (value !== undefined && value !== false) attrObj[name] = value; }); return attrObj; @@ -161,98 +161,98 @@ function parseAndRemoveAttrs(a) { a.attrs = []; var tag = a.opt.convert[a.tag] || a.tag, - tOpt = a.opt.tags[tag]; - a.attr = tOpt && attrsAsObj(a.$, function(value,name){ - a.$.removeAttr(name); - if(tOpt.attrs[name.toLowerCase()]){ - a.attrs.push(name) - return value; - } + tOpt = a.opt.tags[tag]; + a.attr = tOpt && attrsAsObj(a.$, function (value, name) { + a.$.removeAttr(name); + if (tOpt.attrs[name.toLowerCase()]) { + a.attrs.push(name) + return value; + } }); } - function setAttrs(a){ - var l = function(ind,name){ + function setAttrs(a) { + var l = function (ind, name) { var t = name; - name = a.attrs? name : ind; - var value = a.attrs? a.attr[name.toLowerCase()] : t; + name = a.attrs ? name : ind; + var value = a.attrs ? a.attr[name.toLowerCase()] : t; a.$.attr(name, value); } - a.attrs? $(a.attrs.sort()).each(l) : $.each(a.attr,l); + a.attrs ? $(a.attrs.sort()).each(l) : $.each(a.attr, l); } - function convert(a){ + function convert(a) { var t; - if(t = a.opt.convert[a.tag]){ - a.$.replaceWith(a.$ = $('<'+ (a.tag = t.toLowerCase()) +'>').append(a.$.contents())); + if (t = a.opt.convert[a.tag]) { + a.$.replaceWith(a.$ = $('<' + (a.tag = t.toLowerCase()) + '>').append(a.$.contents())); } } // LOOPING (STATE MACHINE) MUTATORS - function exclude(a, addUnstable){ + function exclude(a, addUnstable) { var t = get(a.opt, ['tags', a.tag]), - pt = get(a.opt, ['tags', tag(a.$.parent())]); - if(!t || (pt && get(pt, ['exclude', a.tag]))){ + pt = get(a.opt, ['tags', tag(a.$.parent())]); + if (!t || (pt && get(pt, ['exclude', a.tag]))) { var c = a.$.contents(); a.$.replaceWith(c); - c.length===1 && c[0].a && addUnstable(c[0].a); + c.length === 1 && c[0].a && addUnstable(c[0].a); return false; } } - function moveSpaceUp(a, addUnstable){ + function moveSpaceUp(a, addUnstable) { var n = a.$[0]; - if(moveSpace(n, true) + moveSpace(n, false)) { + if (moveSpace(n, true) + moveSpace(n, false)) { // either front, back or both spaces moved var c; - if(n.textContent==='') { + if (n.textContent === '') { empty(a); - } else if((c = a.$.contents()[0]) && c.a) { + } else if ((c = a.$.contents()[0]) && c.a) { parentOrderWrap(c.a, addUnstable) } } } function moveSpace(n, bef) { - var childRe = bef? /^ / : / $/, - parentRe = bef? / $/ : /^ /, - c = bef? 'firstChild' : 'lastChild', - s = bef? 'previousSibling' : 'nextSibling'; - sAdd = bef? 'after' : 'before'; - pAdd = bef? 'prepend' : 'append'; - if(!n || !n[c] || n[c].nodeType !== n.TEXT_NODE || !n[c].wholeText.match(childRe)) { + var childRe = bef ? /^ / : / $/, + parentRe = bef ? / $/ : /^ /, + c = bef ? 'firstChild' : 'lastChild', + s = bef ? 'previousSibling' : 'nextSibling'; + sAdd = bef ? 'after' : 'before'; + pAdd = bef ? 'prepend' : 'append'; + if (!n || !n[c] || n[c].nodeType !== n.TEXT_NODE || !n[c].wholeText.match(childRe)) { return 0; } - if((n2 = n[s]) && !n.a.opt.blockTag(n.a)) { - if(n2.nodeType === 3 && !n2.textContent.match(parentRe)) { - n2.textContent = (bef?'':' ') + n2.textContent + (bef?' ':''); - } else if(n2.nodeType === 1) { + if ((n2 = n[s]) && !n.a.opt.blockTag(n.a)) { + if (n2.nodeType === 3 && !n2.textContent.match(parentRe)) { + n2.textContent = (bef ? '' : ' ') + n2.textContent + (bef ? ' ' : ''); + } else if (n2.nodeType === 1) { $(n2)[sAdd](' '); } - } else if((n2 = n.parentNode) && !n.a.opt.blockTag(n.a)) { + } else if ((n2 = n.parentNode) && !n.a.opt.blockTag(n.a)) { $(n2)[pAdd](' '); } else { return 0; } n[c].textContent = n[c].wholeText.replace(childRe, ''); - if(!n[c].wholeText.length) + if (!n[c].wholeText.length) $(n[c]).remove(); return 1; } - function next(a, addUnstable, t){ + function next(a, addUnstable, t) { var t = t || joint(a.$, true), sm; - if(!t.length || a.opt.blockTag(a)) + if (!t.length || a.opt.blockTag(a)) return; - if(a.opt.spaceMerge && t.length===1 && t[0].nodeType === 3 && t[0].wholeText===' '){ - if(!(t2 = joint(t, true)).length || a.opt.blockTag(t2[0].a)) + if (a.opt.spaceMerge && t.length === 1 && t[0].nodeType === 3 && t[0].wholeText === ' ') { + if (!(t2 = joint(t, true)).length || a.opt.blockTag(t2[0].a)) return; t.remove(); t2.prepend(' '); return next(a, addUnstable, t2); } - if(!t[0].a || a.tag !== t[0].a.tag || !sameAttrs(a, t[0].a)) + if (!t[0].a || a.tag !== t[0].a.tag || !sameAttrs(a, t[0].a)) return; t.prepend(a.$.contents()); empty(a); @@ -260,20 +260,20 @@ (t = t.children(":first")).length && addUnstable(t[0].a); } - function empty(a){ + function empty(a) { var t = a.opt.tags[a.tag]; - if((!t || !t.empty) && !a.$.contents().length && !a.$[0].attributes.length){ + if ((!t || !t.empty) && !a.$.contents().length && !a.$[0].attributes.length) { a.$.remove(); return true; // NOTE true/false - different API than in exclude } } - function parentOrderWrap(a, addUnstable){ + function parentOrderWrap(a, addUnstable) { var parent = a.$.parent(), children = parent.contents(), - tags = a.opt.tags, ptag; + tags = a.opt.tags, ptag; - if(children.length===1 && children[0] === a.$[0] - && (ptag=tags[tag(parent)]) && ptag.order > tags[a.tag].order){ + if (children.length === 1 && children[0] === a.$[0] + && (ptag = tags[tag(parent)]) && ptag.order > tags[a.tag].order) { parent.after(a.$); parent.append(a.$.contents()); a.$.append(parent); diff --git a/test/sandbox.html b/test/sandbox.html index 977b361..24fef30 100644 --- a/test/sandbox.html +++ b/test/sandbox.html @@ -1,100 +1,104 @@ + HTML normalize sandbox + - - - - + + + + }); + + init(); + + + \ No newline at end of file diff --git a/test/unitTests.html b/test/unitTests.html index ee98965..bde0d22 100644 --- a/test/unitTests.html +++ b/test/unitTests.html @@ -1,34 +1,37 @@ + - HTML normalize unit tests - + HTML normalize unit tests + + - - - + + + - + - + \ No newline at end of file