Browse Source

Move link helpers to it's own file

+ return value of parseLink* functions in the state
pull/14/head
Alex Kocharin 10 years ago
parent
commit
0705173c87
  1. 166
      lib/links.js
  2. 20
      lib/parser_ref.js
  3. 170
      lib/rules_inline/links.js
  4. 1
      lib/rules_inline/state_inline.js

166
lib/links.js

@ -0,0 +1,166 @@
'use strict';
//
// Parse link label
//
// this function assumes that first character ("[") already matches;
// returns the end of the label
function parseLinkLabel(state, start) {
var level, found, marker, ok,
labelEnd = -1,
max = state.posMax,
oldPos = state.pos,
oldLength = state.tokens.length,
oldPending = state.pending,
oldFlag = state.validateInsideLink;
if (state.validateInsideLink) { return -1; }
state.pos = start + 1;
state.validateInsideLink = true;
level = 1;
while (state.pos < max) {
marker = state.src.charCodeAt(state.pos);
if (marker === 0x5B /* [ */) {
level++;
} else if (marker === 0x5D /* ] */) {
level--;
if (level === 0) {
found = true;
break;
}
}
ok = state.parser.tokenizeSingle(state);
if (!ok) { state.pending += state.src[state.pos++]; }
}
if (found) { labelEnd = state.pos; }
// restore old state
state.pos = oldPos;
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideLink = oldFlag;
return labelEnd;
}
//
// Parse link destination
//
// on success it returns a string and updates state.pos;
// on failure it returns null
function parseLinkDestination(state, pos) {
var code, level,
max = state.posMax,
href = '';
if (state.src.charCodeAt(pos) === 0x3C /* < */) {
pos++;
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === 0x0A /* \n */) { return false; }
if (code === 0x3E /* > */) {
state.pos = pos + 1;
state.link_content = href;
return true;
}
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
href += state.src[pos++];
continue;
}
href += state.src[pos++];
}
// no closing '>'
return false;
}
// this should be ... } else { ... branch
level = 0;
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === 0x20) { break; }
// ascii control characters
if (code < 0x20 || code === 0x7F) { break; }
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
href += state.src[pos++];
continue;
}
if (code === 0x28 /* ( */) {
level++;
if (level > 1) { break; }
}
if (code === 0x29 /* ) */) {
level--;
if (level < 0) { break; }
}
href += state.src[pos++];
}
if (!href.length) { return false; }
state.pos = pos;
state.link_content = href;
return true;
}
//
// Parse link title
//
// on success it returns a string and updates state.pos;
// on failure it returns null
function parseLinkTitle(state, pos) {
var title, code,
max = state.posMax,
marker = state.src.charCodeAt(pos);
if (marker !== 0x22 /* " */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return false; }
pos++;
title = '';
// if opening marker is "(", switch it to closing marker ")"
if (marker === 0x28) { marker = 0x29; }
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === marker) {
state.pos = pos + 1;
state.link_content = title;
return true;
}
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
title += state.src[pos++];
continue;
}
title += state.src[pos++];
}
return false;
}
function normalizeReference(str) {
return str.trim().replace(/\s+/g, ' ').toLowerCase();
}
module.exports.parseLinkLabel = parseLinkLabel;
module.exports.parseLinkDestination = parseLinkDestination;
module.exports.parseLinkTitle = parseLinkTitle;
module.exports.normalizeReference = normalizeReference;

20
lib/parser_ref.js

