unpoly-rails 0.37.0 → 0.50.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -26,7 +26,7 @@ describe 'up.util', ->
26
26
  done()
27
27
 
28
28
  it "delays resolution of the proxy's .promise if the inner function returns a promise", (done) ->
29
- funDeferred = $.Deferred()
29
+ funDeferred = u.newDeferred()
30
30
  fun = -> funDeferred
31
31
  proxy = up.util.previewable(fun)
32
32
  callback = jasmine.createSpy('promise callback')
@@ -34,31 +34,31 @@ describe 'up.util', ->
34
34
  proxy()
35
35
  u.nextFrame ->
36
36
  expect(callback).not.toHaveBeenCalled()
37
- funDeferred.resolve()
37
+ funDeferred.resolve('return value')
38
38
  u.nextFrame ->
39
- expect(callback).toHaveBeenCalled()
39
+ expect(callback).toHaveBeenCalledWith('return value')
40
40
  done()
41
41
 
42
42
  describe 'up.util.DivertibleChain', ->
43
43
 
44
44
  it "instantiates a task queue whose (2..n)th tasks can be changed by calling '.asap'", (done) ->
45
45
  chain = new up.util.DivertibleChain()
46
-
46
+
47
47
  timer1Spy = jasmine.createSpy('timer1 has been called')
48
48
  timer1 = ->
49
49
  timer1Spy()
50
50
  u.promiseTimer(50)
51
-
51
+
52
52
  timer2Spy = jasmine.createSpy('timer2 has been called')
53
53
  timer2 = ->
54
54
  timer2Spy()
55
55
  u.promiseTimer(50)
56
-
56
+
57
57
  timer3Spy = jasmine.createSpy('timer3 has been called')
58
58
  timer3 = ->
59
59
  timer3Spy()
60
60
  u.promiseTimer(50)
61
-
61
+
62
62
  timer4Spy = jasmine.createSpy('timer4 has been called')
63
63
  timer4 = ->
64
64
  timer4Spy()
@@ -184,15 +184,6 @@ describe 'up.util', ->
184
184
  expect(element.querySelector("title")).toBeMissing()
185
185
  expect(element.querySelector("h1").textContent).toEqual('Full story')
186
186
 
187
- describe 'up.util.cssAnimate', ->
188
-
189
- it 'returns a deferred that eventually resolves if called with a duration of 0 (bugfix)', (done) ->
190
- $element = affix('.element')
191
- promise = up.util.cssAnimate($element, { 'font-size': '90px' }, { duration: 0 })
192
- promise.then ->
193
- expect('done').toEqual('done') # need an expectation of Jasmine will complain
194
- done()
195
-
196
187
  describe 'up.util.isFixed', ->
197
188
 
198
189
  it 'returns true if the given element or one of its ancestors has a "fixed" CSS position', ->
@@ -212,28 +203,26 @@ describe 'up.util', ->
212
203
 
213
204
  describe 'up.util.setTimer', ->
214
205
 
215
- it 'calls the given function after waiting the given milliseconds', ->
216
- jasmine.clock().install()
217
- jasmine.clock().mockDate()
206
+ it 'calls the given function after waiting the given milliseconds', (done) ->
218
207
  callback = jasmine.createSpy()
219
- up.util.setTimer(2000, callback)
220
- expect(callback).not.toHaveBeenCalled()
221
- jasmine.clock().tick(1500)
222
- expect(callback).not.toHaveBeenCalled()
223
- jasmine.clock().tick(1500)
224
- expect(callback).toHaveBeenCalled()
208
+ expectNotCalled = -> expect(callback).not.toHaveBeenCalled()
209
+ expectCalled = -> expect(callback).toHaveBeenCalled()
210
+
211
+ up.util.setTimer(100, callback)
212
+
213
+ expectNotCalled()
214
+ setTimeout(expectNotCalled, 50)
215
+ setTimeout(expectCalled, 50 + 75)
216
+ setTimeout(done, 50 + 75)
225
217
 
226
218
  describe 'if the delay is zero', ->
227
219
 
228
- it 'calls the given function in the current execution frame', ->
220
+ it 'calls the given function in the next execution frame', ->
229
221
  callback = jasmine.createSpy()
