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