+363
-247
@@ -101,15 +101,34 @@
|
||||
};
|
||||
this.hooks = {};
|
||||
this.html = false;
|
||||
this.blockParsers = [['code', 10], ['shtml', 20], ['ahtml', 30], ['list', 40], ['math', 50], ['pre', 60], ['html', 70], ['footnote', 80], ['definition', 90], ['quote', 100], ['table', 110], ['sh', 120], ['mh', 130], ['hr', 140], ['default', 9999]];
|
||||
this.parsers = {};
|
||||
}
|
||||
|
||||
Parser.prototype.makeHtml = function(text) {
|
||||
var html;
|
||||
var html, j, len, name, parser, ref;
|
||||
this.footnotes = [];
|
||||
this.definitions = {};
|
||||
this.holders = {};
|
||||
this.uniqid = (Math.ceil(Math.random() * 10000000)) + (Math.ceil(Math.random() * 10000000));
|
||||
this.id = 0;
|
||||
this.blockParsers.sort(function(a, b) {
|
||||
if (a[1] < b[1]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
ref = this.blockParsers;
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
parser = ref[j];
|
||||
name = parser[0];
|
||||
if (parser[2] !== void 0) {
|
||||
this.parsers[name] = parser[2];
|
||||
} else {
|
||||
this.parsers[name] = this['parseBlock' + (ucfirst(name))].bind(this);
|
||||
}
|
||||
}
|
||||
text = this.initText(text);
|
||||
html = this.parse(text);
|
||||
html = this.makeFootnotes(html);
|
||||
@@ -403,7 +422,7 @@
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlock = function(text, lines) {
|
||||
var align, aligns, autoHtml, block, emptyCount, head, htmlTagAllRegExp, htmlTagRegExp, indent, isAfterList, j, key, l, lastMatch, len, len1, len2, line, m, matches, n, num, ref, row, rows, space, special, tag;
|
||||
var block, j, key, l, len, len1, line, name, parser, pass, ref, ref1, state;
|
||||
ref = text.split("\n");
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
line = ref[j];
|
||||
@@ -412,266 +431,363 @@
|
||||
this.blocks = [];
|
||||
this.current = 'normal';
|
||||
this.pos = -1;
|
||||
special = (array_keys(this.specialWhiteList)).join('|');
|
||||
emptyCount = 0;
|
||||
autoHtml = false;
|
||||
state = {
|
||||
special: (array_keys(this.specialWhiteList)).join('|'),
|
||||
empty: 0,
|
||||
html: false
|
||||
};
|
||||
for (key = l = 0, len1 = lines.length; l < len1; key = ++l) {
|
||||
line = lines[key];
|
||||
block = this.getBlock();
|
||||
if (block != null) {
|
||||
block = block.slice(0);
|
||||
}
|
||||
if (!!(matches = line.match(/^(\s*)((?:[0-9]+\.)|(?:[a-z]\.?)|\-|\+|\*)\s+/i))) {
|
||||
space = matches[1].length;
|
||||
emptyCount = 0;
|
||||
if (this.isBlock('list')) {
|
||||
this.setBlock(key, space);
|
||||
} else {
|
||||
this.startBlock('list', key, space);
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('list')) {
|
||||
if ((emptyCount === 0) && !!(matches = line.match(/^(\s+)/)) && matches[1].length > block[3]) {
|
||||
this.setBlock(key);
|
||||
if (this.current !== 'normal') {
|
||||
pass = this.parsers[this.current](block, key, line, state, lines);
|
||||
if (!pass) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!!(matches = line.match(/^(\s*)(~{3,}|`{3,})([^`~]*)$/i))) {
|
||||
if (this.isBlock('code')) {
|
||||
isAfterList = block[3][2];
|
||||
if (isAfterList) {
|
||||
this.combineBlock().setBlock(key);
|
||||
} else {
|
||||
(this.setBlock(key)).endBlock();
|
||||
ref1 = this.parsers;
|
||||
for (name in ref1) {
|
||||
parser = ref1[name];
|
||||
if (name !== this.current) {
|
||||
pass = parser(block, key, line, state, lines);
|
||||
if (!pass) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
isAfterList = false;
|
||||
if (this.isBlock('list')) {
|
||||
space = block[3];
|
||||
isAfterList = (space > 0 && matches[1].length >= space) || matches[1].length > space;
|
||||
}
|
||||
this.startBlock('code', key, [matches[1], matches[3], isAfterList]);
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('code')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
}
|
||||
if (this.html) {
|
||||
if (!autoHtml && !!(matches = line.match(/^(\s*)!!!(\s*)$/))) {
|
||||
if (this.isBlock('shtml')) {
|
||||
this.setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('shtml', key);
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('shtml')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
}
|
||||
htmlTagRegExp = new RegExp("^\\s*<(" + this.blockHtmlTags + ")(\\s+[^>]*)?>", 'i');
|
||||
if (matches = line.match(htmlTagRegExp)) {
|
||||
if (this.isBlock('ahtml')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
} else if (matches[2] === void 0 || matches[2] !== '/') {
|
||||
this.startBlock('ahtml', key);
|
||||
htmlTagAllRegExp = new RegExp("\\s*<(" + this.blockHtmlTags + ")(\\s+[^>]*)?>", 'ig');
|
||||
while (true) {
|
||||
m = htmlTagAllRegExp.exec(line);
|
||||
if (!m) {
|
||||
break;
|
||||
}
|
||||
lastMatch = m[1];
|
||||
}
|
||||
if (0 <= line.indexOf("</" + lastMatch + ">")) {
|
||||
this.endBlock();
|
||||
} else {
|
||||
autoHtml = lastMatch;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (!!autoHtml && 0 <= line.indexOf("</" + autoHtml + ">")) {
|
||||
this.setBlock(key).endBlock();
|
||||
autoHtml = false;
|
||||
continue;
|
||||
} else if (this.isBlock('ahtml')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
} else if (!!(matches = line.match(/^\s*<!\-\-(.*?)\-\->\s*$/))) {
|
||||
this.startBlock('ahtml', key).endBlock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!!(matches = line.match(/^(\s*)\$\$(\s*)$/))) {
|
||||
if (this.isBlock('math')) {
|
||||
this.setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('math', key);
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('math')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
}
|
||||
if (!!(line.match(/^ {4}/))) {
|
||||
emptyCount = 0;
|
||||
if ((this.isBlock('pre')) || this.isBlock('list')) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('pre', key);
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('pre')) {
|
||||
if (line.match(/^\s*$/)) {
|
||||
if (emptyCount > 0) {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
emptyCount += 1;
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!!(matches = line.match(new RegExp("^\\s*<(" + special + ")(\\s+[^>]*)?>", 'i')))) {
|
||||
tag = matches[1].toLowerCase();
|
||||
if (!(this.isBlock('html', tag)) && !(this.isBlock('pre'))) {
|
||||
this.startBlock('html', key, tag);
|
||||
}
|
||||
continue;
|
||||
} else if (!!(matches = line.match(new RegExp("</(" + special + ")>\\s*$", 'i')))) {
|
||||
tag = matches[1].toLowerCase();
|
||||
if (this.isBlock('html', tag)) {
|
||||
this.setBlock(key).endBlock();
|
||||
}
|
||||
continue;
|
||||
} else if (this.isBlock('html')) {
|
||||
this.setBlock(key);
|
||||
continue;
|
||||
}
|
||||
switch (true) {
|
||||
case !!(matches = line.match(/^\[\^((?:[^\]]|\\\]|\\\[)+?)\]:/)):
|
||||
space = matches[0].length - 1;
|
||||
this.startBlock('footnote', key, [space, matches[1]]);
|
||||
break;
|
||||
case !!(matches = line.match(/^\s*\[((?:[^\]]|\\\]|\\\[)+?)\]:\s*(.+)$/)):
|
||||
this.definitions[matches[1]] = this.cleanUrl(matches[2]);
|
||||
this.startBlock('definition', key).endBlock();
|
||||
break;
|
||||
case !!(matches = line.match(/^(\s*)>/)):
|
||||
if ((this.isBlock('list')) && matches[1].length > 0) {
|
||||
this.setBlock(key);
|
||||
} else if (this.isBlock('quote')) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('quote', key);
|
||||
}
|
||||
break;
|
||||
case !!(matches = line.match(/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/)):
|
||||
if (this.isBlock('table')) {
|
||||
block[3][0].push(block[3][2]);
|
||||
block[3][2] += 1;
|
||||
this.setBlock(key, block[3]);
|
||||
} else {
|
||||
head = 0;
|
||||
if ((block == null) || block[0] !== 'normal' || lines[block[2]].match(/^\s*$/)) {
|
||||
this.startBlock('table', key);
|
||||
} else {
|
||||
head = 1;
|
||||
this.backBlock(1, 'table');
|
||||
}
|
||||
if (matches[1][0] === '|') {
|
||||
matches[1] = matches[1].substring(1);
|
||||
if (matches[1][matches[1].length - 1] === '|') {
|
||||
matches[1] = matches[1].substring(0, matches[1].length - 1);
|
||||
}
|
||||
}
|
||||
rows = matches[1].split(/\+|\|/);
|
||||
aligns = [];
|
||||
for (n = 0, len2 = rows.length; n < len2; n++) {
|
||||
row = rows[n];
|
||||
align = 'none';
|
||||
if (!!(matches = row.match(/^\s*(:?)\-+(:?)\s*$/))) {
|
||||
if (!!matches[1] && !!matches[2]) {
|
||||
align = 'center';
|
||||
} else if (!!matches[1]) {
|
||||
align = 'left';
|
||||
} else if (!!matches[2]) {
|
||||
align = 'right';
|
||||
}
|
||||
}
|
||||
aligns.push(align);
|
||||
}
|
||||
this.setBlock(key, [[head], aligns, head + 1]);
|
||||
}
|
||||
break;
|
||||
case !!(matches = line.match(/^(#+)(.*)$/)):
|
||||
num = Math.min(matches[1].length, 6);
|
||||
this.startBlock('sh', key, num).endBlock();
|
||||
break;
|
||||
case !!(matches = line.match(/^\s*((=|-){2,})\s*$/)) && ((block != null) && block[0] === 'normal' && !lines[block[2]].match(/^\s*$/)):
|
||||
if (this.isBlock('normal')) {
|
||||
this.backBlock(1, 'mh', matches[1][0] === '=' ? 1 : 2).setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
break;
|
||||
case !!(line.match(/^[-\*]{3,}\s*$/)):
|
||||
this.startBlock('hr', key).endBlock();
|
||||
break;
|
||||
default:
|
||||
if (this.isBlock('list')) {
|
||||
if (!!(matches = line.match(/^(\s*)/))) {
|
||||
indent = matches[1].length > 0;
|
||||
if (emptyCount > 0 && !indent) {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
if (indent) {
|
||||
emptyCount = 0;
|
||||
} else {
|
||||
emptyCount += 1;
|
||||
}
|
||||
} else if (emptyCount === 0) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('footnote')) {
|
||||
matches = line.match(/^(\s*)/);
|
||||
if (matches[1].length >= block[3][0]) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('table')) {
|
||||
if (0 <= line.indexOf('|')) {
|
||||
block[3][2] += 1;
|
||||
this.setBlock(key, block[3]);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('quote')) {
|
||||
if (!line.match(/^(\s*)$/)) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else {
|
||||
if ((block == null) || block[0] !== 'normal') {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.optimizeBlocks(this.blocks, lines);
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockList = function(block, key, line, state) {
|
||||
var matches, space;
|
||||
if (!!(matches = line.match(/^(\s*)((?:[0-9]+\.)|(?:[a-z]\.?)|\-|\+|\*)\s+/i))) {
|
||||
space = matches[1].length;
|
||||
state.empty = 0;
|
||||
if (this.isBlock('list')) {
|
||||
this.setBlock(key, space);
|
||||
} else {
|
||||
this.startBlock('list', key, space);
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('list')) {
|
||||
if ((state.empty === 0) && !!(matches = line.match(/^(\s+)/)) && matches[1].length > block[3]) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockCode = function(block, key, line) {
|
||||
var isAfterList, matches, space;
|
||||
if (!!(matches = line.match(/^(\s*)(~{3,}|`{3,})([^`~]*)$/i))) {
|
||||
if (this.isBlock('code')) {
|
||||
isAfterList = block[3][2];
|
||||
if (isAfterList) {
|
||||
this.combineBlock().setBlock(key);
|
||||
} else {
|
||||
(this.setBlock(key)).endBlock();
|
||||
}
|
||||
} else {
|
||||
isAfterList = false;
|
||||
if (this.isBlock('list')) {
|
||||
space = block[3];
|
||||
isAfterList = (space > 0 && matches[1].length >= space) || matches[1].length > space;
|
||||
}
|
||||
this.startBlock('code', key, [matches[1], matches[3], isAfterList]);
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('code')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockShtml = function(block, key, line, state) {
|
||||
var matches;
|
||||
if (this.html) {
|
||||
if (!!(matches = line.match(/^(\s*)!!!(\s*)$/))) {
|
||||
if (this.isBlock('shtml')) {
|
||||
this.setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('shtml', key);
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('shtml')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockAhtml = function(block, key, line, state) {
|
||||
var htmlTagAllRegExp, htmlTagRegExp, lastMatch, m, matches;
|
||||
if (this.html) {
|
||||
htmlTagRegExp = new RegExp("^\\s*<(" + this.blockHtmlTags + ")(\\s+[^>]*)?>", 'i');
|
||||
if (matches = line.match(htmlTagRegExp)) {
|
||||
if (this.isBlock('ahtml')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
} else if (matches[2] === void 0 || matches[2] !== '/') {
|
||||
this.startBlock('ahtml', key);
|
||||
htmlTagAllRegExp = new RegExp("\\s*<(" + this.blockHtmlTags + ")(\\s+[^>]*)?>", 'ig');
|
||||
while (true) {
|
||||
m = htmlTagAllRegExp.exec(line);
|
||||
if (!m) {
|
||||
break;
|
||||
}
|
||||
lastMatch = m[1];
|
||||
}
|
||||
if (0 <= line.indexOf("</" + lastMatch + ">")) {
|
||||
this.endBlock();
|
||||
} else {
|
||||
state.html = lastMatch;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (!!state.html && 0 <= line.indexOf("</" + state.html + ">")) {
|
||||
this.setBlock(key).endBlock();
|
||||
state.html = false;
|
||||
return false;
|
||||
} else if (this.isBlock('ahtml')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
} else if (!!(matches = line.match(/^\s*<!\-\-(.*?)\-\->\s*$/))) {
|
||||
this.startBlock('ahtml', key).endBlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockMath = function(block, key, line) {
|
||||
var matches;
|
||||
if (!!(matches = line.match(/^(\s*)\$\$(\s*)$/))) {
|
||||
if (this.isBlock('math')) {
|
||||
this.setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('math', key);
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('math')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockPre = function(block, key, line, state) {
|
||||
if (!!(line.match(/^ {4}/))) {
|
||||
state.empty = 0;
|
||||
if (this.isBlock('pre')) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('pre', key);
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('pre')) {
|
||||
if (line.match(/^\s*$/)) {
|
||||
if (state.empty > 0) {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
state.empty += 1;
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockHtml = function(block, key, line, state) {
|
||||
var matches, tag;
|
||||
if (!!(matches = line.match(new RegExp("^\\s*<(" + state.special + ")(\\s+[^>]*)?>", 'i')))) {
|
||||
tag = matches[1].toLowerCase();
|
||||
if (!(this.isBlock('html', tag)) && !(this.isBlock('pre'))) {
|
||||
this.startBlock('html', key, tag);
|
||||
}
|
||||
return false;
|
||||
} else if (!!(matches = line.match(new RegExp("</(" + state.special + ")>\\s*$", 'i')))) {
|
||||
tag = matches[1].toLowerCase();
|
||||
if (this.isBlock('html', tag)) {
|
||||
this.setBlock(key).endBlock();
|
||||
}
|
||||
return false;
|
||||
} else if (this.isBlock('html')) {
|
||||
this.setBlock(key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockFootnote = function(block, key, line) {
|
||||
var matches, space;
|
||||
if (!!(matches = line.match(/^\[\^((?:[^\]]|\\\]|\\\[)+?)\]:/))) {
|
||||
space = matches[0].length - 1;
|
||||
this.startBlock('footnote', key, [space, matches[1]]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockDefinition = function(block, key, line) {
|
||||
var matches;
|
||||
if (!!(matches = line.match(/^\s*\[((?:[^\]]|\\\]|\\\[)+?)\]:\s*(.+)$/))) {
|
||||
this.definitions[matches[1]] = this.cleanUrl(matches[2]);
|
||||
this.startBlock('definition', key).endBlock();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockQuote = function(block, key, line) {
|
||||
var matches;
|
||||
if (!!(matches = line.match(/^(\s*)>/))) {
|
||||
if ((this.isBlock('list')) && matches[1].length > 0) {
|
||||
this.setBlock(key);
|
||||
} else if (this.isBlock('quote')) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('quote', key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockTable = function(block, key, line, state, lines) {
|
||||
var align, aligns, head, j, len, matches, row, rows;
|
||||
if (!!(matches = line.match(/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/))) {
|
||||
if (this.isBlock('table')) {
|
||||
block[3][0].push(block[3][2]);
|
||||
block[3][2] += 1;
|
||||
this.setBlock(key, block[3]);
|
||||
} else {
|
||||
head = 0;
|
||||
if ((block == null) || block[0] !== 'normal' || lines[block[2]].match(/^\s*$/)) {
|
||||
this.startBlock('table', key);
|
||||
} else {
|
||||
head = 1;
|
||||
this.backBlock(1, 'table');
|
||||
}
|
||||
if (matches[1][0] === '|') {
|
||||
matches[1] = matches[1].substring(1);
|
||||
if (matches[1][matches[1].length - 1] === '|') {
|
||||
matches[1] = matches[1].substring(0, matches[1].length - 1);
|
||||
}
|
||||
}
|
||||
rows = matches[1].split(/\+|\|/);
|
||||
aligns = [];
|
||||
for (j = 0, len = rows.length; j < len; j++) {
|
||||
row = rows[j];
|
||||
align = 'none';
|
||||
if (!!(matches = row.match(/^\s*(:?)\-+(:?)\s*$/))) {
|
||||
if (!!matches[1] && !!matches[2]) {
|
||||
align = 'center';
|
||||
} else if (!!matches[1]) {
|
||||
align = 'left';
|
||||
} else if (!!matches[2]) {
|
||||
align = 'right';
|
||||
}
|
||||
}
|
||||
aligns.push(align);
|
||||
}
|
||||
this.setBlock(key, [[head], aligns, head + 1]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockSh = function(block, key, line) {
|
||||
var matches, num;
|
||||
if (!!(matches = line.match(/^(#+)(.*)$/))) {
|
||||
num = Math.min(matches[1].length, 6);
|
||||
this.startBlock('sh', key, num).endBlock();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockMh = function(block, key, line, state, lines) {
|
||||
var matches;
|
||||
if (!!(matches = line.match(/^\s*((=|-){2,})\s*$/)) && ((block != null) && block[0] === 'normal' && !lines[block[2]].match(/^\s*$/))) {
|
||||
if (this.isBlock('normal')) {
|
||||
this.backBlock(1, 'mh', matches[1][0] === '=' ? 1 : 2).setBlock(key).endBlock();
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockHr = function(block, key, line) {
|
||||
if (!!(line.match(/^[-\*]{3,}\s*$/))) {
|
||||
this.startBlock('hr', key).endBlock();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.parseBlockDefault = function(block, key, line, state) {
|
||||
var indent, matches;
|
||||
if (this.isBlock('list')) {
|
||||
if (!!(matches = line.match(/^(\s*)/))) {
|
||||
indent = matches[1].length > 0;
|
||||
if (state.empty > 0 && !indent) {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
if (indent) {
|
||||
state.empty = 0;
|
||||
} else {
|
||||
state.empty += 1;
|
||||
}
|
||||
} else if (state.empty === 0) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('footnote')) {
|
||||
matches = line.match(/^(\s*)/);
|
||||
if (matches[1].length >= block[3][0]) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('table')) {
|
||||
if (0 <= line.indexOf('|')) {
|
||||
block[3][2] += 1;
|
||||
this.setBlock(key, block[3]);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else if (this.isBlock('quote')) {
|
||||
if (!line.match(/^(\s*)$/)) {
|
||||
this.setBlock(key);
|
||||
} else {
|
||||
this.startBlock('normal', key);
|
||||
}
|
||||
} else {
|
||||
if ((block == null) || block[0] !== 'normal') {
|
||||
this.startBlock('normal', key);
|
||||
} else {
|
||||
this.setBlock(key);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Parser.prototype.optimizeBlocks = function(_blocks, _lines) {
|
||||
var block, blocks, from, isEmpty, key, lines, moved, nextBlock, prevBlock, to, type, types;
|
||||
blocks = _blocks.slice(0);
|
||||
@@ -879,7 +995,7 @@
|
||||
}
|
||||
rows = line.split('|').map(function(row) {
|
||||
if (row.match(/^\s+$/)) {
|
||||
return '';
|
||||
return ' ';
|
||||
} else {
|
||||
return trim(row);
|
||||
}
|
||||
|
||||
+542
-308
@@ -45,6 +45,27 @@ class HyperDown
|
||||
*/
|
||||
public $_html = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $blockParsers = array(
|
||||
array('code', 10),
|
||||
array('shtml', 20),
|
||||
array('ahtml', 30),
|
||||
array('list', 40),
|
||||
array('math', 50),
|
||||
array('pre', 60),
|
||||
array('html', 70),
|
||||
array('footnote', 80),
|
||||
array('definition', 90),
|
||||
array('quote', 100),
|
||||
array('table', 110),
|
||||
array('sh', 120),
|
||||
array('mh', 130),
|
||||
array('hr', 140),
|
||||
array('default', 9999)
|
||||
);
|
||||
|
||||
/**
|
||||
* _blocks
|
||||
*
|
||||
@@ -93,6 +114,11 @@ class HyperDown
|
||||
*/
|
||||
private $_id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_parsers = array();
|
||||
|
||||
/**
|
||||
* makeHtml
|
||||
*
|
||||
@@ -107,6 +133,20 @@ class HyperDown
|
||||
$this->_uniqid = md5(uniqid());
|
||||
$this->_id = 0;
|
||||
|
||||
usort($this->blockParsers, function ($a, $b) {
|
||||
return $a[1] < $b[1] ? -1 : 1;
|
||||
});
|
||||
|
||||
foreach ($this->blockParsers as $parser) {
|
||||
list ($name) = $parser;
|
||||
|
||||
if (isset($parser[2])) {
|
||||
$this->_parsers[$name] = $parser[2];
|
||||
} else {
|
||||
$this->_parsers[$name] = array($this, 'parseBlock' . ucfirst($name));
|
||||
}
|
||||
}
|
||||
|
||||
$text = $this->initText($text);
|
||||
$html = $this->parse($text);
|
||||
$html = $this->makeFootnotes($html);
|
||||
@@ -538,333 +578,527 @@ class HyperDown
|
||||
$this->_blocks = array();
|
||||
$this->_current = 'normal';
|
||||
$this->_pos = -1;
|
||||
$special = implode("|", array_keys($this->_specialWhiteList));
|
||||
$emptyCount = 0;
|
||||
$autoHtml = false;
|
||||
|
||||
$state = array(
|
||||
'special' => implode("|", array_keys($this->_specialWhiteList)),
|
||||
'empty' => 0,
|
||||
'html' => false
|
||||
);
|
||||
|
||||
// analyze by line
|
||||
foreach ($lines as $key => $line) {
|
||||
$block = $this->getBlock();
|
||||
$args = array($block, $key, $line, &$state, $lines);
|
||||
|
||||
// list block
|
||||
if (preg_match("/^(\s*)((?:[0-9]+\.)|(?:[a-z]\.?)|\-|\+|\*)\s+/i", $line, $matches)) {
|
||||
$space = strlen($matches[1]);
|
||||
$emptyCount = 0;
|
||||
if ($this->_current != 'normal') {
|
||||
$pass = call_user_func_array($this->_parsers[$this->_current], $args);
|
||||
|
||||
// opened
|
||||
if ($this->isBlock('list')) {
|
||||
$this->setBlock($key, $space);
|
||||
} else {
|
||||
$this->startBlock('list', $key, $space);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('list')) {
|
||||
if ($emptyCount == 0
|
||||
&& preg_match("/^(\s+)/", $line, $matches)
|
||||
&& strlen($matches[1]) > $block[3]) {
|
||||
$this->setBlock($key);
|
||||
if (!$pass) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// code block
|
||||
if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
|
||||
if ($this->isBlock('code')) {
|
||||
$isAfterList = $block[3][2];
|
||||
foreach ($this->_parsers as $name => $parser) {
|
||||
if ($name != $this->_current) {
|
||||
$pass = call_user_func_array($parser, $args);
|
||||
|
||||
if ($isAfterList) {
|
||||
$this->combineBlock()
|
||||
->setBlock($key);
|
||||
} else {
|
||||
$this->setBlock($key)
|
||||
->endBlock();
|
||||
if (!$pass) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$isAfterList = false;
|
||||
|
||||
if ($this->isBlock('list')) {
|
||||
$space = $block[3];
|
||||
|
||||
$isAfterList = ($space > 0 && strlen($matches[1]) >= $space)
|
||||
|| strlen($matches[1]) > $space;
|
||||
}
|
||||
|
||||
$this->startBlock('code', $key, array(
|
||||
$matches[1], $matches[3], $isAfterList
|
||||
));
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('code')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// super html mode
|
||||
if ($this->_html) {
|
||||
if (!$autoHtml && preg_match("/^(\s*)!!!(\s*)$/", $line, $matches)) {
|
||||
if ($this->isBlock('shtml')) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
} else {
|
||||
$this->startBlock('shtml', $key);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('shtml')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// auto html
|
||||
if (preg_match("/^\s*<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $matches)) {
|
||||
if ($this->isBlock('ahtml')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
} else if (empty($matches[2]) || $matches[2] != '/') {
|
||||
$this->startBlock('ahtml', $key);
|
||||
preg_match_all("/<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $allMatches);
|
||||
$lastMatch = $allMatches[1][count($allMatches[0]) - 1];
|
||||
|
||||
if (strpos($line, "</{$lastMatch}>") !== false) {
|
||||
$this->endBlock();
|
||||
} else {
|
||||
$autoHtml = $lastMatch;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (!!$autoHtml && strpos($line, "</{$autoHtml}>") !== false) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
$autoHtml = false;
|
||||
continue;
|
||||
} else if ($this->isBlock('ahtml')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
} else if (preg_match("/^\s*<!\-\-(.*?)\-\->\s*$/", $line, $matches)) {
|
||||
$this->startBlock('ahtml', $key)->endBlock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// mathjax mode
|
||||
if (preg_match("/^(\s*)\\$\\$(\s*)$/", $line, $matches)) {
|
||||
if ($this->isBlock('math')) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
} else {
|
||||
$this->startBlock('math', $key);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('math')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// pre block
|
||||
if (preg_match("/^ {4}/", $line)) {
|
||||
$emptyCount = 0;
|
||||
|
||||
if ($this->isBlock('pre') || $this->isBlock('list')) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('pre', $key);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('pre')) {
|
||||
if (preg_match("/^\s*$/", $line)) {
|
||||
if ($emptyCount > 0) {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
|
||||
$emptyCount ++;
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// html block is special too
|
||||
if (preg_match("/^\s*<({$special})(\s+[^>]*)?>/i", $line, $matches)) {
|
||||
$tag = strtolower($matches[1]);
|
||||
if (!$this->isBlock('html', $tag) && !$this->isBlock('pre')) {
|
||||
$this->startBlock('html', $key, $tag);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (preg_match("/<\/({$special})>\s*$/i", $line, $matches)) {
|
||||
$tag = strtolower($matches[1]);
|
||||
|
||||
if ($this->isBlock('html', $tag)) {
|
||||
$this->setBlock($key)
|
||||
->endBlock();
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ($this->isBlock('html')) {
|
||||
$this->setBlock($key);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
// footnote
|
||||
case preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches):
|
||||
$space = strlen($matches[0]) - 1;
|
||||
$this->startBlock('footnote', $key, array(
|
||||
$space, $matches[1]
|
||||
));
|
||||
break;
|
||||
|
||||
// definition
|
||||
case preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches):
|
||||
$this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]);
|
||||
$this->startBlock('definition', $key)
|
||||
->endBlock();
|
||||
break;
|
||||
|
||||
// block quote
|
||||
case preg_match("/^(\s*)>/", $line, $matches):
|
||||
if ($this->isBlock('list') && strlen($matches[1]) > 0) {
|
||||
$this->setBlock($key);
|
||||
} else if ($this->isBlock('quote')) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('quote', $key);
|
||||
}
|
||||
break;
|
||||
|
||||
// table
|
||||
case preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches):
|
||||
if ($this->isBlock('table')) {
|
||||
$block[3][0][] = $block[3][2];
|
||||
$block[3][2] ++;
|
||||
$this->setBlock($key, $block[3]);
|
||||
} else {
|
||||
$head = 0;
|
||||
|
||||
if (empty($block) ||
|
||||
$block[0] != 'normal' ||
|
||||
preg_match("/^\s*$/", $lines[$block[2]])) {
|
||||
$this->startBlock('table', $key);
|
||||
} else {
|
||||
$head = 1;
|
||||
$this->backBlock(1, 'table');
|
||||
}
|
||||
|
||||
if ($matches[1][0] == '|') {
|
||||
$matches[1] = substr($matches[1], 1);
|
||||
|
||||
if ($matches[1][strlen($matches[1]) - 1] == '|') {
|
||||
$matches[1] = substr($matches[1], 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
$rows = preg_split("/(\+|\|)/", $matches[1]);
|
||||
$aligns = array();
|
||||
foreach ($rows as $row) {
|
||||
$align = 'none';
|
||||
|
||||
if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
|
||||
if (!empty($matches[1]) && !empty($matches[2])) {
|
||||
$align = 'center';
|
||||
} else if (!empty($matches[1])) {
|
||||
$align = 'left';
|
||||
} else if (!empty($matches[2])) {
|
||||
$align = 'right';
|
||||
}
|
||||
}
|
||||
|
||||
$aligns[] = $align;
|
||||
}
|
||||
|
||||
$this->setBlock($key, array(array($head), $aligns, $head + 1));
|
||||
}
|
||||
break;
|
||||
|
||||
// single heading
|
||||
case preg_match("/^(#+)(.*)$/", $line, $matches):
|
||||
$num = min(strlen($matches[1]), 6);
|
||||
$this->startBlock('sh', $key, $num)
|
||||
->endBlock();
|
||||
break;
|
||||
|
||||
// multi heading
|
||||
case preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
|
||||
&& ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]])): // check if last line isn't empty
|
||||
if ($this->isBlock('normal')) {
|
||||
$this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2)
|
||||
->setBlock($key)
|
||||
->endBlock();
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
break;
|
||||
|
||||
// hr
|
||||
case preg_match("/^[-\*]{3,}\s*$/", $line):
|
||||
$this->startBlock('hr', $key)
|
||||
->endBlock();
|
||||
break;
|
||||
|
||||
// normal
|
||||
default:
|
||||
if ($this->isBlock('list')) {
|
||||
if (preg_match("/^(\s*)/", $line, $matches)) { // empty line
|
||||
$indent = strlen($matches[1]) > 0;
|
||||
|
||||
if ($emptyCount > 0 && !$indent) {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
|
||||
if ($indent) {
|
||||
$emptyCount = 0;
|
||||
} else {
|
||||
$emptyCount ++;
|
||||
}
|
||||
} else if ($emptyCount == 0) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('footnote')) {
|
||||
preg_match("/^(\s*)/", $line, $matches);
|
||||
if (strlen($matches[1]) >= $block[3][0]) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('table')) {
|
||||
if (false !== strpos($line, '|')) {
|
||||
$block[3][2] ++;
|
||||
$this->setBlock($key, $block[3]);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('quote')) {
|
||||
if (!preg_match("/^(\s*)$/", $line)) { // empty line
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else {
|
||||
if (empty($block) || $block[0] != 'normal') {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->optimizeBlocks($this->_blocks, $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockList($block, $key, $line, &$state)
|
||||
{
|
||||
if (preg_match("/^(\s*)((?:[0-9]+\.)|(?:[a-z]\.?)|\-|\+|\*)\s+/i", $line, $matches)) {
|
||||
$space = strlen($matches[1]);
|
||||
$state['empty'] = 0;
|
||||
|
||||
// opened
|
||||
if ($this->isBlock('list')) {
|
||||
$this->setBlock($key, $space);
|
||||
} else {
|
||||
$this->startBlock('list', $key, $space);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('list')) {
|
||||
if ($state['empty'] == 0
|
||||
&& preg_match("/^(\s+)/", $line, $matches)
|
||||
&& strlen($matches[1]) > $block[3]) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockCode($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
|
||||
if ($this->isBlock('code')) {
|
||||
$isAfterList = $block[3][2];
|
||||
|
||||
if ($isAfterList) {
|
||||
$this->combineBlock()
|
||||
->setBlock($key);
|
||||
} else {
|
||||
$this->setBlock($key)
|
||||
->endBlock();
|
||||
}
|
||||
} else {
|
||||
$isAfterList = false;
|
||||
|
||||
if ($this->isBlock('list')) {
|
||||
$space = $block[3];
|
||||
|
||||
$isAfterList = ($space > 0 && strlen($matches[1]) >= $space)
|
||||
|| strlen($matches[1]) > $space;
|
||||
}
|
||||
|
||||
$this->startBlock('code', $key, array(
|
||||
$matches[1], $matches[3], $isAfterList
|
||||
));
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('code')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockShtml($block, $key, $line, &$state)
|
||||
{
|
||||
if ($this->_html) {
|
||||
if (preg_match("/^(\s*)!!!(\s*)$/", $line, $matches)) {
|
||||
if ($this->isBlock('shtml')) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
} else {
|
||||
$this->startBlock('shtml', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('shtml')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockAhtml($block, $key, $line, &$state)
|
||||
{
|
||||
if ($this->_html) {
|
||||
if (preg_match("/^\s*<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $matches)) {
|
||||
if ($this->isBlock('ahtml')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
} else if (empty($matches[2]) || $matches[2] != '/') {
|
||||
$this->startBlock('ahtml', $key);
|
||||
preg_match_all("/<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $allMatches);
|
||||
$lastMatch = $allMatches[1][count($allMatches[0]) - 1];
|
||||
|
||||
if (strpos($line, "</{$lastMatch}>") !== false) {
|
||||
$this->endBlock();
|
||||
} else {
|
||||
$state['html'] = $lastMatch;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (!!$state['html'] && strpos($line, "</{$state['html']}>") !== false) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
$state['html'] = false;
|
||||
return false;
|
||||
} else if ($this->isBlock('ahtml')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
} else if (preg_match("/^\s*<!\-\-(.*?)\-\->\s*$/", $line, $matches)) {
|
||||
$this->startBlock('ahtml', $key)->endBlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockMath($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^(\s*)\\$\\$(\s*)$/", $line, $matches)) {
|
||||
if ($this->isBlock('math')) {
|
||||
$this->setBlock($key)->endBlock();
|
||||
} else {
|
||||
$this->startBlock('math', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('math')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockPre($block, $key, $line, &$state)
|
||||
{
|
||||
if (preg_match("/^ {4}/", $line)) {
|
||||
$state['empty'] = 0;
|
||||
|
||||
if ($this->isBlock('pre')) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('pre', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('pre')) {
|
||||
if (preg_match("/^\s*$/", $line)) {
|
||||
if ($state['empty'] > 0) {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
|
||||
$state['empty'] ++;
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockHtml($block, $key, $line, &$state)
|
||||
{
|
||||
if (preg_match("/^\s*<({$state['special']})(\s+[^>]*)?>/i", $line, $matches)) {
|
||||
$tag = strtolower($matches[1]);
|
||||
if (!$this->isBlock('html', $tag) && !$this->isBlock('pre')) {
|
||||
$this->startBlock('html', $key, $tag);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if (preg_match("/<\/({$state['special']})>\s*$/i", $line, $matches)) {
|
||||
$tag = strtolower($matches[1]);
|
||||
|
||||
if ($this->isBlock('html', $tag)) {
|
||||
$this->setBlock($key)
|
||||
->endBlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ($this->isBlock('html')) {
|
||||
$this->setBlock($key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockFootnote($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) {
|
||||
$space = strlen($matches[0]) - 1;
|
||||
$this->startBlock('footnote', $key, array(
|
||||
$space, $matches[1]
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockDefinition($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches)) {
|
||||
$this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]);
|
||||
$this->startBlock('definition', $key)
|
||||
->endBlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockQuote($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^(\s*)>/", $line, $matches)) {
|
||||
if ($this->isBlock('list') && strlen($matches[1]) > 0) {
|
||||
$this->setBlock($key);
|
||||
} else if ($this->isBlock('quote')) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('quote', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @param $lines
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockTable($block, $key, $line, &$state, $lines)
|
||||
{
|
||||
if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
|
||||
if ($this->isBlock('table')) {
|
||||
$block[3][0][] = $block[3][2];
|
||||
$block[3][2]++;
|
||||
$this->setBlock($key, $block[3]);
|
||||
} else {
|
||||
$head = 0;
|
||||
|
||||
if (empty($block) ||
|
||||
$block[0] != 'normal' ||
|
||||
preg_match("/^\s*$/", $lines[$block[2]])) {
|
||||
$this->startBlock('table', $key);
|
||||
} else {
|
||||
$head = 1;
|
||||
$this->backBlock(1, 'table');
|
||||
}
|
||||
|
||||
if ($matches[1][0] == '|') {
|
||||
$matches[1] = substr($matches[1], 1);
|
||||
|
||||
if ($matches[1][strlen($matches[1]) - 1] == '|') {
|
||||
$matches[1] = substr($matches[1], 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
$rows = preg_split("/(\+|\|)/", $matches[1]);
|
||||
$aligns = array();
|
||||
foreach ($rows as $row) {
|
||||
$align = 'none';
|
||||
|
||||
if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
|
||||
if (!empty($matches[1]) && !empty($matches[2])) {
|
||||
$align = 'center';
|
||||
} else if (!empty($matches[1])) {
|
||||
$align = 'left';
|
||||
} else if (!empty($matches[2])) {
|
||||
$align = 'right';
|
||||
}
|
||||
}
|
||||
|
||||
$aligns[] = $align;
|
||||
}
|
||||
|
||||
$this->setBlock($key, array(array($head), $aligns, $head + 1));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockSh($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^(#+)(.*)$/", $line, $matches)) {
|
||||
$num = min(strlen($matches[1]), 6);
|
||||
$this->startBlock('sh', $key, $num)
|
||||
->endBlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @param $lines
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockMh($block, $key, $line, &$state, $lines)
|
||||
{
|
||||
if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
|
||||
&& ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) { // check if last line isn't empty
|
||||
if ($this->isBlock('normal')) {
|
||||
$this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2)
|
||||
->setBlock($key)
|
||||
->endBlock();
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockHr($block, $key, $line)
|
||||
{
|
||||
if (preg_match("/^[-\*]{3,}\s*$/", $line)) {
|
||||
$this->startBlock('hr', $key)
|
||||
->endBlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $block
|
||||
* @param $key
|
||||
* @param $line
|
||||
* @param $state
|
||||
* @return bool
|
||||
*/
|
||||
private function parseBlockDefault($block, $key, $line, &$state)
|
||||
{
|
||||
if ($this->isBlock('list')) {
|
||||
if (preg_match("/^(\s*)/", $line, $matches)) { // empty line
|
||||
$indent = strlen($matches[1]) > 0;
|
||||
|
||||
if ($state['empty'] > 0 && !$indent) {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
|
||||
if ($indent) {
|
||||
$state['empty'] = 0;
|
||||
} else {
|
||||
$state['empty'] ++;
|
||||
}
|
||||
} else if ($state['empty'] == 0) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('footnote')) {
|
||||
preg_match("/^(\s*)/", $line, $matches);
|
||||
if (strlen($matches[1]) >= $block[3][0]) {
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('table')) {
|
||||
if (false !== strpos($line, '|')) {
|
||||
$block[3][2] ++;
|
||||
$this->setBlock($key, $block[3]);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else if ($this->isBlock('quote')) {
|
||||
if (!preg_match("/^(\s*)$/", $line)) { // empty line
|
||||
$this->setBlock($key);
|
||||
} else {
|
||||
$this->startBlock('normal', $key);
|
||||
}
|
||||
} else {
|
||||
if (empty($block) || $block[0] != 'normal') {
|
||||
$this->startBlock('normal', $key);
|
||||
} else {
|
||||
$this->setBlock($key);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $blocks
|
||||
* @param array $lines
|
||||
|
||||
Reference in New Issue
Block a user