statebus 7.1.0 → 7.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/extras/tests.js +120 -40
  2. package/package.json +1 -1
  3. package/statebus.js +207 -187
package/extras/tests.js CHANGED
@@ -319,48 +319,128 @@ test(function proxy_link (done) {
319
319
  done()
320
320
  })
321
321
 
322
- test(function proxy_links_through (done) {
323
- var bus = require('../statebus')()
322
+ // Disable the version of proxy_links_through where the user does not have to
323
+ // explicitly traverse links with ._()
324
+ if (true)
325
+ test(function proxy_links_through2 (done) {
326
+ var bus = require('../statebus')()
324
327
 
325
- // First try to dereference through three links
328
+ // First try to dereference through three links
326
329
 
327
- bus.state.a = [bus.link('b')]
328
- bus.state.b = bus.link('c')
329
- bus.state.c = 'see'
330
-
331
- log('state.c is', bus.state.c)
332
- log('state.b is', bus.state.b, 'from cache', bus.cache.b)
333
- log('state.a is', bus.state.a)
334
- log('state.a[0] is', bus.state.a[0])
335
- log('state.a[0]() is', bus.state.a[0]())
336
- log('state.a[0]()() is', bus.state.a[0]()())
337
- assert(bus.state.a[0]()() === 'see')
338
-
339
- // Add a little more wrapping and try some more
340
-
341
- bus.state.nested = [99, {a: bus.link('a')}]
342
- log('nested is', bus.state.nested)
343
- log('nested[1].a()[0]()() is', bus.state.nested[1].a()[0]()())
344
- assert(bus.state.nested[1].a()[0]()() === 'see')
345
-
346
- // Now try to go raw at the top-level
347
-
348
- log('Raw is', bus.state[bus.symbols.raw].nested)
349
- log('Raw is', bus.raw(bus.state).nested)
350
- assert(bus.deep_equals(
351
- bus.state[bus.symbols.raw].nested,
352
- bus.raw(bus.state).nested
353
- ))
354
- assert(bus.deep_equals(
355
- bus.raw(bus.state).nested,
356
- [ 99, { a: { link: 'a' } } ]
357
- ))
358
-
359
- // Now try to go raw within
360
-
361
- log('Raw inner is', bus.raw(bus.state.nested[1]))
362
-
363
- log('Bad raw call is', bus.raw(bus.state.nested[0]))
330
+ bus.state.a = [bus.link('b')]
331
+ bus.state.b = bus.link('c')
332
+ bus.state.c = 'see'
333
+
334
+ log('state.c is', bus.state.c)
335
+ log('state.b is', bus.state.b, 'from cache', bus.cache.b)
336
+ log('state.a is', bus.state.a)
337
+ log('state.a[0] is', bus.state.a[0])
338
+ log('state.a[0]._() is', bus.state.a[0]._())
339
+ log('state.a[0]._()._() is', bus.state.a[0]._()._())
340
+ assert(bus.state.a[0]._()._() === 'see')
341
+
342
+ // Add a little more wrapping and try some more
343
+
344
+ bus.state.nested = [99, {a: bus.link('a')}]
345
+ log('nested is', bus.state.nested)
346
+ log('nested[1].a._()[0]._()._() is', bus.state.nested[1].a._()[0]._()._())
347
+ assert(bus.state.nested[1].a._()[0]._()._() === 'see')
348
+
349
+ // Now try to go raw at the top-level
350
+
351
+ log('Raw is', bus.state[bus.symbols.raw].nested)
352
+ log('Raw is', bus.raw(bus.state).nested)
353
+ assert(bus.deep_equals(
354
+ bus.state[bus.symbols.raw].nested,
355
+ bus.raw(bus.state).nested
356
+ ))
357
+
358
+ log('Gonna deep_quals between:',
359
+ bus.raw(bus.state).nested,
360
+ [ 99, { a: { link: 'a' } } ])
361
+
362
+ assert(bus.deep_equals(
363
+ bus.raw(bus.state).nested,
364
+ [ 99, { a: { link: 'a' } } ]
365
+ ))
366
+
367
+ // Now try to go raw within
368
+
369
+ log('Raw inner is', bus.raw(bus.state.nested[1]))
370
+
371
+ log('Bad raw call is', bus.raw(bus.state.nested[0]))
372
+
373
+ done()
374
+ })
375
+ else
376
+ test(function proxy_links_through1 (done) {
377
+ var bus = require('../statebus')()
378
+
379
+ // First try to dereference through three links
380
+
381
+ bus.state.a = [bus.link('b')]
382
+ bus.state.b = bus.link('c')
383
+ bus.state.c = 'see'
384
+
385
+ log('state.c is', bus.state.c)
386
+ log('state.b is', bus.state.b, 'from cache', bus.cache.b)
387
+ log('state.a is', bus.state.a)
388
+ log('state.a[0] is', bus.state.a[0])
389
+ log('state.a[0] is', bus.state.a[0])
390
+ assert(bus.state.a[0] === 'see')
391
+
392
+ // Add a little more wrapping and try some more
393
+
394
+ bus.state.nested = [99, {a: bus.link('a')}]
395
+ log('nested is', bus.state.nested)
396
+ log('nested[1].a[0] is', bus.state.nested[1].a[0])
397
+ assert(bus.state.nested[1].a[0] === 'see')
398
+
399
+ // Now try to go raw at the top-level
400
+
401
+ log('Raw is', bus.state[bus.symbols.raw].nested)
402
+ log('Raw is', bus.raw(bus.state).nested)
403
+ assert(bus.deep_equals(
404
+ bus.state[bus.symbols.raw].nested,
405
+ bus.raw(bus.state).nested
406
+ ))
407
+
408
+ log('Gonna deep_quals between:',
409
+ bus.raw(bus.state).nested,
410
+ [ 99, { a: { link: 'a' } } ])
411
+
412
+ assert(bus.deep_equals(
413
+ bus.raw(bus.state).nested,
414
+ [ 99, { a: { link: 'a' } } ]
415
+ ))
416
+
417
+ // Now try to go raw within
418
+
419
+ log('Raw inner is', bus.raw(bus.state.nested[1]))
420
+
421
+ log('Bad raw call is', bus.raw(bus.state.nested[0]))
422
+
423
+ done()
424
+ })
425
+
426
+
427
+ test(function copy_link (done) {
428
+ var bus = require('../statebus')(), state = bus.state
429
+
430
+ state.a = [bus.link('b')]
431
+ state.b = 3
432
+ state.copy_of_a = state.a
433
+ state.copy_of_a0 = state.a[0]
434
+
435
+ console.log(bus.cache.a)
436
+ console.log(bus.cache.b)
437
+ console.log(bus.cache.copy_of_a)
438
+ console.log(bus.cache.copy_of_a0)
439
+
440
+ assert(bus.cache.a.val[0].link === 'b')
441
+ assert(bus.cache.b.val === 3)
442
+ assert(bus.cache.copy_of_a.val[0].link === 'b')
443
+ assert(bus.cache.copy_of_a0.val.link === 'b')
364
444
 
365
445
  done()
366
446
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "7.1.0",
3
+ "version": "7.1.2",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
package/statebus.js CHANGED
@@ -1455,203 +1455,124 @@
1455
1455
  raw: Symbol('raw'),
1456
1456
  link: Symbol('link')
1457
1457
  }
1458
- function make_proxy () {
1459
- // var custom_inspect_symbol = nodejs && require('util').inspect.custom,
1460
- // custom_inspect = function (depth, options) {
1461
- // return '3333333333';// util.inspect(bus.unescape_from_nelson(this), options)
1462
- // }
1463
- function item_proxy (base, path, o) {
1464
-
1465
- // Primitives pass through unscathed
1466
- if (typeof o === 'number'
1467
- || typeof o === 'string'
1468
- || typeof o === 'boolean'
1469
- || o === undefined
1470
- || o === null
1471
- || typeof o === 'function')
1472
-
1473
- return o
1474
-
1475
- // We recursively descend through {key: ...} links
1476
- if (typeof o === 'object' && 'link' in o) {
1477
- // var new_base = bus.get(o.link)
1478
- // return item_proxy(new_base, '', new_base.val)
1479
- return link_proxy(o.link)
1480
- }
1481
1458
 
1459
+ // The top-level Proxy object holds HTTP resources
1460
+ var top_level_proxy = new Proxy(cache, {
1461
+ get: function get(o, k) {
1462
+ if (k === symbols.is_proxy)
1463
+ return true
1464
+ if (k === symbols.raw)
1465
+ return raw_proxy()
1466
+ if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
1467
+ return undefined
1468
+ bogus_check(k)
1469
+ var base = bus.get(k)
1470
+ return json_proxy(base, '', base.val)
1471
+ },
1472
+ set: function set(o, key, val) {
1473
+ bus.set({
1474
+ key: key,
1475
+ val: escape_json_to_bus(val)
1476
+ })
1477
+ return true
1478
+ },
1479
+ deleteProperty: function del (o, k) {
1480
+ bus.delete(escape_field_to_bus(k))
1481
+ return true // Report success to Proxy
1482
+ }
1483
+ })
1482
1484
 
1483
- // For function proxies:
1484
- //
1485
- // // Javascript won't let us function call a proxy unless the
1486
- // // "target" is a function. So we make a dummy target, and
1487
- // // don't use it.
1488
- // var dummy = function () {}
1489
-
1490
-
1491
- return new Proxy(o, {
1492
- get: function (o, k) {
1493
- if (k === 'inspect' || k === 'valueOf')
1494
- return undefined
1495
- // if (custom_inspect && k === custom_inspect)
1496
- // return custom_inspect
1497
- if (k === symbols.is_proxy)
1498
- return true
1499
- if (k === symbols.raw)
1500
- return o
1501
- if (typeof k === 'symbol')
1502
- return undefined
1503
-
1504
- // Compute the new path
1505
- var new_path = path + '[' + JSON.stringify(k) + ']'
1506
- return item_proxy(base, new_path, o[escape_field_json_to_bus(k)])
1507
- },
1508
- set: function (o, k, v) {
1509
- var value = escape_json_to_bus(v)
1510
- o[escape_field_json_to_bus(k)] = value
1511
- var new_path = path + '[' + JSON.stringify(k) + ']'
1512
- bus.set.sync(
1513
- base,
1514
- // Forward the patches too
1515
- {patches: [{unit: 'json',
1516
- range: new_path,
1517
- content: JSON.stringify(v)}]}
1518
- )
1519
- return true
1520
- },
1521
- has: function (o, k) {
1522
- // if (custom_inspect && k === custom_inspect)
1523
- // return true
1524
- return o.hasOwnProperty(escape_field_json_to_bus(k))
1525
- },
1526
- ownKeys: function () {
1527
- return Object.keys(o).map(unescape_field_bus_to_json)
1528
- },
1529
- getOwnPropertyDescriptor: function (target, key) {
1530
- return { enumerable: true, configurable: true, value: this.get(o, key) }
1531
- },
1532
- deleteProperty: function del (o, k) {
1533
- var new_path = path + '[' + JSON.stringify(k) + ']'
1534
- delete o[escape_field_json_to_bus(k)]
1535
- bus.set(
1536
- base,
1537
- // Forward the patches too
1538
- {patches: [{unit: 'json',
1539
- range: new_path,
1540
- content: undefined}]}
1541
- )
1542
- return true // Report success to Proxy
1543
- }
1544
- // For function proxies:
1545
- //
1546
- // apply: function apply (o, This, args) {
1547
- // return translate_fields(o, unescape_field_from_bus)
1548
- // }
1549
- })}
1550
-
1551
- function link_proxy (url) {
1552
- return new Proxy(function follow_link () {}, {
1553
- get: function (o, k) {
1554
- if (k === 'inspect' || k === 'valueOf')
1555
- return undefined
1556
- // if (custom_inspect && k === custom_inspect)
1557
- // return custom_inspect
1558
- if (k === symbols.is_proxy)
1559
- return true
1560
- if (k === symbols.raw)
1561
- return {link: url}
1562
- if (typeof k === 'symbol')
1563
- return undefined
1564
-
1565
- if (k === 'link')
1566
- return url
1485
+ // The proxy within; a recursive tree holds each piece of JSON in a resource
1486
+ function json_proxy (base, path, o) {
1567
1487
 
1568
- return undefined
1569
- },
1570
- set: function (o, k, v) {
1571
- if (k === 'link') {
1572
- url = v
1573
- }
1488
+ // Primitives pass through unscathed
1489
+ if (typeof o === 'number'
1490
+ || typeof o === 'string'
1491
+ || typeof o === 'boolean'
1492
+ || o === undefined
1493
+ || o === null
1494
+ || typeof o === 'function')
1574
1495
 
1575
- console.assert(false, "Have not fully implemented setting links yet")
1576
-
1577
- // Todo: update the containing proxy object with bus.set.sync
1578
- // else return
1579
- // var value = escape_json_to_bus(v)
1580
- // o[escape_field_json_to_bus(k)] = value
1581
- // var new_path = path + '[' + JSON.stringify(k) + ']'
1582
- // bus.set.sync(
1583
- // base,
1584
- // // Forward the patches too
1585
- // {patches: [{unit: 'json',
1586
- // range: new_path,
1587
- // content: JSON.stringify(v)}]}
1588
- // )
1589
- return true
1590
- },
1591
- has: function (o, k) {
1592
- return k === 'link'
1593
- },
1594
- ownKeys: function () {
1595
- return ['link']
1596
- },
1597
- // getOwnPropertyDescriptor: function (target, key) {
1598
- // return { enumerable: true, configurable: true, value: this.get(o, key) }
1599
- // },
1600
- // deleteProperty: function del (o, k) {
1601
- // var new_path = path + '[' + JSON.stringify(k) + ']'
1602
- // delete o[escape_field_json_to_bus(k)]
1603
- // bus.set(
1604
- // base,
1605
- // // Forward the patches too
1606
- // {patches: [{unit: 'json',
1607
- // range: new_path,
1608
- // content: undefined}]}
1609
- // )
1610
- // return true // Report success to Proxy
1611
- // }
1612
-
1613
- // For function proxy:
1614
- apply: function apply (o, This, args) {
1615
- // console.log('Descending into the link', url, '!')
1616
- return top_level_proxy[url]
1617
- }
1618
- })
1496
+ return o
1497
+
1498
+ // We recursively descend through {key: ...} links
1499
+ if (typeof o === 'object' && 'link' in o) {
1500
+ // var new_base = bus.get(o.link)
1501
+ // return json_proxy(new_base, '', new_base.val)
1502
+ return link(o.link)
1619
1503
  }
1620
1504
 
1621
1505
 
1622
- // The top-level Proxy object holds HTTP resources
1623
- var top_level_proxy = new Proxy(cache, {
1624
- get: function get(o, k) {
1506
+ // For function proxies:
1507
+ //
1508
+ // // Javascript won't let us function call a proxy unless the
1509
+ // // "target" is a function. So we make a dummy target, and
1510
+ // // don't use it.
1511
+ // var dummy = function () {}
1512
+
1513
+
1514
+ return new Proxy(o, {
1515
+ get: function (o, k) {
1516
+ if (k === 'inspect' || k === 'valueOf')
1517
+ return undefined
1518
+ // if (custom_inspect && k === custom_inspect)
1519
+ // return custom_inspect
1625
1520
  if (k === symbols.is_proxy)
1626
1521
  return true
1627
1522
  if (k === symbols.raw)
1628
- return raw_proxy()
1629
- if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
1523
+ return o
1524
+ if (typeof k === 'symbol')
1630
1525
  return undefined
1631
- bogus_check(k)
1632
- var base = bus.get(k)
1633
- return item_proxy(base, '', base.val)
1526
+
1527
+ // Compute the new path
1528
+ var new_path = path + '[' + JSON.stringify(k) + ']'
1529
+ return json_proxy(base, new_path, o[escape_field_json_to_bus(k)])
1634
1530
  },
1635
- set: function set(o, key, val) {
1636
- bus.set({
1637
- key: key,
1638
- val: escape_json_to_bus(val)
1639
- })
1531
+ set: function (o, k, v) {
1532
+ var value = escape_json_to_bus(v)
1533
+ o[escape_field_json_to_bus(k)] = value
1534
+ var new_path = path + '[' + JSON.stringify(k) + ']'
1535
+ bus.set.sync(
1536
+ base,
1537
+ // Forward the patches too
1538
+ {patches: [{unit: 'json',
1539
+ range: new_path,
1540
+ content: JSON.stringify(v)}]}
1541
+ )
1640
1542
  return true
1641
1543
  },
1544
+ has: function (o, k) {
1545
+ // if (custom_inspect && k === custom_inspect)
1546
+ // return true
1547
+ return o.hasOwnProperty(escape_field_json_to_bus(k))
1548
+ },
1549
+ ownKeys: function () {
1550
+ return Object.keys(o).map(unescape_field_bus_to_json)
1551
+ },
1552
+ getOwnPropertyDescriptor: function (target, key) {
1553
+ return { enumerable: true, configurable: true, value: this.get(o, key) }
1554
+ },
1642
1555
  deleteProperty: function del (o, k) {
1643
- bus.delete(escape_field_to_bus(k))
1556
+ var new_path = path + '[' + JSON.stringify(k) + ']'
1557
+ delete o[escape_field_json_to_bus(k)]
1558
+ bus.set(
1559
+ base,
1560
+ // Forward the patches too
1561
+ {patches: [{unit: 'json',
1562
+ range: new_path,
1563
+ content: undefined}]}
1564
+ )
1644
1565
  return true // Report success to Proxy
1645
- },
1566
+ }
1646
1567
  // For function proxies:
1568
+ //
1647
1569
  // apply: function apply (o, This, args) {
1648
- // return 'fluffy'
1570
+ // return translate_fields(o, unescape_field_from_bus)
1649
1571
  // }
1650
1572
  })
1651
-
1652
- return top_level_proxy
1653
1573
  }
1654
- bus.state = make_proxy()
1574
+
1575
+ bus.state = top_level_proxy
1655
1576
 
1656
1577
  // This is temporary code to wrap the cache as a flat key/valuel store
1657
1578
  // that returns raw objects, dereferencing .val. We can remove it once we
@@ -1668,9 +1589,104 @@
1668
1589
  })
1669
1590
  }
1670
1591
 
1592
+ // How proxy links work right now:
1593
+ // - The user creates a link with bus.link(key)
1594
+ // - which produces a {[symbols.link]: key}
1595
+ // - which when set() into a proxy, is replaced with a {link: key}, to store internally
1596
+ // - Then, when get()ing that internal {link: key} through Proxy:
1597
+ // - We auto-dereference the key, with another get()
1598
+ // - So the user actually sees the value of the resource on the other side of the link
1671
1599
  function link (url) {
1672
- return {[symbols.link]: url}
1673
- }
1600
+ return {
1601
+ link: url,
1602
+ [symbols.link]: true,
1603
+ _: (args, o) => (o = get(url), json_proxy(o, '', o.val))
1604
+ }
1605
+ }
1606
+
1607
+ // // The proxy object for links. Disabled for now.
1608
+ // // This type of link has to be function called, as link(), to dereference.
1609
+ // function proxy_link (url) {
1610
+ // function follow_link () {}
1611
+ // return new Proxy(follow_link, {
1612
+ // get: function (o, k) {
1613
+ // console.log('get', k)
1614
+ // if (k === 'inspect' || k === 'valueOf')
1615
+ // return undefined
1616
+ // // if (custom_inspect && k === custom_inspect)
1617
+ // // return custom_inspect
1618
+ // if (k === symbols.is_proxy)
1619
+ // return true
1620
+ // if (k === symbols.link)
1621
+ // return true
1622
+ // if (k === symbols.raw)
1623
+ // return {link: url}
1624
+ // if (typeof k === 'symbol')
1625
+ // return undefined
1626
+
1627
+ // if (k === 'link')
1628
+ // return url
1629
+
1630
+ // return follow_link[k]
1631
+
1632
+ // // return undefined
1633
+ // },
1634
+ // set: function (o, k, v) {
1635
+ // if (k === 'link') {
1636
+ // url = v
1637
+ // }
1638
+
1639
+ // console.assert(false, "Have not fully implemented setting links yet")
1640
+
1641
+ // // Todo: update the containing proxy object with bus.set.sync
1642
+ // // else return
1643
+ // // var value = escape_json_to_bus(v)
1644
+ // // o[escape_field_json_to_bus(k)] = value
1645
+ // // var new_path = path + '[' + JSON.stringify(k) + ']'
1646
+ // // bus.set.sync(
1647
+ // // base,
1648
+ // // // Forward the patches too
1649
+ // // {patches: [{unit: 'json',
1650
+ // // range: new_path,
1651
+ // // content: JSON.stringify(v)}]}
1652
+ // // )
1653
+ // return true
1654
+ // },
1655
+ // has: function (o, k) {
1656
+ // return k === 'link'
1657
+ // },
1658
+ // ownKeys: function () {
1659
+ // console.log('ownkeys')
1660
+ // return ['link', 'arguments', 'caller', 'prototype']
1661
+ // },
1662
+ // getOwnPropertyDescriptor: function (target, key) {
1663
+ // console.log('getownpropertydescriptor', key)
1664
+ // return {
1665
+ // enumerable: true,
1666
+ // configurable: true,
1667
+ // value: key === 'link' ? url : undefined
1668
+ // }
1669
+ // },
1670
+ // // deleteProperty: function del (o, k) {
1671
+ // // var new_path = path + '[' + JSON.stringify(k) + ']'
1672
+ // // delete o[escape_field_json_to_bus(k)]
1673
+ // // bus.set(
1674
+ // // base,
1675
+ // // // Forward the patches too
1676
+ // // {patches: [{unit: 'json',
1677
+ // // range: new_path,
1678
+ // // content: undefined}]}
1679
+ // // )
1680
+ // // return true // Report success to Proxy
1681
+ // // }
1682
+
1683
+ // // For function proxy:
1684
+ // apply: function apply (o, This, args) {
1685
+ // // console.log('Descending into the link', url, '!')
1686
+ // return top_level_proxy[url]
1687
+ // }
1688
+ // })
1689
+ // }
1674
1690
  function raw (proxy) {
1675
1691
  if (!(typeof proxy === 'object' && proxy[symbols.is_proxy]))
1676
1692
  return proxy
@@ -2064,7 +2080,7 @@
2064
2080
 
2065
2081
  // Three levels of escaping through state:
2066
2082
  //
2067
- // - statebus-internal escapes key -> _key
2083
+ // - bus internal escapes key -> _key
2068
2084
  // - nelSON escapes link -> _link
2069
2085
  // - JSON
2070
2086
  //
@@ -2086,11 +2102,11 @@
2086
2102
  var escape_json_to_bus = (obj) => {
2087
2103
  obj = translate_fields(obj, escape_field_json_to_bus)
2088
2104
  return deep_map(obj, o => (typeof o === 'object' && symbols.link in o
2089
- ? {link: o[symbols.link]}
2105
+ ? {link: o.link}
2090
2106
  : o))
2091
2107
  }
2092
2108
  var unescape_bus_to_json = (obj) =>
2093
- translate_fields(obj, field => unescape_field_bus_to_jsob(field))
2109
+ translate_fields(obj, field => unescape_field_bus_to_json(field))
2094
2110
 
2095
2111
 
2096
2112
  var field_replace = (field, pattern, replacement) => (typeof field === 'string'
@@ -2276,12 +2292,17 @@
2276
2292
  return object
2277
2293
  }
2278
2294
  function deep_equals (a, b) {
2295
+ // This code was only needed for link_proxy:
2296
+ // // Treat link proxy objects as regular JSON links. Otherwise, the
2297
+ // // function fields on them confuse everything.
2298
+ // if (a && typeof a === 'object' && a[symbols.link]) a = {link: a.link}
2299
+ // if (b && typeof b === 'object' && b[symbols.link]) b = {link: b.link}
2300
+
2279
2301
  // Equal Primitives?
2280
2302
  if (a === b
2281
2303
  // But because NaN === NaN returns false:
2282
- || (isNaN(a) && isNaN(b)
2283
- // And because isNaN(undefined) returns true:
2284
- && typeof a === 'number' && typeof b === 'number'))
2304
+ || (typeof a === 'number' && typeof b === 'number'
2305
+ && isNaN(a) && isNaN(b)))
2285
2306
  return true
2286
2307
 
2287
2308
  // Equal Arrays?
@@ -2319,9 +2340,8 @@
2319
2340
  // Equal Primitives?
2320
2341
  if (a === b
2321
2342
  // But because NaN === NaN returns false:
2322
- || (isNaN(a) && isNaN(b)
2323
- // And because isNaN(undefined) returns true:
2324
- && typeof a === 'number' && typeof b === 'number'))
2343
+ || (typeof a === 'number' && typeof b === 'number'
2344
+ && isNaN(a) && isNaN(b)))
2325
2345
  return null
2326
2346
 
2327
2347
  // Equal Arrays?