UNPKG

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