formatio.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. "use strict";
  2. var samsam = require("@sinonjs/samsam");
  3. var formatio = {
  4. excludeConstructors: ["Object", /^.$/],
  5. quoteStrings: true,
  6. limitChildrenCount: 0
  7. };
  8. var specialObjects = [];
  9. if (typeof global !== "undefined") {
  10. specialObjects.push({ object: global, value: "[object global]" });
  11. }
  12. if (typeof document !== "undefined") {
  13. specialObjects.push({
  14. object: document,
  15. value: "[object HTMLDocument]"
  16. });
  17. }
  18. if (typeof window !== "undefined") {
  19. specialObjects.push({ object: window, value: "[object Window]" });
  20. }
  21. function functionName(func) {
  22. if (!func) { return ""; }
  23. if (func.displayName) { return func.displayName; }
  24. if (func.name) { return func.name; }
  25. var matches = func.toString().match(/function\s+([^\(]+)/m);
  26. return (matches && matches[1]) || "";
  27. }
  28. function constructorName(f, object) {
  29. var name = functionName(object && object.constructor);
  30. var excludes = f.excludeConstructors ||
  31. formatio.excludeConstructors || [];
  32. var i, l;
  33. for (i = 0, l = excludes.length; i < l; ++i) {
  34. if (typeof excludes[i] === "string" && excludes[i] === name) {
  35. return "";
  36. } else if (excludes[i].test && excludes[i].test(name)) {
  37. return "";
  38. }
  39. }
  40. return name;
  41. }
  42. function isCircular(object, objects) {
  43. if (typeof object !== "object") { return false; }
  44. var i, l;
  45. for (i = 0, l = objects.length; i < l; ++i) {
  46. if (objects[i] === object) { return true; }
  47. }
  48. return false;
  49. }
  50. function ascii(f, object, processed, indent) {
  51. if (typeof object === "string") {
  52. if (object.length === 0) { return "(empty string)"; }
  53. var qs = f.quoteStrings;
  54. var quote = typeof qs !== "boolean" || qs;
  55. return processed || quote ? "\"" + object + "\"" : object;
  56. }
  57. if (typeof object === "function" && !(object instanceof RegExp)) {
  58. return ascii.func(object);
  59. }
  60. processed = processed || [];
  61. if (isCircular(object, processed)) { return "[Circular]"; }
  62. if (Object.prototype.toString.call(object) === "[object Array]") {
  63. return ascii.array.call(f, object, processed);
  64. }
  65. if (!object) { return String((1 / object) === -Infinity ? "-0" : object); }
  66. if (samsam.isElement(object)) { return ascii.element(object); }
  67. if (typeof object.toString === "function" &&
  68. object.toString !== Object.prototype.toString) {
  69. return object.toString();
  70. }
  71. var i, l;
  72. for (i = 0, l = specialObjects.length; i < l; i++) {
  73. if (object === specialObjects[i].object) {
  74. return specialObjects[i].value;
  75. }
  76. }
  77. if (typeof Set !== "undefined" && object instanceof Set) {
  78. return ascii.set.call(f, object, processed);
  79. }
  80. return ascii.object.call(f, object, processed, indent);
  81. }
  82. ascii.func = function (func) {
  83. return "function " + functionName(func) + "() {}";
  84. };
  85. function delimit(str, delimiters) {
  86. delimiters = delimiters || ["[", "]"];
  87. return delimiters[0] + str + delimiters[1];
  88. }
  89. ascii.array = function (array, processed, delimiters) {
  90. processed = processed || [];
  91. processed.push(array);
  92. var pieces = [];
  93. var i, l;
  94. l = (this.limitChildrenCount > 0) ?
  95. Math.min(this.limitChildrenCount, array.length) : array.length;
  96. for (i = 0; i < l; ++i) {
  97. pieces.push(ascii(this, array[i], processed));
  98. }
  99. if (l < array.length) {
  100. pieces.push("[... " + (array.length - l) + " more elements]");
  101. }
  102. return delimit(pieces.join(", "), delimiters);
  103. };
  104. ascii.set = function (set, processed) {
  105. return ascii.array.call(this, Array.from(set), processed, ["Set {", "}"]);
  106. };
  107. ascii.object = function (object, processed, indent) {
  108. processed = processed || [];
  109. processed.push(object);
  110. indent = indent || 0;
  111. var pieces = [];
  112. var properties = Object.keys(object).sort();
  113. var length = 3;
  114. var prop, str, obj, i, k, l;
  115. l = (this.limitChildrenCount > 0) ?
  116. Math.min(this.limitChildrenCount, properties.length) : properties.length;
  117. for (i = 0; i < l; ++i) {
  118. prop = properties[i];
  119. obj = object[prop];
  120. if (isCircular(obj, processed)) {
  121. str = "[Circular]";
  122. } else {
  123. str = ascii(this, obj, processed, indent + 2);
  124. }
  125. str = (/\s/.test(prop) ? "\"" + prop + "\"" : prop) + ": " + str;
  126. length += str.length;
  127. pieces.push(str);
  128. }
  129. var cons = constructorName(this, object);
  130. var prefix = cons ? "[" + cons + "] " : "";
  131. var is = "";
  132. for (i = 0, k = indent; i < k; ++i) { is += " "; }
  133. if (l < properties.length)
  134. {pieces.push("[... " + (properties.length - l) + " more elements]");}
  135. if (length + indent > 80) {
  136. return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" +
  137. is + "}";
  138. }
  139. return prefix + "{ " + pieces.join(", ") + " }";
  140. };
  141. ascii.element = function (element) {
  142. var tagName = element.tagName.toLowerCase();
  143. var attrs = element.attributes;
  144. var pairs = [];
  145. var attr, attrName, i, l, val;
  146. for (i = 0, l = attrs.length; i < l; ++i) {
  147. attr = attrs.item(i);
  148. attrName = attr.nodeName.toLowerCase().replace("html:", "");
  149. val = attr.nodeValue;
  150. if (attrName !== "contenteditable" || val !== "inherit") {
  151. if (val) { pairs.push(attrName + "=\"" + val + "\""); }
  152. }
  153. }
  154. var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
  155. // SVG elements have undefined innerHTML
  156. var content = element.innerHTML || "";
  157. if (content.length > 20) {
  158. content = content.substr(0, 20) + "[...]";
  159. }
  160. var res = formatted + pairs.join(" ") + ">" + content +
  161. "</" + tagName + ">";
  162. return res.replace(/ contentEditable="inherit"/, "");
  163. };
  164. function Formatio(options) {
  165. // eslint-disable-next-line guard-for-in
  166. for (var opt in options) {
  167. this[opt] = options[opt];
  168. }
  169. }
  170. Formatio.prototype = {
  171. functionName: functionName,
  172. configure: function (options) {
  173. return new Formatio(options);
  174. },
  175. constructorName: function (object) {
  176. return constructorName(this, object);
  177. },
  178. ascii: function (object, processed, indent) {
  179. return ascii(this, object, processed, indent);
  180. }
  181. };
  182. module.exports = Formatio.prototype;