showdown/src/helpers.js

314 lines
7.9 KiB
JavaScript
Raw Normal View History

2015-01-16 05:21:33 +08:00
/**
2015-01-19 22:57:43 +08:00
* showdownjs helper functions
2015-01-16 05:21:33 +08:00
*/
2015-01-19 22:57:43 +08:00
if (!showdown.hasOwnProperty('helper')) {
showdown.helper = {};
}
/**
* Check if var is string
2015-03-02 02:15:32 +08:00
* @static
2015-01-19 22:57:43 +08:00
* @param {string} a
* @returns {boolean}
*/
showdown.helper.isString = function isString(a) {
2015-01-19 19:37:21 +08:00
'use strict';
return (typeof a === 'string' || a instanceof String);
2015-01-19 22:57:43 +08:00
};
2015-01-16 05:21:33 +08:00
/**
* Check if var is a function
* @static
* @param {string} a
* @returns {boolean}
*/
showdown.helper.isFunction = function isFunction(a) {
'use strict';
var getType = {};
return a && getType.toString.call(a) === '[object Function]';
};
2015-01-19 22:57:43 +08:00
/**
* ForEach helper function
2015-03-02 02:15:32 +08:00
* @static
2015-01-19 22:57:43 +08:00
* @param {*} obj
* @param {function} callback
*/
showdown.helper.forEach = function forEach(obj, callback) {
2015-01-19 19:37:21 +08:00
'use strict';
if (typeof obj.forEach === 'function') {
obj.forEach(callback);
} else {
2015-03-02 02:15:32 +08:00
for (var i = 0; i < obj.length; i++) {
2015-01-19 19:37:21 +08:00
callback(obj[i], i, obj);
2015-01-16 05:21:33 +08:00
}
2015-01-19 19:37:21 +08:00
}
2015-01-16 05:21:33 +08:00
};
/**
* isArray helper function
2015-03-02 02:15:32 +08:00
* @static
2015-01-16 05:21:33 +08:00
* @param {*} a
* @returns {boolean}
*/
2015-01-19 22:57:43 +08:00
showdown.helper.isArray = function isArray(a) {
'use strict';
return a.constructor === Array;
};
2015-01-16 05:21:33 +08:00
/**
* Check if value is undefined
* @static
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
*/
2015-01-19 22:57:43 +08:00
showdown.helper.isUndefined = function isUndefined(value) {
'use strict';
return typeof value === 'undefined';
};
2015-01-16 05:21:33 +08:00
2015-03-02 02:15:32 +08:00
/**
* Standardidize extension name
* @static
* @param {string} s extension name
* @returns {string}
*/
showdown.helper.stdExtName = function (s) {
'use strict';
return s.replace(/[_-]||\s/g, '').toLowerCase();
};
function escapeCharactersCallback(wholeMatch, m1) {
'use strict';
var charCodeToEscape = m1.charCodeAt(0);
return '~E' + charCodeToEscape + 'E';
}
2015-01-16 05:21:33 +08:00
/**
* Callback used to escape characters when passing through String.replace
2015-03-02 02:15:32 +08:00
* @static
2015-01-16 05:21:33 +08:00
* @param {string} wholeMatch
* @param {string} m1
* @returns {string}
*/
showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
2015-01-16 05:21:33 +08:00
/**
* Escape characters in a string
2015-03-02 02:15:32 +08:00
* @static
2015-01-16 05:21:33 +08:00
* @param {string} text
* @param {string} charsToEscape
* @param {boolean} afterBackslash
* @returns {XML|string|void|*}
*/
2015-01-19 22:57:43 +08:00
showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
'use strict';
// First we have to escape the escape characters so that
// we can build a character class out of them
var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
if (afterBackslash) {
regexString = '\\\\' + regexString;
}
var regex = new RegExp(regexString, 'g');
text = text.replace(regex, escapeCharactersCallback);
return text;
};
var rgxFindMatchPos = function (str, left, right, flags) {
'use strict';
var f = flags || '',
g = f.indexOf('g') > -1,
x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
l = new RegExp(left, f.replace(/g/g, '')),
pos = [],
t, s, m, start, end;
do {
t = 0;
while ((m = x.exec(str))) {
if (l.test(m[0])) {
if (!(t++)) {
s = x.lastIndex;
start = s - m[0].length;
}
} else if (t) {
if (!--t) {
end = m.index + m[0].length;
var obj = {
left: {start: start, end: s},
match: {start: s, end: m.index},
right: {start: m.index, end: end},
wholeMatch: {start: start, end: end}
};
pos.push(obj);
if (!g) {
return pos;
}
}
}
}
} while (t && (x.lastIndex = s));
return pos;
};
/**
* matchRecursiveRegExp
*
* (c) 2007 Steven Levithan <stevenlevithan.com>
* MIT License
*
* Accepts a string to search, a left and right format delimiter
* as regex patterns, and optional regex flags. Returns an array
* of matches, allowing nested instances of left/right delimiters.
* Use the "g" flag to return all matches, otherwise only the
* first is returned. Be careful to ensure that the left and
* right format delimiters produce mutually exclusive matches.
* Backreferences are not supported within the right delimiter
* due to how it is internally combined with the left delimiter.
* When matching strings whose format delimiters are unbalanced
* to the left or right, the output is intentionally as a
* conventional regex library with recursion support would
* produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
* "<" and ">" as the delimiters (both strings contain a single,
* balanced instance of "<x>").
*
* examples:
* matchRecursiveRegExp("test", "\\(", "\\)")
* returns: []
* matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
* returns: ["t<<e>><s>", ""]
* matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
* returns: ["test"]
*/
showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
'use strict';
2016-01-02 09:16:40 +08:00
var matchPos = rgxFindMatchPos (str, left, right, flags),
results = [];
2016-01-02 09:16:40 +08:00
for (var i = 0; i < matchPos.length; ++i) {
results.push([
str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
str.slice(matchPos[i].match.start, matchPos[i].match.end),
str.slice(matchPos[i].left.start, matchPos[i].left.end),
str.slice(matchPos[i].right.start, matchPos[i].right.end)
]);
}
return results;
};
/**
*
* @param {string} str
* @param {string|function} replacement
* @param {string} left
* @param {string} right
* @param {string} flags
* @returns {string}
*/
showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
'use strict';
if (!showdown.helper.isFunction(replacement)) {
var repStr = replacement;
replacement = function () {
return repStr;
};
}
var matchPos = rgxFindMatchPos(str, left, right, flags),
finalStr = str,
lng = matchPos.length;
if (lng > 0) {
var bits = [];
if (matchPos[0].wholeMatch.start !== 0) {
bits.push(str.slice(0, matchPos[0].wholeMatch.start));
}
for (var i = 0; i < lng; ++i) {
bits.push(
replacement(
str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
str.slice(matchPos[i].match.start, matchPos[i].match.end),
str.slice(matchPos[i].left.start, matchPos[i].left.end),
str.slice(matchPos[i].right.start, matchPos[i].right.end)
)
);
if (i < lng - 1) {
bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
}
}
if (matchPos[lng - 1].wholeMatch.end < str.length) {
bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
}
finalStr = bits.join('');
}
return finalStr;
};
/**
* Obfuscate an e-mail address through the use of Character Entities,
* transforming ASCII characters into their equivalent decimal or hex entities.
*
* Since it has a random component, subsequent calls to this function produce different results
*
* @param {string} mail
* @returns {string}
*/
showdown.helper.encodeEmailAddress = function (mail) {
'use strict';
var encode = [
function (ch) {
return '&#' + ch.charCodeAt(0) + ';';
},
function (ch) {
return '&#x' + ch.charCodeAt(0).toString(16) + ';';
},
function (ch) {
return ch;
}
];
mail = mail.replace(/./g, function (ch) {
if (ch === '@') {
// this *must* be encoded. I insist.
ch = encode[Math.floor(Math.random() * 2)](ch);
} else {
var r = Math.random();
// roughly 10% raw, 45% hex, 45% dec
ch = (
r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
);
}
return ch;
});
return mail;
};
/**
* POLYFILLS
*/
// use this instead of builtin is undefined for IE8 compatibility
if (typeof(console) === 'undefined') {
console = {
warn: function (msg) {
'use strict';
alert(msg);
},
log: function (msg) {
'use strict';
alert(msg);
},
error: function (msg) {
'use strict';
throw msg;
}
};
}