utils.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /**
  2. * Module dependencies.
  3. */
  4. var fs = require('fs')
  5. , path = require('path')
  6. , join = path.join
  7. , debug = require('debug')('mocha:watch');
  8. /**
  9. * Ignored directories.
  10. */
  11. var ignore = ['node_modules', '.git'];
  12. /**
  13. * Escape special characters in the given string of html.
  14. *
  15. * @param {String} html
  16. * @return {String}
  17. * @api private
  18. */
  19. exports.escape = function(html){
  20. return String(html)
  21. .replace(/&/g, '&')
  22. .replace(/"/g, '"')
  23. .replace(/</g, '&lt;')
  24. .replace(/>/g, '&gt;');
  25. };
  26. /**
  27. * Array#forEach (<=IE8)
  28. *
  29. * @param {Array} array
  30. * @param {Function} fn
  31. * @param {Object} scope
  32. * @api private
  33. */
  34. exports.forEach = function(arr, fn, scope){
  35. for (var i = 0, l = arr.length; i < l; i++)
  36. fn.call(scope, arr[i], i);
  37. };
  38. /**
  39. * Array#indexOf (<=IE8)
  40. *
  41. * @parma {Array} arr
  42. * @param {Object} obj to find index of
  43. * @param {Number} start
  44. * @api private
  45. */
  46. exports.indexOf = function(arr, obj, start){
  47. for (var i = start || 0, l = arr.length; i < l; i++) {
  48. if (arr[i] === obj)
  49. return i;
  50. }
  51. return -1;
  52. };
  53. /**
  54. * Array#reduce (<=IE8)
  55. *
  56. * @param {Array} array
  57. * @param {Function} fn
  58. * @param {Object} initial value
  59. * @api private
  60. */
  61. exports.reduce = function(arr, fn, val){
  62. var rval = val;
  63. for (var i = 0, l = arr.length; i < l; i++) {
  64. rval = fn(rval, arr[i], i, arr);
  65. }
  66. return rval;
  67. };
  68. /**
  69. * Array#filter (<=IE8)
  70. *
  71. * @param {Array} array
  72. * @param {Function} fn
  73. * @api private
  74. */
  75. exports.filter = function(arr, fn){
  76. var ret = [];
  77. for (var i = 0, l = arr.length; i < l; i++) {
  78. var val = arr[i];
  79. if (fn(val, i, arr)) ret.push(val);
  80. }
  81. return ret;
  82. };
  83. /**
  84. * Object.keys (<=IE8)
  85. *
  86. * @param {Object} obj
  87. * @return {Array} keys
  88. * @api private
  89. */
  90. exports.keys = Object.keys || function(obj) {
  91. var keys = []
  92. , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
  93. for (var key in obj) {
  94. if (has.call(obj, key)) {
  95. keys.push(key);
  96. }
  97. }
  98. return keys;
  99. };
  100. /**
  101. * Watch the given `files` for changes
  102. * and invoke `fn(file)` on modification.
  103. *
  104. * @param {Array} files
  105. * @param {Function} fn
  106. * @api private
  107. */
  108. exports.watch = function(files, fn){
  109. var options = { interval: 100 };
  110. files.forEach(function(file){
  111. debug('file %s', file);
  112. fs.watchFile(file, options, function(curr, prev){
  113. if (prev.mtime < curr.mtime) fn(file);
  114. });
  115. });
  116. };
  117. /**
  118. * Ignored files.
  119. */
  120. function ignored(path){
  121. return !~ignore.indexOf(path);
  122. }
  123. /**
  124. * Lookup files in the given `dir`.
  125. *
  126. * @return {Array}
  127. * @api private
  128. */
  129. exports.files = function(dir, ret){
  130. ret = ret || [];
  131. fs.readdirSync(dir)
  132. .filter(ignored)
  133. .forEach(function(path){
  134. path = join(dir, path);
  135. if (fs.statSync(path).isDirectory()) {
  136. exports.files(path, ret);
  137. } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
  138. ret.push(path);
  139. }
  140. });
  141. return ret;
  142. };
  143. /**
  144. * Compute a slug from the given `str`.
  145. *
  146. * @param {String} str
  147. * @return {String}
  148. * @api private
  149. */
  150. exports.slug = function(str){
  151. return str
  152. .toLowerCase()
  153. .replace(/ +/g, '-')
  154. .replace(/[^-\w]/g, '');
  155. };
  156. /**
  157. * Strip the function definition from `str`,
  158. * and re-indent for pre whitespace.
  159. */
  160. exports.clean = function(str) {
  161. str = str
  162. .replace(/^function *\(.*\) *{/, '')
  163. .replace(/\s+\}$/, '');
  164. var whitespace = str.match(/^\n?(\s*)/)[1]
  165. , re = new RegExp('^' + whitespace, 'gm');
  166. str = str.replace(re, '');
  167. return exports.trim(str);
  168. };
  169. /**
  170. * Escape regular expression characters in `str`.
  171. *
  172. * @param {String} str
  173. * @return {String}
  174. * @api private
  175. */
  176. exports.escapeRegexp = function(str){
  177. return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
  178. };
  179. /**
  180. * Trim the given `str`.
  181. *
  182. * @param {String} str
  183. * @return {String}
  184. * @api private
  185. */
  186. exports.trim = function(str){
  187. return str.replace(/^\s+|\s+$/g, '');
  188. };
  189. /**
  190. * Parse the given `qs`.
  191. *
  192. * @param {String} qs
  193. * @return {Object}
  194. * @api private
  195. */
  196. exports.parseQuery = function(qs){
  197. return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
  198. var i = pair.indexOf('=')
  199. , key = pair.slice(0, i)
  200. , val = pair.slice(++i);
  201. obj[key] = decodeURIComponent(val);
  202. return obj;
  203. }, {});
  204. };
  205. /**
  206. * Highlight the given string of `js`.
  207. *
  208. * @param {String} js
  209. * @return {String}
  210. * @api private
  211. */
  212. function highlight(js) {
  213. return js
  214. .replace(/</g, '&lt;')
  215. .replace(/>/g, '&gt;')
  216. .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
  217. .replace(/('.*?')/gm, '<span class="string">$1</span>')
  218. .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
  219. .replace(/(\d+)/gm, '<span class="number">$1</span>')
  220. .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
  221. .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
  222. }
  223. /**
  224. * Highlight the contents of tag `name`.
  225. *
  226. * @param {String} name
  227. * @api private
  228. */
  229. exports.highlightTags = function(name) {
  230. var code = document.getElementsByTagName(name);
  231. for (var i = 0, len = code.length; i < len; ++i) {
  232. code[i].innerHTML = highlight(code[i].innerHTML);
  233. }
  234. };