gun/lib/normalize.js

138 lines
3.7 KiB
JavaScript

;(function(){
function normalize(opt){
var el = $(this);
opt = opt || $.extend(true, normalize.opt, opt||{});
el.children().each(function(){
var a = {$: $(this), opt: opt};
a.tag = normalize.tag(a.$);
$(a.opt.mutate).each(function(i,fn){
fn && fn(a);
});
})
return el;
};
var n = normalize, u;
n.get = function(o, p){
p = p.split('.');
var i = 0, l = p.length, u;
while((o = o[p[i++]]) != null && i < l){};
return i < l ? u : o;
}
n.has = function(o,p){
return Object.prototype.hasOwnProperty.call(o, p);
}
n.tag = function(e){
return (($(e)[0]||{}).nodeName||'').toLowerCase();
}
n.attrs = function(e, cb){
var attr = {};
(e = $(e)) && e.length && $(e[0].attributes||[]).each(function(v,n){
n = n.nodeName||n.name;
v = e.attr(n);
v = cb? cb(v,n,e) : v;
if(v !== u && v !== false){ attr[n] = v }
});
return attr;
}
n.joint = function(e, d){
d = (d? 'next' : 'previous') + 'Sibling'
return $(($(e)[0]||{})[d]);
}
var h = {
attr: function(a$, av, al){
var l = function(i,v){
var t = v;
i = al? v : i;
v = al? av[v.toLowerCase()] : t;
a$.attr(i, v);
}
al? $(al.sort()).each(l) : $.each(av,l);
}
}
n.opt = { // some reasonable defaults, limited to content alone.
tags: {
'a': {attrs:{'src':1}, exclude:{'a':1}},
'b': {exclude:{'b':1}},
//'blockquote':1,
'br': {empty: 1},
'div': 1,
//'code':1,
'i': {exclude:{'i':1}},
'img': {attrs:{'src':1}, empty: 1},
'li':1, 'ol':1,
'p': {exclude:{'p':1,'div':1}},
//'pre':1,
's': {exclude:{'s':1}},
'sub':1, 'sup':1,
'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}},
'u': {exclude:{'u':1,'p':1}},
'ul':1
}
// a, audio, b, br, div, i, img, li, ol, p, s, span, sub, sup, u, ul, video
// button, canvas, embed, form, iframe, input, style, svg, table,
// Text: bold, italics, underline, align, bullet, list,
,convert: {
'em': 'i', 'strong': 'b'
}
,attrs: {
'id':1
,'class':1
,'style':1
}
,mutate: [
function(a){ // attr
a.attrs = [];
a.attr = $.extend(a.opt.attrs, n.get(a.opt,'tags.'+ a.tag +'attrs'));
a.attr = n.attrs(a.$, function(v,i){
a.$.removeAttr(i);
if(a.attr[i.toLowerCase()]){
a.attrs.push(i)
return v;
}
});
// if this tag is gonna get converted, wait to add attr back till after the convert
if(a.attrs && !n.get(a.opt, 'convert.' + a.tag)){
h.attr(a.$, a.attr, a.attrs);
}
}
,function(a, tmp){ // convert
if(!(tmp = n.get(a.opt,'convert.' + a.tag))){ return }
a.attr = a.attr || n.attrs(a.$);
a.$.replaceWith(a.$ = $('<'+ (a.tag = tmp.toLowerCase()) +'>').append(a.$.contents()));
h.attr(a.$, a.attr, a.attrs);
}
,function(a, tmp){ // lookahead
if((tmp = n.joint(a.$,1)) && (tmp = tmp.contents()).length === 1 && a.tag === n.tag(tmp = tmp.first())){
a.$.append(tmp.parent()); // no need to unwrap the child, since the recursion will do it for us
}
}
,function(a){ // recurse
// this needs to precede the exclusion and empty.
normalize(a);
}
,function(a, tmp){ // exclude
if(!n.get(a.opt,'tags.' + a.tag)
|| ((tmp = n.get(a.opt,'tags.'+ a.tag +'.exclude'))
&& a.$.parents($.map(tmp,function(i,v){return v})+' ').length)
){
a.$.replaceWith(a.$.contents());
}
}
,function(a, tmp){ // prior
if((tmp = n.joint(a.$)).length && a.tag === n.tag(tmp)){
tmp.append(a.$.contents());
}
}
,function(a){ // empty
// should always go last, since the element will be removed!
if(a.opt.empty || !n.has(a.opt,'empty')){
if(!n.get(a.opt,'tags.'+ a.tag +'.empty')
&& !a.$.contents().length){
a.$.remove();
}
}
}
]
}
$.fn.normalize = normalize;
}());