bar
baz', 'foobar', 'c', true) 'foobar
baz' > Ox.highlight('foobaz
back`tick
'
> Ox.parseMarkdown('foo\n\nbar\n\nbaz')
'foobar\n\nbaz\n
'
> Ox.parseMarkdown('<http://example.com>
'
> Ox.parseMarkdown('[example](http://example.com "example.com")')
'example'
> Ox.parseMarkdown('[example](http://example.com?foo=bar&bar=baz)')
'example'
> Ox(Ox.parseMarkdown(''
+ code.trim().replace(/
'
);
return salt.join(array.length - 1);
}
)
.replace(
/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function(match, prev, backticks, code, next) {
array.push(
prev + ''
+ code.trim().replace(/'
);
return salt.join(array.length - 1);
}
)
.replace(
/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
'$2'
)
.replace(
/(\*|_)(?=\S)([^\r]*?\S)\1/g,
'$2'
)
.replace(
/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
function(match, all, text, id, url, rest, quote, title) {
return '' + text + '';
}
)
.replace(
/<((https?|ftp|dict):[^'">\s]+)>/gi,
'$1'
)
.replace(
/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
function(match, mail) {
return Ox.encodeEmailAddress(mail);
}
)
.replace(/\n\n/g, '
')
.replace(
new RegExp(salt.join('(\\d+)'), 'g'),
function(match, index) {
return array[parseInt(index)];
}
);
};
/*@
Ox.sanitizeHTML Takes untrusted HTML and returns something trustworthy
> Ox.sanitizeHTML('http://foo.com, ...')
'http://foo.com, ...'
> Ox.sanitizeHTML('http://foo.com/foo?bar&baz, ...')
'http://foo.com/foo?bar&baz, ...'
> Ox.sanitizeHTML('(see: www.foo.com)')
'(see: www.foo.com)'
> Ox.sanitizeHTML('foo@bar.com')
'foo@bar.com'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('http://www.foo.com/')
'http://www.foo.com/'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('foo')
'<a href="javascript:alert()">foo</a>'
> Ox.sanitizeHTML('foo')
'<a href="foo">foo</a>'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('[http://foo.com foo]')
'foo'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('')
'<script>alert()</script>'
> Ox.sanitizeHTML('\'foo\' < \'bar\' && "foo" > "bar"')
'\'foo\' < \'bar\' && "foo" > "bar"'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('foo')
'foo'
> Ox.sanitizeHTML('&&')
'&&'
> Ox.sanitizeHTML('')
'<http://foo.com>'
> Ox.sanitizeHTML(' ')
'"<foo value="http://foo.com"></foo>"'
@*/
Ox.sanitizeHTML = function(html, tags, globalAttributes) {
tags = tags || defaultTags;
globalAttributes = globalAttributes || [];
var escaped = {},
level = 0,
matches = [],
selfClosingTags = ['img', 'br'],
validAttributes = {}, requiredAttributes = {}, validate = {},
validTags = tags.map(function(tag) {
validAttributes[tag.name] = globalAttributes
.concat(tag.required || [])
.concat(tag.optional || []);
requiredAttributes[tag.name] = tag.required || [];
validate[tag.name] = tag.validate || {};
return tag.name;
});
// html = Ox.clean(html); fixme: can this be a parameter?
if (Ox.contains(validTags, '[]')) {
html = html.replace(
/\[((\/|https?:\/\/|mailto:).+?) (.+?)\]/gi,
'$3'
);
validTags = validTags.filter(function(tag) {
return tag != '[]';
});
}
html = splitHTMLTags(html).map(function(string, i) {
var attrs = {},
attrMatch,
attrRegexp = /([^=\ ]+)="([^"]+)"/g,
attrString,
isClosing,
isTag = i % 2,
isValid = true,
tag,
tagMatch,
tagRegexp = /<(\/)?([^\ \/]+)(.*?)(\/)?>/g;
if (isTag) {
tagMatch = tagRegexp.exec(string);
if (tagMatch) {
isClosing = !Ox.isUndefined(tagMatch[1]);
tag = tagMatch[2];
attrString = tagMatch[3].trim();
while (attrMatch = attrRegexp.exec(attrString)) {
if (
validAttributes[tag]
&& Ox.contains(validAttributes[tag], attrMatch[1])
) {
attrs[attrMatch[1]] = attrMatch[2];
}
}
if (!isClosing && !Ox.contains(selfClosingTags, tag)) {
level++;
}
if (
!Ox.contains(validTags, tag)
|| (attrString.length && Ox.isEmpty(attrs))
) {
isValid = false;
} else if (!isClosing && requiredAttributes[tag]) {
requiredAttributes[tag].forEach(function(attr) {
if (Ox.isUndefined(attrs[attr])) {
isValid = false;
}
});
}
if (isValid && !Ox.isEmpty(attrs)) {
Ox.forEach(attrs, function(value, key) {
if (
!Ox.isUndefined(validate[tag][key])
&& !validate[tag][key].exec(value)
) {
isValid = false;
return false; // break
}
});
}
if (isValid && isClosing) {
isValid = !escaped[level];
} else {
escaped[level] = !isValid;
}
if (isClosing) {
level--;
}
if (isValid) {
return '<'
+ (isClosing ? '/' : '')
+ tag
+ (!isClosing && !Ox.isEmpty(attrs)
? ' ' + Ox.values(Ox.map(attrs, function(value, key) {
return key + '="' + value + '"';
})).join(' ')
: '')
+ '>';
}
}
}
return Ox.encodeHTMLEntities(Ox.decodeHTMLEntities(string));
}).join('');
//FIXME: dont add links to urls inside of escaped tags
html = Ox.addLinks(html, true);
html = html.replace(/\n\n/g, '
');
// Close extra opening and remove extra closing tags.
// Note: this converts ''' to "'" and '"' to '"'
return Ox.normalizeHTML(html);
};
/*@
Ox.stripTags Strips HTML tags from a string
> Ox.stripTags('foo')
'foo'
@*/
Ox.stripTags = function(string) {
return string.replace(/<.*?>/g, '');
};
}());