230
222
  up.util.setTimer(0, callback)
231
- expect(callback).toHaveBeenCalled()
223
+ expect(callback).not.toHaveBeenCalled()
232
224
 
233
- it "returns undefined so the return value won't be mistaken with a JavaScript timer ID", ->
234
- callback = -> 'function return value'
235
- timerReturnValue = up.util.setTimer(0, callback)
236
- expect(timerReturnValue).toBeUndefined()
225
+ setTimeout((-> expect(callback).toHaveBeenCalled()), 0)
237
226
 
238
227
  # describe 'up.util.argNames', ->
239
228
  #
@@ -361,28 +350,28 @@ describe 'up.util', ->
361
350
  expect(count).toBe(3)
362
351
 
363
352
  describe 'up.util.isBlank', ->
364
-
353
+
365
354
  it 'returns false for false', ->
366
355
  expect(up.util.isBlank(false)).toBe(false)
367
-
356
+
368
357
  it 'returns false for true', ->
369
358
  expect(up.util.isBlank(true)).toBe(false)
370
-
359
+
371
360
  it 'returns true for null', ->
372
361
  expect(up.util.isBlank(null)).toBe(true)
373
-
362
+
374
363
  it 'returns true for undefined', ->
375
364
  expect(up.util.isBlank(undefined)).toBe(true)
376
-
365
+
377
366
  it 'returns true for an empty String', ->
378
367
  expect(up.util.isBlank('')).toBe(true)
379
-
368
+
380
369
  it 'returns false for a String with at least one character', ->
381
370
  expect(up.util.isBlank('string')).toBe(false)
382
-
371
+
383
372
  it 'returns true for an empty array', ->
384
373
  expect(up.util.isBlank([])).toBe(true)
385
-
374
+
386
375
  it 'returns false for an array with at least one element', ->
387
376
  expect(up.util.isBlank(['element'])).toBe(false)
388
377
 
@@ -412,6 +401,9 @@ describe 'up.util', ->
412
401
  it 'does not strip a trailing slash by default', ->
413
402
  expect(up.util.normalizeUrl('/foo/')).toEqual("http://#{location.hostname}:#{location.port}/foo/")
414
403
 
404
+ it 'normalizes redundant segments', ->
405
+ expect(up.util.normalizeUrl('/foo/../foo')).toBe("http://#{location.hostname}:#{location.port}/foo")
406
+
415
407
  describe 'up.util.detect', ->
416
408
 
417
409
  it 'finds the first element in the given array that matches the given tester', ->
@@ -509,97 +501,19 @@ describe 'up.util', ->
509
501
  string = up.util.requestDataAsQuery({ 'my+key': 'my+value' })
510
502
  expect(string).toEqual('my%2Bkey=my%2Bvalue')
511
503
 
512
- describe 'up.util.unresolvableDeferred', ->
513
-
514
- it 'returns a different object every time (to prevent memory leaks)', ->
515
- one = up.util.unresolvableDeferred()
516
- two = up.util.unresolvableDeferred()
517
- expect(one).not.toBe(two)
518
-
519
504
  describe 'up.util.unresolvablePromise', ->
520
-
505
+
506
+ it 'return a pending promise', (done) ->
507
+ promise = up.util.unresolvablePromise()
508
+ promiseState(promise).then (result) ->
509
+ expect(result.state).toEqual('pending')
510
+ done()
511
+
521
512
  it 'returns a different object every time (to prevent memory leaks)', ->
522
513
  one = up.util.unresolvablePromise()
523
514
  two = up.util.unresolvablePromise()
524
515
  expect(one).not.toBe(two)
525
516
 
