offendersHelpers.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. var OffendersHelpers = function() {
  2. this.domPathToArray = function(str) {
  3. return str.split(/\s?>\s?/);
  4. };
  5. this.listOfDomArraysToTree = function(listOfDomArrays) {
  6. var result = {};
  7. function recursiveTreeBuilder(tree, domArray) {
  8. if (domArray.length > 0) {
  9. var currentDomElement = domArray.shift();
  10. if (tree === null) {
  11. tree = {};
  12. }
  13. tree[currentDomElement] = recursiveTreeBuilder(tree[currentDomElement] || null, domArray);
  14. return tree;
  15. } else if (tree === null) {
  16. return 1;
  17. } else {
  18. return tree + 1;
  19. }
  20. }
  21. listOfDomArrays.forEach(function(domArray) {
  22. result = recursiveTreeBuilder(result, domArray);
  23. });
  24. return result;
  25. };
  26. this.domPathToDomElementObj = function(domPath) {
  27. if (typeof domPath === 'boolean') {
  28. return {
  29. // Not a normal element path
  30. type: 'notAnElement',
  31. element: domPath
  32. };
  33. }
  34. var domArray = this.domPathToArray(domPath);
  35. var domTree = this.listOfDomArraysToTree([this.domPathToArray(domPath)]);
  36. if (domArray[0] === 'html') {
  37. return {
  38. type: 'html'
  39. };
  40. }
  41. if (domArray[0] === 'body') {
  42. if (domArray.length === 1) {
  43. return {
  44. type: 'body'
  45. };
  46. } else {
  47. return {
  48. type: 'domElement',
  49. element: domArray[domArray.length - 1],
  50. tree: domTree
  51. };
  52. }
  53. }
  54. if (domArray[0] === 'head') {
  55. return {
  56. type: 'head'
  57. };
  58. }
  59. if (domArray[0] === '#document') {
  60. return {
  61. type: 'document'
  62. };
  63. }
  64. if (domArray[0] === 'window') {
  65. return {
  66. type: 'window'
  67. };
  68. }
  69. if (domArray[0] === 'DocumentFragment') {
  70. if (domArray.length === 1) {
  71. return {
  72. type: 'fragment'
  73. };
  74. } else {
  75. return {
  76. type: 'fragmentElement',
  77. element: domArray[domArray.length - 1],
  78. tree: domTree
  79. };
  80. }
  81. }
  82. // Not attached element, such as just created with document.createElement()
  83. if (domArray.length === 1) {
  84. return {
  85. type: 'createdElement',
  86. element: domPath
  87. };
  88. } else {
  89. return {
  90. type: 'createdElement',
  91. element: domArray[domArray.length - 1],
  92. tree: domTree
  93. };
  94. }
  95. };
  96. this.backtraceToArray = function(str) {
  97. var traceArray = str.split(/ \/ /);
  98. if (traceArray) {
  99. var results = [];
  100. var parts = null;
  101. var obj;
  102. for (var i=0 ; i<traceArray.length ; i++) {
  103. // Handle the new PhantomJS 2.x syntax
  104. parts = /^(([\w$]+)@)?([^ ]+):(\d+):(\d+)$/.exec(traceArray[i]);
  105. if (parts) {
  106. obj = {
  107. file: parts[3],
  108. line: parseInt(parts[4], 10),
  109. column: parseInt(parts[5], 10)
  110. };
  111. if (parts[2]) {
  112. obj.functionName = parts[2];
  113. }
  114. results.push(obj);
  115. } else {
  116. // Old syntax
  117. parts = /^(([\w$]+) )?\(?([^ ]+):(\d+)\)?$/.exec(traceArray[i]);
  118. if (parts) {
  119. obj = {
  120. file: parts[3],
  121. line: parseInt(parts[4], 10)
  122. };
  123. if (parts[2]) {
  124. obj.functionName = parts[2];
  125. }
  126. results.push(obj);
  127. } else {
  128. return null;
  129. }
  130. }
  131. }
  132. return results;
  133. } else {
  134. return null;
  135. }
  136. };
  137. this.sortVarsLikeChromeDevTools = function(vars) {
  138. return vars.sort(function(a, b) {
  139. return (a < b) ? -1 : 1;
  140. });
  141. };
  142. this.urlToLink = function(url) {
  143. var shortUrl = (url.length > 110) ? url.substr(0, 47) + ' ... ' + url.substr(-48) : url;
  144. return '<a href="' + url + '" target="_blank" title="' + url + '">' + shortUrl + '</a>';
  145. };
  146. this.cssOffenderPattern = function(offender) {
  147. // Used to work with strings
  148. // As of Phantomas v2, offender is now in JSON format.
  149. // Let's just adapt this for now and we'll see later if we remove completely this function
  150. return {
  151. css: offender.value.message,
  152. file: offender.url,
  153. line: offender.value.position.start.line,
  154. column: offender.value.position.start.column
  155. };
  156. };
  157. this.fileWithSizePattern = function(fileWithSize) {
  158. var parts = /^([^ ]*) \((\d+\.\d{2}|NaN) kB\)$/.exec(fileWithSize);
  159. if (!parts) {
  160. return {
  161. file: fileWithSize
  162. };
  163. } else {
  164. return {
  165. file: parts[1],
  166. size: parseFloat(parts[2])
  167. };
  168. }
  169. };
  170. this.orderByFile = function(offenders) {
  171. var byFileObj = {};
  172. offenders.forEach(function(offender) {
  173. var file = offender.file || 'Inline CSS';
  174. delete offender.file;
  175. if (!byFileObj[file]) {
  176. byFileObj[file] = {
  177. url: file,
  178. count: 0,
  179. offenders: []
  180. };
  181. }
  182. byFileObj[file].count ++;
  183. byFileObj[file].offenders.push(offender);
  184. });
  185. // Transform object into array
  186. var byFileArray = [];
  187. for (var file in byFileObj) {
  188. byFileArray.push(byFileObj[file]);
  189. }
  190. return {byFile: byFileArray};
  191. };
  192. };
  193. module.exports = new OffendersHelpers();