1 |
|
2 |
|
3 | !function(exports, _setTimeout, _clearTimeout, _Date, _Error, _Infinity) {
|
4 | var started, testSuite, timerType, inSuite
|
5 | , tests = []
|
6 | , describe = exports.describe = curry(def, 1)
|
7 | , _global = describe.global = exports.window || global
|
8 | , _process = _global.process || { exit: This }
|
9 | , _isArray = Array.isArray
|
10 | , _keys = Object.keys
|
11 | , call = def.bind.bind(def.call)
|
12 | , slice = call(tests.slice)
|
13 | , push = call(tests.push)
|
14 | , lineRe = /{(\w+)}/g
|
15 | , totalCases = 0
|
16 | , failedCases = []
|
17 | , totalAsserts = 0
|
18 | , passedAsserts = 0
|
19 | , skipped = 0
|
20 | , runPos = 0
|
21 | , splicePos = 0
|
22 | , assert = describe.assert = {
|
23 | notOk: function(value, message) {
|
24 | return this(!value, message, value, "!=", "falsy")
|
25 | },
|
26 | equal: function(actual, expected, message) {
|
27 | return this(
|
28 | arguments.length > 1 && _deepEqual(actual, expected, []),
|
29 | message, actual, "equal", expected
|
30 | )
|
31 | },
|
32 | notEqual: function(actual, expected, message) {
|
33 | return this(
|
34 | arguments.length > 1 && !_deepEqual(actual, expected, []),
|
35 | message, actual, "notEqual", expected
|
36 | )
|
37 | },
|
38 | skip: This,
|
39 | strictEqual: function(actual, expected, message) {
|
40 | return this(
|
41 | arguments.length > 1 && actual === expected,
|
42 | message, actual, "===", expected
|
43 | )
|
44 | },
|
45 | notStrictEqual: function(actual, expected, message) {
|
46 | return this(
|
47 | arguments.length > 1 && actual !== expected,
|
48 | message, actual, "!==", expected
|
49 | )
|
50 | },
|
51 | own: function(actual, expected, message) {
|
52 | own.lastMsg = ""
|
53 | return this(own(actual, expected), message || own.lastMsg)
|
54 | },
|
55 | notOwn: function(actual, expected, message) {
|
56 | own.lastMsg = ""
|
57 | return this(!own(actual, expected), message || own.lastMsg)
|
58 | },
|
59 | throws: function(fn, message) {
|
60 | var actual = false
|
61 | try {
|
62 | fn()
|
63 | } catch(e) {
|
64 | actual = true
|
65 | }
|
66 | return this(actual, message || "throws")
|
67 | },
|
68 | type: function(thing, expected) {
|
69 | var actual = type(thing)
|
70 | return this(actual === expected, 0, actual, "type", expected)
|
71 | },
|
72 | anyOf: function(a, b) {
|
73 | return this(
|
74 | _isArray(b) && b.indexOf(a) > -1,
|
75 | "should be one from " + stringify(b) + ", got " + a
|
76 | )
|
77 | }
|
78 | }
|
79 | , argv = _process.argv && _process.argv.slice(2) || []
|
80 | , conf = describe.conf = opts(argv, {
|
81 |
|
82 | file: (_Error().stack + " /cli/test.js:").match(/\S+?:(?=[:\d)]*$)/m)[0],
|
83 | global: "describe,it",
|
84 | head: "",
|
85 | indent: " ",
|
86 | suite: "{indent}{n}",
|
87 | ok: "{indent} {green}✔{reset} {i}. {n} [{passed}/{total}]",
|
88 | nok: "{indent} {red}✘{reset} {i}. {n} [{passed}/{total}]",
|
89 | skip: "{indent} {yellow}∅{reset} {i}. {n}",
|
90 | sum: "1..{total}\n#{passGreen} pass {pass}/{total} [{passAsserts}/{totalAsserts}]{timeStr}",
|
91 | failSum: "#{red}{bold} FAIL tests {failNums}",
|
92 | skipSum: "#{yellow}{bold} skip {s}",
|
93 | bold: "\x1b[1m",
|
94 | red: "\x1b[31m",
|
95 | green: "\x1b[32m",
|
96 | yellow: "\x1b[33m",
|
97 | reset: "\x1b[0m",
|
98 | color: (_process.stdout || _process).isTTY,
|
99 | cut: 1500,
|
100 | delay: 1,
|
101 | seed: (Math.random() * 1e5)|0,
|
102 | stack: 9,
|
103 | status: 1,
|
104 | time: 1,
|
105 | timeout: 999,
|
106 | total: 0
|
107 | })
|
108 | , toStr = conf.toString
|
109 | , hasOwn = call(conf.hasOwnProperty)
|
110 |
|
111 | , fakeNow
|
112 | , timers = []
|
113 | , timerId = 0
|
114 | , fakeTimers = {
|
115 | setTimeout: curry(fakeTimeout, false),
|
116 | setInterval: curry(fakeTimeout, true),
|
117 | clearTimeout: fakeClear,
|
118 | clearInterval: fakeClear,
|
119 | setImmediate: fakeNextTick,
|
120 | clearImmediate: fakeClear,
|
121 | Date: fakeDate
|
122 | }
|
123 | function fakeDate(year, month, date, hr, min, sec, ms) {
|
124 | return (
|
125 | arguments.length > 1 ?
|
126 | new _Date(num(year), num(month), num(date, 1), num(hr), num(min), num(sec), num(ms)) :
|
127 | new _Date(num(year, fakeNow))
|
128 | )
|
129 | }
|
130 | fakeDate.now = function() {
|
131 | return fakeNow
|
132 | }
|
133 | fakeDate.parse = _Date.parse
|
134 | fakeDate.UTC = _Date.UTC
|
135 | function fakeHrtime(time) {
|
136 | var diff = _isArray(time) ? fakeNow - (time[0] * 1e3 + time[1] / 1e6) : fakeNow
|
137 | return [Math.floor(diff / 1000), Math.round((diff % 1e3) * 1e3) * 1e3]
|
138 | }
|
139 | function fakeTimeout(repeat, fn, ms) {
|
140 | if (Date === _Date) {
|
141 | return _setTimeout.apply(this, slice(arguments, 1))
|
142 | }
|
143 | if (!isObj(repeat)) {
|
144 | repeat = {
|
145 | id: ++timerId,
|
146 | repeat: repeat,
|
147 | fn: fn,
|
148 | args: slice(arguments, 3),
|
149 | at: fakeNow + ms,
|
150 | ms: ms
|
151 | }
|
152 | }
|
153 | for (var i = timers.length; i-- && !(timers[i].at <= repeat.at););
|
154 | timers.splice(i + 1, 0, repeat)
|
155 | return timerType == "number" ? repeat.id : {
|
156 | id: repeat.id,
|
157 | unref: This
|
158 | }
|
159 | }
|
160 | function fakeNextTick(fn) {
|
161 | fakeTimeout({
|
162 | id: ++timerId,
|
163 | fn: fn,
|
164 | args: slice(arguments, 1),
|
165 | at: fakeNow - 1
|
166 | })
|
167 | }
|
168 | function fakeClear(id) {
|
169 | if (id) for (var i = timers.length; i--; ) {
|
170 | if (timers[i].id === id || timers[i].id === id.id) {
|
171 | timers.splice(i, 1)
|
172 | break
|
173 | }
|
174 | }
|
175 | }
|
176 |
|
177 |
|
178 | describe.describe = describe
|
179 | describe.test = curry(def, 2)
|
180 | describe.it = curry(def, 3)
|
181 | describe.should = curry(def, 4)
|
182 | describe.failed = 0
|
183 | describe.output = ""
|
184 |
|
185 | describe.format = format
|
186 | describe.opts = opts
|
187 | describe.print = print
|
188 | describe.stringify = stringify
|
189 |
|
190 | each(conf.global, function(_i, value) {
|
191 | _global[value] = describe[value]
|
192 | })
|
193 |
|
194 | function def(t, name, data, fn) {
|
195 | if (t < 1) t = isFn(data) + 1
|
196 | if (!started) {
|
197 | started = new _Date()
|
198 |
|
199 | if (!conf.color) {
|
200 | conf.bold = conf.red = conf.green = conf.yellow = conf.reset = ""
|
201 | }
|
202 |
|
203 | if (conf.tap) {
|
204 | conf.head = "TAP version 13"
|
205 | conf.suite = "# {n}"
|
206 | conf.ok = conf.skip = "ok {i} - {n} [{passed}/{total}]"
|
207 | conf.nok = "not " + conf.ok
|
208 | conf.indent = ""
|
209 | } else if (conf.brief) {
|
210 | conf.suite = conf.ok = conf.indent = ""
|
211 | conf.skip = "{yellow}skip {i} - {n}"
|
212 | conf.sum = conf.sum.slice(11)
|
213 | }
|
214 |
|
215 | if (t !== 1) def(1, "Tests")
|
216 | line("head")
|
217 | timerType = type(_setTimeout(nextCase, conf.delay|0))
|
218 | }
|
219 | if (!isStr(name)) {
|
220 | fn = data
|
221 | data = name
|
222 | name = "Unnamed Test" + (t > 1 ? "Case" : "Suite")
|
223 | }
|
224 | if (!isFn(fn)) {
|
225 | fn = data
|
226 | }
|
227 | var spliceData = [++splicePos, 0, {
|
228 | p: inSuite,
|
229 | indent: inSuite ? inSuite.indent + (t > 1 ? "" : conf.indent) : "",
|
230 | s: t > 1 && !isFn(fn) ? "pending" : data === false ? "by data" : 0,
|
231 | t: t,
|
232 | n: name,
|
233 | f: fn
|
234 | }]
|
235 | if (data !== fn) {
|
236 | each(data, curry(function(item, i, row) {
|
237 | i = spliceData[i - 0 + 2] = Object.create(item)
|
238 | i.f = curry(i.f, i.r = _isArray(row) ? row : isObj(row) ? Object.entries(row) : (row = [row]))
|
239 | i.n = format(i.n, row, conf)
|
240 | if (item.f.length > i.r.length + 2 || i.r.length !== spliceData[2].r.length) throw "Invalid data for: " + i.n
|
241 | }, spliceData[2]))
|
242 | splicePos += data.length - 1
|
243 | }
|
244 | tests.splice.apply(tests, spliceData)
|
245 | return describe
|
246 | }
|
247 |
|
248 | function nextCase() {
|
249 | var tick
|
250 | , args = tests[splicePos = runPos++]
|
251 | if (!args) printResult()
|
252 | else if (args.t === 1) nextSuite(args)
|
253 | else {
|
254 | testCase.i = ++totalCases
|
255 | if (args.p && args.p !== testSuite) testSuite = args.p
|
256 | testCase.indent = testSuite.indent
|
257 | testCase.n = (args.t < 3 ? "" : "it " + (args.t < 4 ? "" : "should ")) + args.n
|
258 | testCase.errors = []
|
259 | testCase.total = testCase.passed = 0
|
260 | if (args.s || testSuite.s || argv.length && argv.indexOf("" + totalCases) < 0) {
|
261 | skipped++
|
262 | if (!argv.length) line("skip", testCase)
|
263 | return nextCase()
|
264 | }
|
265 | Object.assign(testCase, assert)
|
266 | testCase.end = end
|
267 | testCase.ok = testCase
|
268 | testCase.plan = function(planned) {
|
269 | testCase.planned = planned
|
270 | if (planned <= testCase.total) end()
|
271 | return testCase
|
272 | }
|
273 | testCase.setTimeout = function(ms) {
|
274 | _clearTimeout(tick)
|
275 | tick = _setTimeout(end, ms, "TIMEOUT: " + ms + "ms")
|
276 | return testCase
|
277 | }
|
278 |
|
279 | try {
|
280 | testCase.setTimeout(conf.timeout)
|
281 | args = args.f.call(testCase, testCase, (testCase.mock = args.f.length > 1 && new Mock()))
|
282 | if (args && args.then) args.then(curry(end, null), end)
|
283 | } catch (e) {
|
284 | print("" + e)
|
285 | end(e)
|
286 | }
|
287 | }
|
288 | function testCase(value, message, actual, op, expected) {
|
289 | testCase.total++
|
290 | if (testCase.ended) {
|
291 | fail("assertion after end")
|
292 | }
|
293 | if (value) {
|
294 | testCase.passed++
|
295 | } else {
|
296 | fail("Assertion:" + testCase.total + ": " + (message || (
|
297 | op ? op +
|
298 | "\nexpected: " + stringify(expected) +
|
299 | "\nactual: " + stringify(actual)
|
300 | : stringify(value) + " is truthy"
|
301 | )))
|
302 | }
|
303 | return testCase.plan(testCase.planned)
|
304 | }
|
305 | function fail(_err) {
|
306 | var row, start, i = 0
|
307 | , err = type(_err) != "error" ? _Error(_err) : _err
|
308 | , stack = err.stack
|
309 | if (stack) {
|
310 |
|
311 | for (stack = _isArray(stack) ? stack : (stack + "").replace(err, "").split("\n"); (row = stack[++i]); ) {
|
312 | if (row.indexOf(conf.file) < 0) {
|
313 | if (!start) start = i
|
314 | }
|
315 | if (i - start >= conf.stack) break
|
316 | }
|
317 | err = [ err ].concat(stack.slice(start, i)).join("\n")
|
318 | }
|
319 |
|
320 | if (push(testCase.errors, err) == 1) {
|
321 | push(failedCases, testCase)
|
322 | }
|
323 | if (describe.result) printResult()
|
324 | return testCase
|
325 | }
|
326 | function end(err) {
|
327 | _clearTimeout(tick)
|
328 | if (err) fail(err)
|
329 | if (testCase.ended) return fail("ended multiple times")
|
330 | testCase.ended = _Date.now()
|
331 |
|
332 | if (testCase.planned != void 0 && testCase.planned !== testCase.total) {
|
333 | fail("planned " + testCase.planned + " actual " + testCase.total)
|
334 | }
|
335 | if (testCase.mock) {
|
336 | testCase.n += testCase.mock.txt
|
337 | testCase.mock.restore()
|
338 | }
|
339 |
|
340 | totalAsserts += testCase.total
|
341 | passedAsserts += testCase.passed
|
342 |
|
343 | line(testCase.errors.length ? "nok" : "ok", testCase)
|
344 | if (runPos % 1000) nextCase()
|
345 | else _setTimeout(nextCase, 1)
|
346 | }
|
347 | }
|
348 | function nextSuite(newSuite) {
|
349 | if (!argv.length) line("suite", newSuite)
|
350 | newSuite.p = inSuite
|
351 | inSuite = testSuite = newSuite
|
352 | if (isFn(testSuite.f)) {
|
353 | testSuite.f.call(describe)
|
354 | } else if (isObj(testSuite.f)) {
|
355 | each(testSuite.f, curry(def, 0))
|
356 | }
|
357 | inSuite = newSuite.p
|
358 | nextCase()
|
359 | }
|
360 | function printResult() {
|
361 | testSuite = null
|
362 | conf.total = totalCases
|
363 | var testCase
|
364 | , nums = []
|
365 | , failed = failedCases.length
|
366 | conf.fail = describe.failed += failed
|
367 | conf.pass = totalCases - conf.fail
|
368 | conf.s = skipped
|
369 | conf.passAsserts = passedAsserts
|
370 | conf.totalAsserts = totalAsserts
|
371 | conf.passGreen = conf.fail ? "" : conf.green + conf.bold
|
372 | conf.failRed = conf.fail ? conf.red : ""
|
373 | conf.timeStr = conf.time ? " in " + (_Date.now() - started) + " ms at " + started.toTimeString().slice(0, 8) : ""
|
374 | if (conf.status) _process.exitCode = conf.fail
|
375 | if (failed) {
|
376 | for (; (testCase = failedCases[--failed]); ) {
|
377 | nums[failed] = testCase.i
|
378 | print("---")
|
379 | line("nok", testCase)
|
380 | print(testCase.errors.join("\n"))
|
381 | }
|
382 | conf.failNums = nums.join(", ")
|
383 | print("...")
|
384 | line("failSum", conf)
|
385 | failedCases.length = 0
|
386 | }
|
387 | describe.result = line("sum", conf)
|
388 | if (skipped) {
|
389 | line("skipSum", conf)
|
390 | }
|
391 | if (describe.onend) describe.onend()
|
392 | }
|
393 |
|
394 | function This() {
|
395 | return this
|
396 | }
|
397 | function format(str, map, fallback) {
|
398 | return str.replace(lineRe, function(_, field) {
|
399 | return map[field] != null ? map[field] : fallback[field]
|
400 | })
|
401 | }
|
402 | function line(name, map) {
|
403 | return print(format(conf[name], map, conf))
|
404 | }
|
405 | function opts(argv, defaults) {
|
406 | for (var arg, conf = Object.assign({}, defaults), i = argv.length; i; ) {
|
407 | arg = argv[--i].split(/=|--(no-)?/)
|
408 | if (arg[0] === "") {
|
409 | conf[arg[2]] = arg[4] || !arg[1]
|
410 | argv.splice(i, 1)
|
411 | }
|
412 | }
|
413 | return conf
|
414 | }
|
415 | function print(str) {
|
416 | if (!str) return
|
417 | if (testSuite && testSuite.indent) {
|
418 | str = str.split("\n").join("\n" + testSuite.indent)
|
419 | }
|
420 | describe.output += str + "\n"
|
421 | if (describe.onprint) describe.onprint(str)
|
422 | if (_global.console && console.log) console.log(str + conf.reset)
|
423 | return str
|
424 | }
|
425 |
|
426 |
|
427 |
|
428 | function Mock() {
|
429 | this.txt = ""
|
430 | this._r = []
|
431 | }
|
432 | Mock.prototype = describe.mock = {
|
433 | fn: function(origin) {
|
434 | spy.called = 0
|
435 | spy.calls = []
|
436 | spy.errors = 0
|
437 | spy.results = []
|
438 | return spy
|
439 | function spy() {
|
440 | var err, key, result
|
441 | , args = slice(arguments)
|
442 | if (isFn(origin)) {
|
443 | try {
|
444 | result = origin.apply(this, arguments)
|
445 | } catch(e) {
|
446 | spy.errors++
|
447 | err = e
|
448 | }
|
449 | } else if (_isArray(origin)) {
|
450 | result = origin[spy.called % origin.length]
|
451 | } else if (isObj(origin)) {
|
452 | key = JSON.stringify(args).slice(1, -1)
|
453 | result = hasOwn(origin, key) ? origin[key] : origin["*"]
|
454 | } else result = origin
|
455 | spy.called++
|
456 | push(spy.results, result)
|
457 | push(spy.calls, {
|
458 | scope: this,
|
459 | args: args,
|
460 | error: err,
|
461 | result: result
|
462 | })
|
463 | return result
|
464 | }
|
465 | },
|
466 | rand: function(seed_) {
|
467 | var seed = seed_ || conf.seed
|
468 | this.txt += " #seed:" + seed
|
469 | this.swap(Math, "random", xorshift128(seed))
|
470 | },
|
471 | spy: function(obj, name, stub) {
|
472 | this.swap(obj, name, this.fn(stub || obj[name]))
|
473 | },
|
474 | swap: function swap(obj, name, fn) {
|
475 | if (isObj(name)) {
|
476 | each(name, curry(swap, obj, this))
|
477 | return
|
478 | }
|
479 | var existing = obj[name]
|
480 | push(this._r, obj, name, hasOwn(obj, name) && existing)
|
481 | obj[name] = fn
|
482 | if (fn === fn && obj[name] !== fn) throw _Error("Unable to swap " + stringify(name))
|
483 | return existing
|
484 | },
|
485 | restore: function() {
|
486 | for (var arr = this._r, i = arr.length; --i > 0; i -= 2) {
|
487 | if (arr[i]) {
|
488 | arr[i - 2][arr[i - 1]] = arr[i]
|
489 | } else {
|
490 | delete arr[i - 2][arr[i - 1]]
|
491 | }
|
492 | }
|
493 |
|
494 | this.tick(_Infinity, true)
|
495 | },
|
496 | time: function(newTime, newZone) {
|
497 | var mock = this
|
498 | if (!mock._time) {
|
499 | mock._time = fakeNow = _Date.now()
|
500 | mock.swap(_global, fakeTimers)
|
501 | mock.swap(_process, { nextTick: fakeNextTick, hrtime: fakeHrtime })
|
502 | }
|
503 | if (newTime) {
|
504 | fakeNow = isStr(newTime) ? _Date.parse(newTime) : newTime
|
505 | mock.tick(0)
|
506 | }
|
507 | fakeDate._z = newZone
|
508 | },
|
509 | tick: function(amount, noRepeat) {
|
510 | var t
|
511 | , nextNow = type(amount) === "number" ? fakeNow + amount : timers[0] ? timers[0].at : fakeNow
|
512 |
|
513 | for (; (t = timers[0]) && t.at <= nextNow; ) {
|
514 | fakeNow = t.at
|
515 | timers.shift()
|
516 | if (isStr(t.fn)) t.fn = Function(t.fn)
|
517 | if (isFn(t.fn)) t.fn.apply(null, t.args)
|
518 | if (!noRepeat && t.repeat) {
|
519 | t.at += t.ms
|
520 | fakeTimeout(t)
|
521 | }
|
522 | }
|
523 | fakeNow = nextNow
|
524 |
|
525 | }
|
526 | }
|
527 |
|
528 | function xorshift128(a) {
|
529 | var b = a * 2e3, c = a * 3e4, d = a * 4e5
|
530 | return function() {
|
531 | var t = d ^ (d << 11)
|
532 | d = c; c = b; b = a
|
533 | a ^= t ^ (t >>> 8) ^ (a >>> 19)
|
534 | return (a >>> 0) / 4294967295
|
535 | }
|
536 | }
|
537 |
|
538 | function _deepEqual(actual, expected, circ) {
|
539 | if (
|
540 | actual === expected ||
|
541 |
|
542 | expected === null && actual == expected ||
|
543 |
|
544 | actual !== actual && expected !== expected
|
545 | ) return true
|
546 |
|
547 | var key, aKeys, len
|
548 | , aType = typeof actual
|
549 |
|
550 | if (
|
551 | aType !== "object" ||
|
552 | actual == null ||
|
553 | aType !== typeof expected ||
|
554 | (aType = type(actual)) != type(expected) ||
|
555 | (actual.constructor && actual.constructor !== expected.constructor) ||
|
556 | (aType == "date" && actual.getTime() !== expected.getTime()) ||
|
557 | (aType == "regexp" && "" + actual !== "" + expected)
|
558 | ) {
|
559 | return false
|
560 | }
|
561 |
|
562 | key = circ.indexOf(actual)
|
563 | if (key > -1) return true
|
564 | push(circ, actual)
|
565 |
|
566 | if (aType == "array" || aType == "arguments") {
|
567 | len = actual.length
|
568 | if (len !== expected.length) return false
|
569 | for (; len--; ) {
|
570 | if (!_deepEqual(actual[len], expected[len], circ)) return false
|
571 | }
|
572 | } else {
|
573 | aKeys = _keys(actual)
|
574 | len = aKeys.length
|
575 | if (len !== _keys(expected).length) return false
|
576 | for (; len--; ) {
|
577 | key = aKeys[len]
|
578 | if (
|
579 | !hasOwn(expected, key) ||
|
580 | !_deepEqual(actual[key], expected[key], circ)
|
581 | ) return false
|
582 | }
|
583 | }
|
584 | return true
|
585 | }
|
586 |
|
587 | function type(obj) {
|
588 |
|
589 |
|
590 |
|
591 | return (
|
592 | obj !== obj ? "nan" :
|
593 | obj === _Infinity || obj === -_Infinity ? "infinity" :
|
594 | obj == null ? "" + obj :
|
595 | toStr.call(obj).slice(8, -1).toLowerCase()
|
596 | )
|
597 | }
|
598 | function num(a, b) {
|
599 | return type(a -= 0) === "number" ? a : b
|
600 | }
|
601 | function isStr(str) {
|
602 | return typeof str === "string"
|
603 | }
|
604 | function isFn(fn) {
|
605 | return typeof fn === "function"
|
606 | }
|
607 | function isObj(obj) {
|
608 | return type(obj) === "object"
|
609 | }
|
610 | function own(a, b) {
|
611 | if (a === b) {
|
612 | own.lastMsg = "Can not be strictEqual"
|
613 | } else if (a) {
|
614 | for (var k in b) if (hasOwn(b, k)) {
|
615 | if (!hasOwn(a, k) || (
|
616 | isObj(b[k]) ? !own(a[k], b[k]) :
|
617 | _isArray(b[k]) ? b[k].some(itemNotOwn, a[k]) :
|
618 | a[k] !== b[k]
|
619 | )) {
|
620 | own.lastMsg = own.lastMsg || k + " " + stringify(a[k]) + " != " + stringify(b[k])
|
621 | return false
|
622 | }
|
623 | }
|
624 | return true
|
625 | }
|
626 | function itemNotOwn(val, idx) {
|
627 | return isObj(val) ? !own(this[idx], val) : this[idx] !== val
|
628 | }
|
629 | }
|
630 | function curry(fn, arg, scope) {
|
631 | return fn.bind.apply(fn, [scope].concat(arg))
|
632 | }
|
633 |
|
634 | function each(arr, fn) {
|
635 | if (arr) {
|
636 | if (isStr(arr)) arr = arr.split(",")
|
637 | for (var i in arr) if (hasOwn(arr, i)) fn(i, arr[i])
|
638 | }
|
639 | }
|
640 |
|
641 | function stringify(item) {
|
642 | var max = conf.cut > 0 ? conf.cut : _Infinity
|
643 | , str = _stringify(item, max, [])
|
644 | return str.length > max ? str.slice(0, max - 3) + ".." + str.slice(-1) : str
|
645 | }
|
646 |
|
647 | function _stringify(item, max, circ) {
|
648 | var i, t, tmp
|
649 | , left = max
|
650 | , str =
|
651 | isStr(item) ? JSON.stringify(item) :
|
652 | isFn(item) ? ("" + item).replace(/^\w+|\s+|{[\s\S]*/g, "") :
|
653 | !item || item === true || typeof item === "number" ? "" + item :
|
654 | (t = type(item)) === "error" || t === "symbol" || t === "regexp" ? item.toString() :
|
655 | item.toJSON ? item.toJSON() :
|
656 | item
|
657 |
|
658 | if (!isStr(str)) {
|
659 | if (circ.indexOf(str) > -1) return "Circular"
|
660 | push(circ, str)
|
661 | tmp = []
|
662 | for (i in str) if (hasOwn(str, i)) {
|
663 | i = (t === "object" ? _stringify(i, left) + ":" : "") + _stringify(str[i], left, circ)
|
664 | push(tmp, i)
|
665 | left -= i.length
|
666 | if (left < 0) break
|
667 | }
|
668 | str =
|
669 | t === "array" ? "[" + tmp + "]" :
|
670 | t === "arguments" ? t + "[" + tmp + "]" :
|
671 | (t = item.constructor) === Object ? "{" + tmp + "}" :
|
672 | (t ? t.name || /^\w+\s+([^\s(]+)|/.exec(t)[1] || "<anon>" : "<null>") + "{" + tmp + "}"
|
673 | }
|
674 | return str
|
675 | }
|
676 | }(this, setTimeout, clearTimeout, Date, Error, Infinity)
|
677 |
|