526
- describe 'up.util.resolvableWhen', ->
527
-
528
- it 'returns a promise that is resolved when all the given deferreds are resolved', ->
529
- one = jasmine.createSpy()
530
- two = jasmine.createSpy()
531
- both = jasmine.createSpy()
532
- oneDeferred = $.Deferred()
533
- oneDeferred.then(one)
534
- twoDeferred = $.Deferred()
535
- twoDeferred.then(two)
536
-
537
- bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
538
- bothDeferred.then(both)
539
-
540
- expect(one).not.toHaveBeenCalled()
541
- expect(two).not.toHaveBeenCalled()
542
- expect(both).not.toHaveBeenCalled()
543
-
544
- oneDeferred.resolve()
545
- expect(one).toHaveBeenCalled()
546
- expect(two).not.toHaveBeenCalled()
547
- expect(both).not.toHaveBeenCalled()
548
-
549
- twoDeferred.resolve()
550
- expect(one).toHaveBeenCalled()
551
- expect(two).toHaveBeenCalled()
552
- expect(both).toHaveBeenCalled()
553
-
554
- it 'returns a promise with a .resolve method that resolves the given deferreds', ->
555
- one = jasmine.createSpy()
556
- two = jasmine.createSpy()
557
- both = jasmine.createSpy()
558
- oneDeferred = $.Deferred()
559
- oneDeferred.then(one)
560
- twoDeferred = $.Deferred()
561
- twoDeferred.then(two)
562
-
563
- bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
564
- bothDeferred.then(both)
565
-
566
- expect(one).not.toHaveBeenCalled()
567
- expect(two).not.toHaveBeenCalled()
568
- expect(both).not.toHaveBeenCalled()
569
-
570
- bothDeferred.resolve()
571
- expect(one).toHaveBeenCalled()
572
- expect(two).toHaveBeenCalled()
573
- expect(both).toHaveBeenCalled()
574
-
575
- it 'does not resolve the given deferreds more than once', ->
576
- oneDeferred = $.Deferred()
577
- spyOn(oneDeferred, 'resolve')
578
- bothDeferred = up.util.resolvableWhen(oneDeferred)
579
-
580
- bothDeferred.resolve()
581
- bothDeferred.resolve()
582
-
583
- expect(oneDeferred.resolve.calls.count()).toEqual(1)
584
-
585
- describe 'bugfix against troublesome jQuery optimization if only one deferred is given', ->
586
-
587
- it 'does not simply return the given deferred', ->
588
- oneDeferred = $.Deferred()
589
- whenDeferred = up.util.resolvableWhen(oneDeferred)
590
- # This is what $.when returns if only passed a single argument
591
- expect(whenDeferred).not.toBe(oneDeferred.promise())
592
- # Cover eventual implementations
593
- expect(whenDeferred).not.toBe(oneDeferred)
594
- expect(whenDeferred.promise()).not.toBe(oneDeferred.promise())
595
- expect(whenDeferred.promise()).not.toBe(oneDeferred)
596
-
597
- it 'does not create an infinite loop if the given deferred is nested twice and the first nesting is resolved', ->
598
- oneDeferred = $.Deferred()
599
- firstNesting = up.util.resolvableWhen(oneDeferred)
600
- secondNesting = up.util.resolvableWhen(firstNesting)
601
- expect(-> firstNesting.resolve()).not.toThrowError()
602
-
603
517
  describe 'up.util.requestDataAsArray', ->
604
518
 
605
519
  it 'normalized null to an empty array', ->
@@ -678,112 +592,224 @@ describe 'up.util', ->
678
592
  { name: 'my=key', value: 'my=value' },
679
593
  ])
680
594
 
