runtime.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*!
  2. * Jade - runtime
  3. * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Lame Array.isArray() polyfill for now.
  8. */
  9. if (!Array.isArray) {
  10. Array.isArray = function(arr){
  11. return '[object Array]' == Object.prototype.toString.call(arr);
  12. };
  13. }
  14. /**
  15. * Lame Object.keys() polyfill for now.
  16. */
  17. if (!Object.keys) {
  18. Object.keys = function(obj){
  19. var arr = [];
  20. for (var key in obj) {
  21. if (obj.hasOwnProperty(key)) {
  22. arr.push(key);
  23. }
  24. }
  25. return arr;
  26. }
  27. }
  28. /**
  29. * Merge two attribute objects giving precedence
  30. * to values in object `b`. Classes are special-cased
  31. * allowing for arrays and merging/joining appropriately
  32. * resulting in a string.
  33. *
  34. * @param {Object} a
  35. * @param {Object} b
  36. * @return {Object} a
  37. * @api private
  38. */
  39. exports.merge = function merge(a, b) {
  40. var ac = a['class'];
  41. var bc = b['class'];
  42. if (ac || bc) {
  43. ac = ac || [];
  44. bc = bc || [];
  45. if (!Array.isArray(ac)) ac = [ac];
  46. if (!Array.isArray(bc)) bc = [bc];
  47. ac = ac.filter(nulls);
  48. bc = bc.filter(nulls);
  49. a['class'] = ac.concat(bc).join(' ');
  50. }
  51. for (var key in b) {
  52. if (key != 'class') {
  53. a[key] = b[key];
  54. }
  55. }
  56. return a;
  57. };
  58. /**
  59. * Filter null `val`s.
  60. *
  61. * @param {Mixed} val
  62. * @return {Mixed}
  63. * @api private
  64. */
  65. function nulls(val) {
  66. return val != null;
  67. }
  68. /**
  69. * Render the given attributes object.
  70. *
  71. * @param {Object} obj
  72. * @param {Object} escaped
  73. * @return {String}
  74. * @api private
  75. */
  76. exports.attrs = function attrs(obj, escaped){
  77. var buf = []
  78. , terse = obj.terse;
  79. delete obj.terse;
  80. var keys = Object.keys(obj)
  81. , len = keys.length;
  82. if (len) {
  83. buf.push('');
  84. for (var i = 0; i < len; ++i) {
  85. var key = keys[i]
  86. , val = obj[key];
  87. if ('boolean' == typeof val || null == val) {
  88. if (val) {
  89. terse
  90. ? buf.push(key)
  91. : buf.push(key + '="' + key + '"');
  92. }
  93. } else if (0 == key.indexOf('data') && 'string' != typeof val) {
  94. buf.push(key + "='" + JSON.stringify(val) + "'");
  95. } else if ('class' == key && Array.isArray(val)) {
  96. buf.push(key + '="' + exports.escape(val.join(' ')) + '"');
  97. } else if (escaped && escaped[key]) {
  98. buf.push(key + '="' + exports.escape(val) + '"');
  99. } else {
  100. buf.push(key + '="' + val + '"');
  101. }
  102. }
  103. }
  104. return buf.join(' ');
  105. };
  106. /**
  107. * Escape the given string of `html`.
  108. *
  109. * @param {String} html
  110. * @return {String}
  111. * @api private
  112. */
  113. exports.escape = function escape(html){
  114. return String(html)
  115. .replace(/&(?!(\w+|\#\d+);)/g, '&amp;')
  116. .replace(/</g, '&lt;')
  117. .replace(/>/g, '&gt;')
  118. .replace(/"/g, '&quot;');
  119. };
  120. /**
  121. * Re-throw the given `err` in context to the
  122. * the jade in `filename` at the given `lineno`.
  123. *
  124. * @param {Error} err
  125. * @param {String} filename
  126. * @param {String} lineno
  127. * @api private
  128. */
  129. exports.rethrow = function rethrow(err, filename, lineno){
  130. if (!filename) throw err;
  131. var context = 3
  132. , str = require('fs').readFileSync(filename, 'utf8')
  133. , lines = str.split('\n')
  134. , start = Math.max(lineno - context, 0)
  135. , end = Math.min(lines.length, lineno + context);
  136. // Error context
  137. var context = lines.slice(start, end).map(function(line, i){
  138. var curr = i + start + 1;
  139. return (curr == lineno ? ' > ' : ' ')
  140. + curr
  141. + '| '
  142. + line;
  143. }).join('\n');
  144. // Alter exception message
  145. err.path = filename;
  146. err.message = (filename || 'Jade') + ':' + lineno
  147. + '\n' + context + '\n\n' + err.message;
  148. throw err;
  149. };