refactor HTML module; add functionality to Ox.parseMarkdown; add tests

This commit is contained in:
rolux 2012-05-31 00:47:19 +02:00
parent d6b86b518b
commit 3683bf3d29

View file

@ -26,31 +26,6 @@
].join('|') + ')\\/?>', 'gi')
},
replace = {
mail: [
/\b([0-9A-Z\.\+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6})\b/gi, '$1'
/*
function(match, mail) {
return Ox.encodeEmailAddress(mail);
}
*/
],
namedEntity: [
new RegExp('(' + Ox.values(htmlEntities).join('|') + ')', 'g'),
function(match) {
return Ox.keyOf(htmlEntities, match);
}
],
numericEntity: [
/&#([0-9A-FX]+);/gi,
function(match, code) {
return Ox.char(
/^X/i.test(code)
? parseInt(code.slice(1), 16)
: parseInt(code, 10)
);
}
],
tag: {
a: [
[
/<a [^<>]*?href="((\/|https?:\/\/|mailto:).+?)".*?>/gi,
@ -86,7 +61,15 @@
];
}
},
url: [
salt = Ox.range(2).map(function(){
return Ox.range(16).map(function() {
return Ox.char(65 + Ox.random(26));
}).join('');
});
function addLinks(string, obfuscate) {
return string
.replace(
/\b((https?:\/\/|www\.).+?)([\.,:;!\?\)\]]*?(\s|$))/gi,
function(match, url, prefix, end) {
prefix = prefix.toLowerCase() == 'www.' ? 'http://' : '';
@ -95,13 +78,34 @@
{end: end, prefix: prefix, url: url}
);
}
]
},
salt = Ox.range(2).map(function(){
return Ox.range(16).map(function() {
return Ox.char(65 + Ox.random(26));
}).join('');
});
)
.replace(
/\b([0-9A-Z\.\+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6})\b/gi,
obfuscate ? function(match, mail) {
return Ox.encodeEmailAddress(mail);
} : '<a href="mailto:$1">$1</a>'
);
}
function decodeHTMLEntities(string) {
return string
.replace(
new RegExp('(' + Ox.values(htmlEntities).join('|') + ')', 'g'),
function(match) {
return Ox.keyOf(htmlEntities, match);
}
)
.replace(
/&#([0-9A-FX]+);/gi,
function(match, code) {
return Ox.char(
/^X/i.test(code)
? parseInt(code.slice(1), 16)
: parseInt(code, 10)
);
}
);
}
// Splits a string into text (even indices) and tags (odd indices), ignoring
// tags with starting positions that are included in the ignore array
@ -136,11 +140,6 @@
@*/
Ox.addLinks = function(string, isHTML) {
var isLink = false;
function replaceString(string) {
return string
.replace(replace.mail[0], replace.mail[1])
.replace(replace.url[0], replace.url[1]);
}
return isHTML
? splitHTMLTags(string).map(function(string, i) {
var isTag = i % 2;
@ -151,9 +150,9 @@
isLink = false;
}
}
return isTag || isLink ? string : replaceString(string);
return isTag || isLink ? string : addLinks(string);
}).join('')
: Ox.normalizeHTML(replaceString(string));
: Ox.normalizeHTML(addLinks(string));
};
/*@
@ -224,9 +223,7 @@
Ox.decodeHTMLEntities = function(string, decodeAll) {
return decodeAll
? Ox.decodeHTMLEntities(Ox.normalizeHTML(string))
: String(string)
.replace(replace.namedEntity[0], replace.namedEntity[1])
.replace(replace.numericEntity[0], replace.numericEntity[1]);
: decodeHTMLEntities(string);
};
/*@
@ -360,10 +357,20 @@
[example](http://example.com "example.com") -> <a href="http://example.com" title="example.com">example</a>
> Ox.parseMarkdown('*foo* **bar** `baz` ``back`tick``')
'<em>foo</em> <strong>bar</strong> <code>baz</code> <code>back`tick</code>'
> Ox.parseMarkdown('<http://example.com>')
'<a href="http://example.com">http://example.com</a>'
> Ox.parseMarkdown('[example](http://example.com "example.com")')
'<a href="http://example.com" title="example.com">example</a>'
> Ox.parseMarkdown('[example](http://example.com?foo=bar&bar=baz)')
'<a href="http://example.com?foo=bar&amp;bar=baz">example</a>'
> Ox(Ox.parseMarkdown('<mail@example.com>')).startsWith('<a href="')
true
> Ox(Ox.parseMarkdown('<mail@example.com>')).endsWith('</a>')
true
> Ox(Ox.parseMarkdown('<mail@example.com>')).count(':')
1
> Ox(Ox.parseMarkdown('<mail@example.com>')).decodeHTMLEntities()
'<a href="mailto:mail@example.com">mail@example.com</a>'
*/
Ox.parseMarkdown = function(string) {
// see https://github.com/coreyti/showdown/blob/master/src/showdown.js
@ -379,24 +386,35 @@
)
.replace(
/\n```(.*)\n([^`]+)\n```/g,
function(match, a, b) {
return '<pre><code' + (a ? ' class="' + a + '"' : '') + '>'
+ b + '\n</code></pre>';
function(match, classname, code) {
return '<pre><code'
+ (classname ? ' class="' + classname + '"' : '')
+ '>' + code + '\n</code></pre>';
}
)
.replace(
/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function(match, a, b, c, d) {
return a + '<code>'
+ Ox.encodeHTMLEntities(c.trim()) + '</code>';
function(match, prev, backticks, code, next) {
return prev + '<code>'
+ Ox.encodeHTMLEntities(code.trim()) + '</code>';
}
)
.replace(
/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
function(match, a, b, c, d, e, f, g) {
return '<a href="' + Ox.encodeHTMLEntities(d) + '"' + (
g ? ' title="' + Ox.encodeHTMLEntities(g) + '"' : ''
) + '>' + b + '</a>';
function(match, all, text, id, url, rest, quote, title) {
return '<a href="' + Ox.encodeHTMLEntities(url) + '"' + (
title ? ' title="' + Ox.encodeHTMLEntities(title) + '"' : ''
) + '>' + text + '</a>';
}
)
.replace(
/<((https?|ftp|dict):[^'">\s]+)>/gi,
'<a href=\"$1\">$1</a>'
)
.replace(
/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
function(match, mail) {
return Ox.encodeEmailAddress(mail);
}
);
};
@ -447,7 +465,7 @@
});
}
tags.forEach(function(tag) {
var array = replace.tag[tag] || replace.tag['*'](tag);
var array = replace[tag] || replace['*'](tag);
Ox.forEach(array, function(value) {
html = html.replace(value[0], function() {
matches.push(Ox.formatString(value[1], arguments));