681
- describe 'up.util.flatten', ->
595
+ describe 'up.util.flatten', ->
596
+
597
+ it 'flattens the given array', ->
598
+ array = [1, [2, 3], 4]
599
+ expect(u.flatten(array)).toEqual([1, 2, 3, 4])
600
+
601
+ it 'only flattens one level deep for performance reasons', ->
602
+ array = [1, [2, [3,4]], 5]
603
+ expect(u.flatten(array)).toEqual([1, 2, [3, 4], 5])
604
+
605
+ describe 'up.util.renameKey', ->
606
+
607
+ it 'renames a key in the given property', ->
608
+ object = { a: 'a value', b: 'b value'}
609
+ u.renameKey(object, 'a', 'c')
610
+ expect(object.a).toBeUndefined()
611
+ expect(object.b).toBe('b value')
612
+ expect(object.c).toBe('a value')
613
+
614
+ describe 'up.util.selectInSubtree', ->
615
+
616
+ it 'finds the selector in ancestors and descendants of the given element', ->
617
+ $grandMother = affix('.grand-mother.match')
618
+ $mother = $grandMother.affix('.mother')
619
+ $element = $mother.affix('.element')
620
+ $child = $element.affix('.child.match')
621
+ $grandChild = $child.affix('.grand-child.match')
622
+
623
+ $matches = up.util.selectInSubtree($element, '.match')
624
+ $expected = $child.add($grandChild)
625
+ expect($matches).toEqual $expected
626
+
627
+ it 'finds the element itself if it matches the selector', ->
628
+ $element = affix('.element.match')
629
+ $matches = up.util.selectInSubtree($element, '.match')
630
+ expect($matches).toEqual $element
631
+
632
+ describe 'when given a jQuery collection with multiple elements', ->
633
+
634
+ it 'searches in a all subtrees of the given elements', ->
635
+ $a_grandMother = affix('.grand-mother.match')
636
+ $a_mother = $a_grandMother.affix('.mother')
637
+ $a_element = $a_mother.affix('.element')
638
+ $a_child = $a_element.affix('.child.match')
639
+ $a_grandChild = $a_child.affix('.grand-child.match')
640
+
641
+ $b_grandMother = affix('.grand-mother.match')
642
+ $b_mother = $b_grandMother.affix('.mother')
643
+ $b_element = $b_mother.affix('.element')
644
+ $b_child = $b_element.affix('.child.match')
645
+ $b_grandChild = $b_child.affix('.grand-child.match')
646
+
647
+ $matches = up.util.selectInSubtree($a_element.add($b_element), '.match')
648
+ expect($matches).toEqual $a_child.add($a_grandChild).add($b_child).add($b_grandChild)
649
+
650
+
651
+ describe 'up.util.selectInDynasty', ->
652
+
653
+ it 'finds the selector in both ancestors and descendants of the given element', ->
654
+ $grandMother = affix('.grand-mother.match')
655
+ $mother = $grandMother.affix('.mother')
656
+ $element = $mother.affix('.element')
657
+ $child = $element.affix('.child.match')
658
+ $grandChild = $child.affix('.grand-child.match')
659
+
660
+ $matches = up.util.selectInDynasty($element, '.match')
661
+ $expected = $grandMother.add($child).add($grandChild)
662
+ expect($matches).toEqual $expected
663
+
664
+ it 'finds the element itself if it matches the selector', ->
665
+ $element = affix('.element.match')
666
+ $matches = up.util.selectInDynasty($element, '.match')
667
+ expect($matches).toEqual $element
668
+
669
+ describe 'up.util.isCrossDomain', ->
670
+
671
+ it 'returns false for an absolute path', ->
672
+ expect(up.util.isCrossDomain('/foo')).toBe(false)
673
+
674
+ it 'returns false for an relative path', ->
675
+ expect(up.util.isCrossDomain('foo')).toBe(false)
676
+
677
+ it 'returns false for a fully qualified URL with the same protocol and hostname as the current location', ->
678
+ fullUrl = "#{location.protocol}//#{location.host}/foo"
679
+ expect(up.util.isCrossDomain(fullUrl)).toBe(false)
680
+
681
+ it 'returns true for a fully qualified URL with a different protocol than the current location', ->
682
+ fullUrl = "otherprotocol://#{location.host}/foo"
683
+ expect(up.util.isCrossDomain(fullUrl)).toBe(true)
684
+
685
+ it 'returns false for a fully qualified URL with a different hostname than the current location', ->
686
+ fullUrl = "#{location.protocol}//other-host.tld/foo"
687
+ expect(up.util.isCrossDomain(fullUrl)).toBe(true)
688
+
689
+ describe 'up.util.isOptions', ->
690
+
691
+ it 'returns true for an Object instance', ->
692
+ expect(up.util.isOptions(new Object())).toBe(true)
693
+
694
+ it 'returns true for an object literal', ->
695
+ expect(up.util.isOptions({ foo: 'bar'})).toBe(true)
696
+
697
+ it 'returns false for undefined', ->
698
+ expect(up.util.isOptions(undefined)).toBe(false)
699
+
700
+ it 'returns false for null', ->
701
+ expect(up.util.isOptions(null)).toBe(false)
702
+
703
+ it 'returns false for a function (which is technically an object)', ->
704
+ fn = -> 'foo'
705
+ fn.key = 'value'
706
+ expect(up.util.isOptions(fn)).toBe(false)
707
+
708
+ it 'returns false for an array', ->
709
+ expect(up.util.isOptions(['foo'])).toBe(false)
710
+
711
+ it 'returns false for a jQuery collection', ->
712
+ expect(up.util.isOptions($('body'))).toBe(false)
713
+
714
+ it 'returns false for a promise', ->
715
+ expect(up.util.isOptions(Promise.resolve())).toBe(false)
716
+
717
+ it 'returns false for a FormData object', ->
718
+ expect(up.util.isOptions(new FormData())).toBe(false)
719
+
720
+ describe 'up.util.isObject', ->
721
+
722
+ it 'returns true for an Object instance', ->
723
+ expect(up.util.isObject(new Object())).toBe(true)
682
724
 
