helpers.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * showdownjs helper functions
  3. */
  4. if (!showdown.hasOwnProperty('helper')) {
  5. showdown.helper = {};
  6. }
  7. /**
  8. * Check if var is string
  9. * @static
  10. * @param {string} a
  11. * @returns {boolean}
  12. */
  13. showdown.helper.isString = function isString(a) {
  14. 'use strict';
  15. return (typeof a === 'string' || a instanceof String);
  16. };
  17. /**
  18. * Check if var is a function
  19. * @static
  20. * @param {string} a
  21. * @returns {boolean}
  22. */
  23. showdown.helper.isFunction = function isFunction(a) {
  24. 'use strict';
  25. var getType = {};
  26. return a && getType.toString.call(a) === '[object Function]';
  27. };
  28. /**
  29. * ForEach helper function
  30. * @static
  31. * @param {*} obj
  32. * @param {function} callback
  33. */
  34. showdown.helper.forEach = function forEach(obj, callback) {
  35. 'use strict';
  36. if (typeof obj.forEach === 'function') {
  37. obj.forEach(callback);
  38. } else {
  39. for (var i = 0; i < obj.length; i++) {
  40. callback(obj[i], i, obj);
  41. }
  42. }
  43. };
  44. /**
  45. * isArray helper function
  46. * @static
  47. * @param {*} a
  48. * @returns {boolean}
  49. */
  50. showdown.helper.isArray = function isArray(a) {
  51. 'use strict';
  52. return a.constructor === Array;
  53. };
  54. /**
  55. * Check if value is undefined
  56. * @static
  57. * @param {*} value The value to check.
  58. * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
  59. */
  60. showdown.helper.isUndefined = function isUndefined(value) {
  61. 'use strict';
  62. return typeof value === 'undefined';
  63. };
  64. /**
  65. * Standardidize extension name
  66. * @static
  67. * @param {string} s extension name
  68. * @returns {string}
  69. */
  70. showdown.helper.stdExtName = function (s) {
  71. 'use strict';
  72. return s.replace(/[_-]||\s/g, '').toLowerCase();
  73. };
  74. function escapeCharactersCallback(wholeMatch, m1) {
  75. 'use strict';
  76. var charCodeToEscape = m1.charCodeAt(0);
  77. return '~E' + charCodeToEscape + 'E';
  78. }
  79. /**
  80. * Callback used to escape characters when passing through String.replace
  81. * @static
  82. * @param {string} wholeMatch
  83. * @param {string} m1
  84. * @returns {string}
  85. */
  86. showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
  87. /**
  88. * Escape characters in a string
  89. * @static
  90. * @param {string} text
  91. * @param {string} charsToEscape
  92. * @param {boolean} afterBackslash
  93. * @returns {XML|string|void|*}
  94. */
  95. showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
  96. 'use strict';
  97. // First we have to escape the escape characters so that
  98. // we can build a character class out of them
  99. var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
  100. if (afterBackslash) {
  101. regexString = '\\\\' + regexString;
  102. }
  103. var regex = new RegExp(regexString, 'g');
  104. text = text.replace(regex, escapeCharactersCallback);
  105. return text;
  106. };
  107. var rgxFindMatchPos = function (str, left, right, flags) {
  108. 'use strict';
  109. var f = flags || '',
  110. g = f.indexOf('g') > -1,
  111. x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
  112. l = new RegExp(left, f.replace(/g/g, '')),
  113. pos = [],
  114. t, s, m, start, end;
  115. do {
  116. t = 0;
  117. while ((m = x.exec(str))) {
  118. if (l.test(m[0])) {
  119. if (!(t++)) {
  120. s = x.lastIndex;
  121. start = s - m[0].length;
  122. }
  123. } else if (t) {
  124. if (!--t) {
  125. end = m.index + m[0].length;
  126. var obj = {
  127. left: {start: start, end: s},
  128. match: {start: s, end: m.index},
  129. right: {start: m.index, end: end},
  130. wholeMatch: {start: start, end: end}
  131. };
  132. pos.push(obj);
  133. if (!g) {
  134. return pos;
  135. }
  136. }
  137. }
  138. }
  139. } while (t && (x.lastIndex = s));
  140. return pos;
  141. };
  142. /**
  143. * matchRecursiveRegExp
  144. *
  145. * (c) 2007 Steven Levithan <stevenlevithan.com>
  146. * MIT License
  147. *
  148. * Accepts a string to search, a left and right format delimiter
  149. * as regex patterns, and optional regex flags. Returns an array
  150. * of matches, allowing nested instances of left/right delimiters.
  151. * Use the "g" flag to return all matches, otherwise only the
  152. * first is returned. Be careful to ensure that the left and
  153. * right format delimiters produce mutually exclusive matches.
  154. * Backreferences are not supported within the right delimiter
  155. * due to how it is internally combined with the left delimiter.
  156. * When matching strings whose format delimiters are unbalanced
  157. * to the left or right, the output is intentionally as a
  158. * conventional regex library with recursion support would
  159. * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
  160. * "<" and ">" as the delimiters (both strings contain a single,
  161. * balanced instance of "<x>").
  162. *
  163. * examples:
  164. * matchRecursiveRegExp("test", "\\(", "\\)")
  165. * returns: []
  166. * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
  167. * returns: ["t<<e>><s>", ""]
  168. * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
  169. * returns: ["test"]
  170. */
  171. showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
  172. 'use strict';
  173. var matchPos = rgxFindMatchPos (str, left, right, flags),
  174. results = [];
  175. for (var i = 0; i < matchPos.length; ++i) {
  176. results.push([
  177. str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
  178. str.slice(matchPos[i].match.start, matchPos[i].match.end),
  179. str.slice(matchPos[i].left.start, matchPos[i].left.end),
  180. str.slice(matchPos[i].right.start, matchPos[i].right.end)
  181. ]);
  182. }
  183. return results;
  184. };
  185. /**
  186. *
  187. * @param {string} str
  188. * @param {string|function} replacement
  189. * @param {string} left
  190. * @param {string} right
  191. * @param {string} flags
  192. * @returns {string}
  193. */
  194. showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
  195. 'use strict';
  196. if (!showdown.helper.isFunction(replacement)) {
  197. var repStr = replacement;
  198. replacement = function () {
  199. return repStr;
  200. };
  201. }
  202. var matchPos = rgxFindMatchPos(str, left, right, flags),
  203. finalStr = str,
  204. lng = matchPos.length;
  205. if (lng > 0) {
  206. var bits = [];
  207. if (matchPos[0].wholeMatch.start !== 0) {
  208. bits.push(str.slice(0, matchPos[0].wholeMatch.start));
  209. }
  210. for (var i = 0; i < lng; ++i) {
  211. bits.push(
  212. replacement(
  213. str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
  214. str.slice(matchPos[i].match.start, matchPos[i].match.end),
  215. str.slice(matchPos[i].left.start, matchPos[i].left.end),
  216. str.slice(matchPos[i].right.start, matchPos[i].right.end)
  217. )
  218. );
  219. if (i < lng - 1) {
  220. bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
  221. }
  222. }
  223. if (matchPos[lng - 1].wholeMatch.end < str.length) {
  224. bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
  225. }
  226. finalStr = bits.join('');
  227. }
  228. return finalStr;
  229. };
  230. /**
  231. * POLYFILLS
  232. */
  233. // use this instead of builtin is undefined for IE8 compatibility
  234. if (typeof(console) === 'undefined') {
  235. console = {
  236. warn: function (msg) {
  237. 'use strict';
  238. alert(msg);
  239. },
  240. log: function (msg) {
  241. 'use strict';
  242. alert(msg);
  243. },
  244. error: function (msg) {
  245. 'use strict';
  246. throw msg;
  247. }
  248. };
  249. }