@ -2,9 +2,12 @@
'use strict'; 'use strict';
var StateInline = require('./rules_inline/state_inline'); var StateInline = require('./rules_inline/state_inline');
var links = require('./rules_inline/links'); var skipSpaces = require('./helpers').skipSpaces;
var skipSpaces = require('./helpers').skipSpaces; var parseLinkLabel = require('./links').parseLinkLabel;
var parseLinkDestination = require('./links').parseLinkDestination;
var parseLinkTitle = require('./links').parseLinkTitle;
var normalizeReference = require('./links').normalizeReference;
// Parse link reference definition. // Parse link reference definition.
@ -18,7 +21,7 @@ module.exports = function parse_reference(str, parser, options, env) {
if (str.indexOf(']:') === -1) { return -1; } if (str.indexOf(']:') === -1) { return -1; }
state = new StateInline(str, parser, options, env); state = new StateInline(str, parser, options, env);
labelEnd = links.parseLinkLabel(state, 0); labelEnd = parseLinkLabel(state, 0);
if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return -1; } if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return -1; }
@ -33,8 +36,8 @@ module.exports = function parse_reference(str, parser, options, env) {
// [label]: destination 'title' // [label]: destination 'title'
// ^^^^^^^^^^^ parse this // ^^^^^^^^^^^ parse this
href = links.parseLinkDestination(state, pos); if (!parseLinkDestination(state, pos)) { return -1; }
if (href === null) { return -1; } href = state.link_content;
pos = state.pos; pos = state.pos;
// [label]: destination 'title' // [label]: destination 'title'
@ -47,7 +50,8 @@ module.exports = function parse_reference(str, parser, options, env) {
// [label]: destination 'title' // [label]: destination 'title'
// ^^^^^^^ parse this // ^^^^^^^ parse this
if (pos < max && start !== pos && (title = links.parseLinkTitle(state, pos)) !== null) { if (pos < max && start !== pos && parseLinkTitle(state, pos)) {
title = state.link_content;
pos = state.pos; pos = state.pos;
} else { } else {
title = ''; title = '';
@ -58,7 +62,7 @@ module.exports = function parse_reference(str, parser, options, env) {
pos = skipSpaces(state, pos); pos = skipSpaces(state, pos);
if (pos < max && state.src.charCodeAt(pos) !== 0x0A) { return -1; } if (pos < max && state.src.charCodeAt(pos) !== 0x0A) { return -1; }
label = links.normalizeReference(str.slice(1, labelEnd)); label = normalizeReference(str.slice(1, labelEnd));
env.references[label] = env.references[label] || { title: title, href: href }; env.references[label] = env.references[label] || { title: title, href: href };
return pos; return pos;

170
lib/rules_inline/links.js

@ -2,162 +2,11 @@
'use strict'; 'use strict';
var parseLinkLabel = require('../links').parseLinkLabel;
var parseLinkDestination = require('../links').parseLinkDestination;
var parseLinkTitle = require('../links').parseLinkTitle;
var normalizeReference = require('../links').normalizeReference;
//
// Parse link label
//
// this function assumes that first character ("[") already matches;
// returns the end of the label
function parseLinkLabel(state, start) {
var level, found, marker, ok,
labelEnd = -1,
max = state.posMax,
oldPos = state.pos,
oldLength = state.tokens.length,
oldPending = state.pending,
oldFlag = state.validateInsideLink;
if (state.validateInsideLink) { return -1; }
state.pos = start + 1;
state.validateInsideLink = true;
level = 1;
while (state.pos < max) {
marker = state.src.charCodeAt(state.pos);
if (marker === 0x5B /* [ */) {
level++;
} else if (marker === 0x5D /* ] */) {
level--;
if (level === 0) {
found = true;
break;
}
}
ok = state.parser.tokenizeSingle(state);
if (!ok) { state.pending += state.src[state.pos++]; }
}
if (found) { labelEnd = state.pos; }
// restore old state
state.pos = oldPos;
state.tokens.length = oldLength;
state.pending = oldPending;
state.validateInsideLink = oldFlag;
return labelEnd;
}
//
// Parse link destination
//
// on success it returns a string and updates state.pos;
// on failure it returns null
function parseLinkDestination(state, pos) {
var code, level,
max = state.posMax,
href = '';
if (state.src.charCodeAt(pos) === 0x3C /* < */) {
pos++;
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === 0x0A /* \n */) { return null; }
if (code === 0x3E /* > */) {
state.pos = pos + 1;
return href;
}
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
href += state.src[pos++];
continue;
}
href += state.src[pos++];
}
// no closing '>'
return null;
}
// this should be ... } else { ... branch
level = 0;
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === 0x20) { break; }
// ascii control characters
if (code < 0x20 || code === 0x7F) { break; }
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
href += state.src[pos++];
continue;
}
if (code === 0x28 /* ( */) {
level++;
if (level > 1) { break; }
}
if (code === 0x29 /* ) */) {
level--;
if (level < 0) { break; }
}
href += state.src[pos++];
}
if (!href.length) { return null; }
state.pos = pos;
return href;
}
//
// Parse link title
//
// on success it returns a string and updates state.pos;
// on failure it returns null
function parseLinkTitle(state, pos) {
var title, code,
max = state.posMax,
marker = state.src.charCodeAt(pos);
if (marker !== 0x22 /* " */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return null; }
pos++;
title = '';
// if opening marker is "(", switch it to closing marker ")"
if (marker === 0x28) { marker = 0x29; }
while (pos < max) {
code = state.src.charCodeAt(pos);
if (code === marker) {
state.pos = pos + 1;
return title;
}
if (code === 0x5C /* \ */ && pos + 1 < max) {
pos++;
title += state.src[pos++];
continue;
}
title += state.src[pos++];
}
return null;
}
function normalizeReference(str) {
return str.trim().replace(/\s+/g, ' ').toLowerCase();
}
function links(state) { function links(state) {
var labelStart, var labelStart,
@ -205,8 +54,8 @@ function links(state) {
// [link]( <href> "title" ) // [link]( <href> "title" )
// ^^^^^^ parsing link destination // ^^^^^^ parsing link destination
start = pos; start = pos;
href = parseLinkDestination(state, pos); if (parseLinkDestination(state, pos)) {
if (href !== null) { href = state.link_content;
pos = state.pos; pos = state.pos;
} else { } else {
href = ''; href = '';
@ -222,7 +71,8 @@ function links(state) {
// [link]( <href> "title" ) // [link]( <href> "title" )
// ^^^^^^^ parsing link title // ^^^^^^^ parsing link title
if (pos < max && start !== pos && (title = parseLinkTitle(state, pos)) !== null) { if (pos < max && start !== pos && parseLinkTitle(state, pos)) {
title = state.link_content;
pos = state.pos; pos = state.pos;
// [link]( <href> "title" ) // [link]( <href> "title" )
@ -307,7 +157,3 @@ function links(state) {
} }
module.exports = links; module.exports = links;
module.exports.parseLinkLabel = parseLinkLabel;
module.exports.parseLinkDestination = parseLinkDestination;
module.exports.parseLinkTitle = parseLinkTitle;
module.exports.normalizeReference = normalizeReference;

1
lib/rules_inline/state_inline.js

@ -15,6 +15,7 @@ function StateInline(src, parser, options, env) {
this.validateInsideEm = false; this.validateInsideEm = false;
this.validateInsideLink = false; this.validateInsideLink = false;
this.level = 0; this.level = 0;
this.link_content = '';
this.pendingLevel = 0; this.pendingLevel = 0;
} }

Loading…
Cancel
Save