683
- it 'flattens the given array', ->
684
- array = [1, [2, 3], 4]
685
- expect(u.flatten(array)).toEqual([1, 2, 3, 4])
725
+ it 'returns true for an object literal', ->
726
+ expect(up.util.isObject({ foo: 'bar'})).toBe(true)
686
727
 
687
- it 'only flattens one level deep for performance reasons', ->
688
- array = [1, [2, [3,4]], 5]
689
- expect(u.flatten(array)).toEqual([1, 2, [3, 4], 5])
728
+ it 'returns false for undefined', ->
729
+ expect(up.util.isObject(undefined)).toBe(false)
690
730
 
691
- describe 'up.util.renameKey', ->
731
+ it 'returns false for null', ->
732
+ expect(up.util.isObject(null)).toBe(false)
692
733
 
693
- it 'renames a key in the given property', ->
694
- object = { a: 'a value', b: 'b value'}
695
- u.renameKey(object, 'a', 'c')
696
- expect(object.a).toBeUndefined()
697
- expect(object.b).toBe('b value')
698
- expect(object.c).toBe('a value')
734
+ it 'returns true for a function (which is technically an object)', ->
735
+ fn = -> 'foo'
736
+ fn.key = 'value'
737
+ expect(up.util.isObject(fn)).toBe(true)
699
738
 
700
- describe 'up.util.findWithSelf', ->
739
+ it 'returns true for an array', ->
740
+ expect(up.util.isObject(['foo'])).toBe(true)
701
741
 
702
- it 'finds the selector in descendants of the given element', ->
703
- $container = affix('div')
704
- $child1 = $container.affix('div.match')
705
- $child2 = $container.affix('div')
706
- $child2Child1 = $child2.affix('div.match')
707
- matches = u.findWithSelf($container, '.match')
708
- expect(matches).toEqual [$child1.get(0), $child2Child1.get(0)]
742
+ it 'returns true for a jQuery collection', ->
743
+ expect(up.util.isObject($('body'))).toBe(true)
709
744
 
710
- it 'finds the element itself if the element matches the given selector', ->
711
- $container = affix('div.match')
712
- $child1 = $container.affix('div')
713
- $child1Child1 = $child1.affix('div.match')
714
- matches = u.findWithSelf($container, '.match')
715
- expect(matches).toEqual [$container.get(0), $child1Child1.get(0)]
745
+ it 'returns true for a promise', ->
746
+ expect(up.util.isObject(Promise.resolve())).toBe(true)
716
747
 
717
- it 'returns multiple matches in the same subtree', ->
718
- $container = affix('div.match')
719
- $child1 = $container.affix('div')
720
- $child2 = $container.affix('div.match')
721
- $child2Child1 = $child2.affix('div.match')
722
- matches = u.findWithSelf($container, '.match')
723
- expect(matches).toEqual [$container.get(0), $child2.get(0), $child2Child1.get(0)]
748
+ it 'returns true for a FormData object', ->
749
+ expect(up.util.isObject(new FormData())).toBe(true)
724
750
 
725
- describe 'up.util.memoize', ->
751
+ describe 'up.util.memoize', ->
726
752
 
727
- it 'returns a function that calls the memoized function', ->
728
- fun = (a, b) -> a + b
729
- memoized = u.memoize(fun)
730
- expect(memoized(2, 3)).toEqual(5)
753
+ it 'returns a function that calls the memoized function', ->
754
+ fun = (a, b) -> a + b
755
+ memoized = u.memoize(fun)
756
+ expect(memoized(2, 3)).toEqual(5)
731
757
 
732
- it 'returns the cached return value of the first call when called again', ->
733
- spy = jasmine.createSpy().and.returnValue(5)
734
- memoized = u.memoize(spy)
735
- expect(memoized(2, 3)).toEqual(5)
736
- expect(memoized(2, 3)).toEqual(5)
737
- expect(spy.calls.count()).toEqual(1)
758
+ it 'returns the cached return value of the first call when called again', ->
759
+ spy = jasmine.createSpy().and.returnValue(5)
760
+ memoized = u.memoize(spy)
761
+ expect(memoized(2, 3)).toEqual(5)
762
+ expect(memoized(2, 3)).toEqual(5)
763
+ expect(spy.calls.count()).toEqual(1)
738
764
 
739
- ['assign', 'assignPolyfill'].forEach (assignVariant) ->
765
+ ['assign', 'assignPolyfill'].forEach (assignVariant) ->
740
766
 
741
- describe "up.util.#{assignVariant}", ->
767
+ describe "up.util.#{assignVariant}", ->
742
768
 
743
- assign = up.util[assignVariant]
769
+ assign = up.util[assignVariant]
744
770
 
745
- it 'copies the second object into the first object', ->
746
- target = { a: 1 }
747
- source = { b: 2, c: 3 }
771
+ it 'copies the second object into the first object', ->
772
+ target = { a: 1 }
773
+ source = { b: 2, c: 3 }
748
774
 
749
- assign(target, source)
775
+ assign(target, source)
750
776
 
751
- expect(target).toEqual { a: 1, b: 2, c: 3 }
777
+ expect(target).toEqual { a: 1, b: 2, c: 3 }
752
778
 
753
- # Source is unchanged
754
- expect(source).toEqual { b: 2, c: 3 }
779
+ # Source is unchanged
780
+ expect(source).toEqual { b: 2, c: 3 }
755
781
 
756
- it 'copies null property values', ->
757
- target = { a: 1, b: 2 }
758
- source = { b: null }
782
+ it 'copies null property values', ->
783
+ target = { a: 1, b: 2 }
784
+ source = { b: null }
759
785
 
760
- assign(target, source)
786
+ assign(target, source)
761
787
 
762
- expect(target).toEqual { a: 1, b: null }
788
+ expect(target).toEqual { a: 1, b: null }
763
789
 
764
- it 'copies undefined property values', ->
765
- target = { a: 1, b: 2 }
766
- source = { b: undefined }
790
+ it 'copies undefined property values', ->
791
+ target = { a: 1, b: 2 }
792
+ source = { b: undefined }
767
793
 
768
- assign(target, source)
794
+ assign(target, source)
769
795
 
770
- expect(target).toEqual { a: 1, b: undefined }
796
+ expect(target).toEqual { a: 1, b: undefined }
771
797
 
772
- it 'returns the first object', ->
773
- target = { a: 1 }
774
- source = { b: 2 }
798
+ it 'returns the first object', ->
799
+ target = { a: 1 }
800
+ source = { b: 2 }
775
801
 
776
- result = assign(target, source)
802
+ result = assign(target, source)
777
803
 
778
- expect(result).toBe(target)
804
+ expect(result).toBe(target)
779
805
 
780
- it 'takes multiple sources to copy from', ->
781
- target = { a: 1 }
782
- source1 = { b: 2, c: 3 }
783
- source2 = { d: 4, e: 5 }
806
+ it 'takes multiple sources to copy from', ->
807
+ target = { a: 1 }
808
+ source1 = { b: 2, c: 3 }
809
+ source2 = { d: 4, e: 5 }
784
810
 
785
- assign(target, source1, source2)
811
+ assign(target, source1, source2)
786
812
 
787
- expect(target).toEqual { a: 1, b: 2, c: 3, d: 4, e: 5 }
813
+ expect(target).toEqual { a: 1, b: 2, c: 3, d: 4, e: 5 }
788
814
 
789
815