unpoly-rails 0.55.1 → 0.56.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -2
- data/dist/unpoly-bootstrap3.js +6 -4
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.js +1323 -805
- data/dist/unpoly.min.js +4 -3
- data/lib/assets/javascripts/unpoly-bootstrap3/{navigation-ext.coffee → feedback-ext.coffee} +2 -0
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +7 -7
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +5 -6
- data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +127 -0
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +62 -32
- data/lib/assets/javascripts/unpoly/classes/url_set.coffee +27 -0
- data/lib/assets/javascripts/unpoly/dom.coffee.erb +78 -99
- data/lib/assets/javascripts/unpoly/feedback.coffee +147 -96
- data/lib/assets/javascripts/unpoly/form.coffee.erb +26 -2
- data/lib/assets/javascripts/unpoly/history.coffee +2 -1
- data/lib/assets/javascripts/unpoly/layout.coffee.erb +68 -12
- data/lib/assets/javascripts/unpoly/link.coffee.erb +10 -4
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +11 -9
- data/lib/assets/javascripts/unpoly/{motion.coffee → motion.coffee.erb} +184 -322
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +13 -12
- data/lib/assets/javascripts/unpoly/radio.coffee +1 -1
- data/lib/assets/javascripts/unpoly/syntax.coffee +8 -17
- data/lib/assets/javascripts/unpoly/tooltip.coffee +11 -11
- data/lib/assets/javascripts/unpoly/util.coffee +332 -145
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/stylesheets/integration_test.sass +1 -0
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +4 -0
- data/spec_app/app/views/motion_test/transitions.erb +13 -0
- data/spec_app/app/views/pages/start.erb +1 -0
- data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +11 -0
- data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +217 -102
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +162 -44
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +97 -10
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +3 -3
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +22 -20
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +344 -228
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +194 -0
- metadata +11 -4
data/lib/unpoly/rails/version.rb
CHANGED
data/package.json
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="fixed-top-bar">
|
2
|
+
<% for animation in %w(cross-fade move-up move-right move-down move-left) do %>
|
3
|
+
<span class="button" onclick="up.replace('.morphed-object', 'transitions', { transition: '<%= animation %>', duration: 2000, cache: false })">
|
4
|
+
<%= animation %>
|
5
|
+
</span>
|
6
|
+
<% end %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="example">
|
10
|
+
<div class="morphed-object" style="padding: 30px; background-color: #<%= SecureRandom.hex(3) %>; color: white; width: 300px; height: 200px; margin-left: 200px; margin-top: 200px">
|
11
|
+
<%= ('a'..'z').to_a.shuffle[0, 20].join %>
|
12
|
+
</div>
|
13
|
+
</div>
|
@@ -63,6 +63,7 @@
|
|
63
63
|
<li><%= link_to 'Hash links (without Unpoly)', '/hash_test/vanilla' %></li>
|
64
64
|
<li><%= link_to 'Revealing long pages', '/reveal_test/long1' %></li>
|
65
65
|
<li><%= link_to 'Animations', '/motion_test/animations' %></li>
|
66
|
+
<li><%= link_to 'Transitions', '/motion_test/transitions' %></li>
|
66
67
|
</ul>
|
67
68
|
|
68
69
|
</div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
beforeEach ->
|
2
|
+
jasmine.addMatchers
|
3
|
+
toHaveOpacity: (util, customEqualityTesters) ->
|
4
|
+
compare: (element, expectedOpacity, tolerance = 0.0) ->
|
5
|
+
element = up.util.element(element)
|
6
|
+
actualOpacity = up.util.opacity(element)
|
7
|
+
result = {}
|
8
|
+
result.pass = Math.abs(expectedOpacity - actualOpacity) <= tolerance
|
9
|
+
unless result.pass
|
10
|
+
result.message = up.browser.sprintf("Expected %o to have opacity %o, but it was %o", element, expectedOpacity, actualOpacity)
|
11
|
+
return result
|
@@ -48,39 +48,7 @@ describe 'up.dom', ->
|
|
48
48
|
expect(resolution).toHaveBeenCalled()
|
49
49
|
expect($('.middle')).toHaveText('new-middle')
|
50
50
|
|
51
|
-
describe '
|
52
|
-
|
53
|
-
it 'calls destructors on the replaced element', asyncSpec (next) ->
|
54
|
-
destructor = jasmine.createSpy('destructor')
|
55
|
-
up.compiler '.container', -> destructor
|
56
|
-
$container = affix('.container')
|
57
|
-
up.hello($container)
|
58
|
-
up.replace('.container', '/path')
|
59
|
-
|
60
|
-
next =>
|
61
|
-
@respondWith '<div class="container">new text</div>'
|
62
|
-
|
63
|
-
next =>
|
64
|
-
expect('.container').toHaveText('new text')
|
65
|
-
expect(destructor).toHaveBeenCalled()
|
66
|
-
|
67
|
-
it 'calls destructors when the replaced element is a singleton element like <body> (bugfix)', asyncSpec (next) ->
|
68
|
-
# shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
|
69
|
-
up.dom.knife.mock('shouldSwapElementsDirectly').and.callFake ($element) -> $element.is('.container')
|
70
|
-
destructor = jasmine.createSpy('destructor')
|
71
|
-
up.compiler '.container', -> destructor
|
72
|
-
$container = affix('.container')
|
73
|
-
up.hello($container)
|
74
|
-
up.replace('.container', '/path')
|
75
|
-
|
76
|
-
next =>
|
77
|
-
@respondWith '<div class="container">new text</div>'
|
78
|
-
|
79
|
-
next =>
|
80
|
-
expect('.container').toHaveText('new text')
|
81
|
-
expect(destructor).toHaveBeenCalled()
|
82
|
-
|
83
|
-
describe 'transitions', ->
|
51
|
+
describe 'with { transition } option', ->
|
84
52
|
|
85
53
|
it 'returns a promise that will be fulfilled once the server response was received and the swap transition has completed', asyncSpec (next) ->
|
86
54
|
resolution = jasmine.createSpy()
|
@@ -99,21 +67,6 @@ describe 'up.dom', ->
|
|
99
67
|
next.after 80, =>
|
100
68
|
expect(resolution).toHaveBeenCalled()
|
101
69
|
|
102
|
-
it 'ignores a { transition } option when replacing the body element', asyncSpec (next) ->
|
103
|
-
up.dom.knife.mock('swapElementsDirectly') # can't have the example replace the Jasmine test runner UI
|
104
|
-
up.dom.knife.mock('destroy') # if we don't swap the body, up.dom will destroy it
|
105
|
-
replaceCallback = jasmine.createSpy()
|
106
|
-
promise = up.replace('body', '/path', transition: 'cross-fade', duration: 50)
|
107
|
-
promise.then(replaceCallback)
|
108
|
-
expect(replaceCallback).not.toHaveBeenCalled()
|
109
|
-
|
110
|
-
next =>
|
111
|
-
@responseText = '<body>new text</body>'
|
112
|
-
@respond()
|
113
|
-
|
114
|
-
next =>
|
115
|
-
expect(replaceCallback).toHaveBeenCalled()
|
116
|
-
|
117
70
|
describe 'with { data } option', ->
|
118
71
|
|
119
72
|
it "uses the given params as a non-GET request's payload", asyncSpec (next) ->
|
@@ -1438,15 +1391,6 @@ describe 'up.dom', ->
|
|
1438
1391
|
expect(result.value).toMatch(/Could not find selector/i)
|
1439
1392
|
done()
|
1440
1393
|
|
1441
|
-
it "ignores an element that matches the selector but also matches .up-ghost", (done) ->
|
1442
|
-
html = '<div class="foo-bar">text</div>'
|
1443
|
-
affix('.foo-bar.up-ghost')
|
1444
|
-
promise = up.extract('.foo-bar', html)
|
1445
|
-
promiseState(promise).then (result) =>
|
1446
|
-
expect(result.state).toEqual('rejected')
|
1447
|
-
expect(result.value).toMatch(/Could not find selector/i)
|
1448
|
-
done()
|
1449
|
-
|
1450
1394
|
it "ignores an element that matches the selector but also has a parent matching .up-destroying", (done) ->
|
1451
1395
|
html = '<div class="foo-bar">text</div>'
|
1452
1396
|
$parent = affix('.up-destroying')
|
@@ -1457,16 +1401,6 @@ describe 'up.dom', ->
|
|
1457
1401
|
expect(result.value).toMatch(/Could not find selector/i)
|
1458
1402
|
done()
|
1459
1403
|
|
1460
|
-
it "ignores an element that matches the selector but also has a parent matching .up-ghost", (done) ->
|
1461
|
-
html = '<div class="foo-bar">text</div>'
|
1462
|
-
$parent = affix('.up-ghost')
|
1463
|
-
$child = affix('.foo-bar').appendTo($parent)
|
1464
|
-
promise = up.extract('.foo-bar', html)
|
1465
|
-
promiseState(promise).then (result) =>
|
1466
|
-
expect(result.state).toEqual('rejected')
|
1467
|
-
expect(result.value).toMatch(/Could not find selector/i)
|
1468
|
-
done()
|
1469
|
-
|
1470
1404
|
it 'only replaces the first element matching the selector', asyncSpec (next) ->
|
1471
1405
|
html = '<div class="foo-bar">text</div>'
|
1472
1406
|
affix('.foo-bar')
|
@@ -1474,86 +1408,209 @@ describe 'up.dom', ->
|
|
1474
1408
|
up.extract('.foo-bar', html)
|
1475
1409
|
|
1476
1410
|
next =>
|
1477
|
-
elements = $('.foo-bar')
|
1478
|
-
expect($(elements.get(0)).text()).toEqual('text')
|
1479
|
-
expect($(elements.get(1)).text()).toEqual('')
|
1411
|
+
$elements = $('.foo-bar')
|
1412
|
+
expect($($elements.get(0)).text()).toEqual('text')
|
1413
|
+
expect($($elements.get(1)).text()).toEqual('')
|
1414
|
+
|
1415
|
+
it 'focuses an [autofocus] element in the new fragment', asyncSpec (next) ->
|
1416
|
+
affix('.foo-bar')
|
1417
|
+
up.extract '.foo-bar', """
|
1418
|
+
<form class='foo-bar'>
|
1419
|
+
<input class="autofocused-input" autofocus>
|
1420
|
+
</form>
|
1421
|
+
"""
|
1422
|
+
|
1423
|
+
next =>
|
1424
|
+
input = $('.autofocused-input').get(0)
|
1425
|
+
expect(input).toBeGiven()
|
1426
|
+
expect(document.activeElement).toBe(input)
|
1427
|
+
|
1428
|
+
describe 'cleaning up', ->
|
1429
|
+
|
1430
|
+
it 'calls destructors on the old element', asyncSpec (next) ->
|
1431
|
+
destructor = jasmine.createSpy('destructor')
|
1432
|
+
up.compiler '.container', ($element) ->
|
1433
|
+
-> destructor($element.text())
|
1434
|
+
$container = affix('.container').text('old text')
|
1435
|
+
up.hello($container)
|
1436
|
+
up.extract('.container', '<div class="container">new text</div>')
|
1437
|
+
|
1438
|
+
next =>
|
1439
|
+
expect('.container').toHaveText('new text')
|
1440
|
+
expect(destructor).toHaveBeenCalledWith('old text')
|
1441
|
+
|
1442
|
+
it 'calls destructors on the old element after a { transition }', (done) ->
|
1443
|
+
destructor = jasmine.createSpy('destructor')
|
1444
|
+
up.compiler '.container', ($element) ->
|
1445
|
+
-> destructor($element.text())
|
1446
|
+
$container = affix('.container').text('old text')
|
1447
|
+
up.hello($container)
|
1448
|
+
|
1449
|
+
up.extract('.container', '<div class="container">new text</div>', transition: 'cross-fade', duration: 100)
|
1450
|
+
|
1451
|
+
u.setTimer 50, =>
|
1452
|
+
expect(destructor).not.toHaveBeenCalled()
|
1453
|
+
|
1454
|
+
u.setTimer 220, =>
|
1455
|
+
expect('.container').toHaveText('new text')
|
1456
|
+
expect(destructor).toHaveBeenCalledWith('old text')
|
1457
|
+
done()
|
1458
|
+
|
1459
|
+
it 'calls destructors when the replaced element is a singleton element like <body> (bugfix)', asyncSpec (next) ->
|
1460
|
+
# shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
|
1461
|
+
up.motion.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
|
1462
|
+
destructor = jasmine.createSpy('destructor')
|
1463
|
+
up.compiler '.container', -> destructor
|
1464
|
+
$container = affix('.container')
|
1465
|
+
up.hello($container)
|
1466
|
+
up.extract('.container', '<div class="container">new text</div>')
|
1467
|
+
|
1468
|
+
next =>
|
1469
|
+
expect('.container').toHaveText('new text')
|
1470
|
+
expect(destructor).toHaveBeenCalled()
|
1471
|
+
|
1472
|
+
it 'calls destructors while the element is still attached to the DOM, so destructors see ancestry and events bubble up', asyncSpec (next) ->
|
1473
|
+
spy = jasmine.createSpy('parent spy')
|
1474
|
+
up.compiler '.element', ($element) ->
|
1475
|
+
return -> spy($element.text(), $element.parent())
|
1476
|
+
|
1477
|
+
$parent = affix('.parent')
|
1478
|
+
$element = $parent.affix('.element').text('old text')
|
1479
|
+
up.hello($element)
|
1480
|
+
|
1481
|
+
up.extract '.element', '<div class="element">new text</div>'
|
1482
|
+
|
1483
|
+
next =>
|
1484
|
+
expect(spy).toHaveBeenCalledWith('old text', $parent)
|
1485
|
+
|
1486
|
+
it 'calls destructors while the element is still attached to the DOM when also using a { transition }', (done) ->
|
1487
|
+
spy = jasmine.createSpy('parent spy')
|
1488
|
+
up.compiler '.element', ($element) ->
|
1489
|
+
return ->
|
1490
|
+
# We must seek .parent in our ancestry, because our direct parent() is an .up-bounds container
|
1491
|
+
spy($element.text(), $element.closest('.parent'))
|
1492
|
+
|
1493
|
+
$parent = affix('.parent')
|
1494
|
+
$element = $parent.affix('.element').text('old text')
|
1495
|
+
up.hello($element)
|
1496
|
+
|
1497
|
+
extractDone = up.extract('.element', '<div class="element">new text</div>', transition: 'cross-fade', duration: 30)
|
1498
|
+
|
1499
|
+
extractDone.then ->
|
1500
|
+
expect(spy).toHaveBeenCalledWith('old text', $parent)
|
1501
|
+
done()
|
1480
1502
|
|
1481
1503
|
describe 'with { transition } option', ->
|
1482
1504
|
|
1483
1505
|
it 'morphs between the old and new element', asyncSpec (next) ->
|
1484
|
-
affix('.element').text('version 1')
|
1485
|
-
up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration:
|
1506
|
+
affix('.element.v1').text('version 1')
|
1507
|
+
up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 100, easing: 'linear')
|
1508
|
+
|
1509
|
+
$old = undefined
|
1510
|
+
$new = undefined
|
1486
1511
|
|
1487
1512
|
next =>
|
1488
|
-
|
1489
|
-
|
1490
|
-
expect(u.opacity(@$ghost1)).toBeAround(1.0, 0.1)
|
1513
|
+
$old = $('.element.v1')
|
1514
|
+
$new = $('.element.v2')
|
1491
1515
|
|
1492
|
-
|
1493
|
-
expect(
|
1494
|
-
expect(u.opacity(@$ghost2)).toBeAround(0.0, 0.1)
|
1516
|
+
expect($old).toHaveLength(1)
|
1517
|
+
expect(u.opacity($old)).toBeAround(1.0, 0.2)
|
1495
1518
|
|
1496
|
-
|
1497
|
-
expect(u.opacity(
|
1498
|
-
expect(u.opacity(@$ghost2)).toBeAround(1.0, 0.3)
|
1519
|
+
expect($new).toHaveLength(1)
|
1520
|
+
expect(u.opacity($new)).toBeAround(0.0, 0.2)
|
1499
1521
|
|
1500
|
-
|
1522
|
+
next.after 50, =>
|
1523
|
+
expect(u.opacity($old)).toBeAround(0.5, 0.2)
|
1524
|
+
expect(u.opacity($new)).toBeAround(0.5, 0.2)
|
1525
|
+
|
1526
|
+
next.after 60, =>
|
1527
|
+
expect(u.opacity($new)).toBeAround(1.0, 0.1)
|
1528
|
+
expect($old).toBeDetached()
|
1529
|
+
|
1530
|
+
|
1531
|
+
it 'ignores a { transition } option when replacing a singleton element like <body>', asyncSpec (next) ->
|
1532
|
+
# shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
|
1533
|
+
up.motion.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
|
1534
|
+
|
1535
|
+
affix('.container').text('old text')
|
1536
|
+
|
1537
|
+
extractDone = jasmine.createSpy()
|
1538
|
+
promise = up.extract('.container', '<div class="container">new text</div>', transition: 'cross-fade', duration: 200)
|
1539
|
+
promise.then(extractDone)
|
1540
|
+
|
1541
|
+
next =>
|
1542
|
+
# See that we've already immediately swapped the element and ignored the duration of 200ms
|
1543
|
+
expect(extractDone).toHaveBeenCalled()
|
1544
|
+
expect($('.container').length).toEqual(1)
|
1545
|
+
expect(u.opacity($('.container'))).toEqual(1.0)
|
1546
|
+
|
1547
|
+
|
1548
|
+
it 'marks the old fragment as .up-destroying during the transition', asyncSpec (next) ->
|
1501
1549
|
affix('.element').text('version 1')
|
1502
1550
|
up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
|
1503
1551
|
|
1504
1552
|
next =>
|
1505
|
-
$version1 = $('.element:
|
1506
|
-
$version1Ghost = $('.element.up-ghost:contains("version 1")')
|
1553
|
+
$version1 = $('.element:contains("version 1")')
|
1507
1554
|
expect($version1).toHaveLength(1)
|
1508
|
-
expect($version1Ghost).toHaveLength(1)
|
1509
1555
|
expect($version1).toHaveClass('up-destroying')
|
1510
|
-
expect($version1Ghost).toHaveClass('up-destroying')
|
1511
1556
|
|
1512
|
-
$version2 = $('.element:
|
1513
|
-
$version2Ghost = $('.element.up-ghost:contains("version 2")')
|
1557
|
+
$version2 = $('.element:contains("version 2")')
|
1514
1558
|
expect($version2).toHaveLength(1)
|
1515
|
-
expect($version2Ghost).toHaveLength(1)
|
1516
1559
|
expect($version2).not.toHaveClass('up-destroying')
|
1517
|
-
expect($version2Ghost).not.toHaveClass('up-destroying')
|
1518
1560
|
|
1519
1561
|
it 'cancels an existing transition by instantly jumping to the last frame', asyncSpec (next) ->
|
1520
|
-
affix('.element').text('version 1')
|
1521
|
-
|
1562
|
+
affix('.element.v1').text('version 1')
|
1563
|
+
|
1564
|
+
up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 200)
|
1522
1565
|
|
1523
1566
|
next =>
|
1524
|
-
$ghost1 = $('.element
|
1567
|
+
$ghost1 = $('.element:contains("version 1")')
|
1525
1568
|
expect($ghost1).toHaveLength(1)
|
1526
1569
|
expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
|
1527
1570
|
|
1528
|
-
$ghost2 = $('.element
|
1571
|
+
$ghost2 = $('.element:contains("version 2")')
|
1529
1572
|
expect($ghost2).toHaveLength(1)
|
1530
1573
|
expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
|
1531
1574
|
|
1532
1575
|
next =>
|
1533
|
-
up.extract('.element', '<div class="element">version 3</div>', transition: 'cross-fade', duration: 200)
|
1576
|
+
up.extract('.element', '<div class="element v3">version 3</div>', transition: 'cross-fade', duration: 200)
|
1534
1577
|
|
1535
1578
|
next =>
|
1536
|
-
$ghost1 = $('.element
|
1579
|
+
$ghost1 = $('.element:contains("version 1")')
|
1537
1580
|
expect($ghost1).toHaveLength(0)
|
1538
1581
|
|
1539
|
-
$ghost2 = $('.element
|
1582
|
+
$ghost2 = $('.element:contains("version 2")')
|
1540
1583
|
expect($ghost2).toHaveLength(1)
|
1541
1584
|
expect($ghost2.css('opacity')).toBeAround(1.0, 0.1)
|
1542
1585
|
|
1543
|
-
$ghost3 = $('.element
|
1586
|
+
$ghost3 = $('.element:contains("version 3")')
|
1544
1587
|
expect($ghost3).toHaveLength(1)
|
1545
1588
|
expect($ghost3.css('opacity')).toBeAround(0.0, 0.1)
|
1546
1589
|
|
1547
1590
|
it 'delays the resolution of the returned promise until the transition is over', (done) ->
|
1548
1591
|
affix('.element').text('version 1')
|
1549
1592
|
resolution = jasmine.createSpy()
|
1550
|
-
promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration:
|
1593
|
+
promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 60)
|
1551
1594
|
promise.then(resolution)
|
1552
1595
|
expect(resolution).not.toHaveBeenCalled()
|
1553
|
-
|
1596
|
+
|
1597
|
+
u.setTimer 40, ->
|
1598
|
+
expect(resolution).not.toHaveBeenCalled()
|
1599
|
+
|
1600
|
+
u.setTimer 200, ->
|
1554
1601
|
expect(resolution).toHaveBeenCalled()
|
1555
1602
|
done()
|
1556
1603
|
|
1604
|
+
it 'attaches the new element to the DOM before compilers are called, so they can see their parents and trigger bubbling events', asyncSpec (next)->
|
1605
|
+
$parent = affix('.parent')
|
1606
|
+
$element = $parent.affix('.element').text('old text')
|
1607
|
+
spy = jasmine.createSpy('parent spy')
|
1608
|
+
up.compiler '.element', ($element) -> spy($element.text(), $element.parent())
|
1609
|
+
up.extract '.element', '<div class="element">new text</div>', transition: 'cross-fade', duration: 50
|
1610
|
+
|
1611
|
+
next =>
|
1612
|
+
expect(spy).toHaveBeenCalledWith('new text', $parent)
|
1613
|
+
|
1557
1614
|
describe 'when animation is disabled', ->
|
1558
1615
|
|
1559
1616
|
beforeEach ->
|
@@ -1567,7 +1624,7 @@ describe 'up.dom', ->
|
|
1567
1624
|
expect($('.up-ghost')).toHaveLength(0)
|
1568
1625
|
|
1569
1626
|
it "replaces the elements directly, since first inserting and then removing would shift scroll positions", asyncSpec (next) ->
|
1570
|
-
swapDirectlySpy = up.
|
1627
|
+
swapDirectlySpy = up.motion.knife.mock('swapElementsDirectly')
|
1571
1628
|
affix('.element').text('version 1')
|
1572
1629
|
up.extract('.element', '<div class="element">version 2</div>', transition: false)
|
1573
1630
|
|
@@ -1896,7 +1953,7 @@ describe 'up.dom', ->
|
|
1896
1953
|
expect(insertedListener).toHaveBeenCalledWith(jasmine.anything(), $('.container'), jasmine.anything())
|
1897
1954
|
expect(keptListener).toHaveBeenCalledWith(jasmine.anything(), $('.container .keeper'), jasmine.anything())
|
1898
1955
|
|
1899
|
-
it 'emits an up:fragment:kept event on a kept element with a newData property corresponding to the up-data attribute value of the discarded element', (next) ->
|
1956
|
+
it 'emits an up:fragment:kept event on a kept element with a newData property corresponding to the up-data attribute value of the discarded element', asyncSpec (next) ->
|
1900
1957
|
keptListener = jasmine.createSpy()
|
1901
1958
|
up.on 'up:fragment:kept', (event) -> keptListener(event.$element, event.newData)
|
1902
1959
|
$container = affix('.container')
|
@@ -2009,6 +2066,64 @@ describe 'up.dom', ->
|
|
2009
2066
|
expect(document.title).toEqual('Title from options')
|
2010
2067
|
done()
|
2011
2068
|
|
2069
|
+
it 'runs an animation before removal with { animate } option', asyncSpec (next) ->
|
2070
|
+
$element = affix('.element')
|
2071
|
+
up.destroy($element, animation: 'fade-out', duration: 150, easing: 'linear')
|
2072
|
+
|
2073
|
+
next ->
|
2074
|
+
expect($element).toHaveOpacity(1.0, 0.2)
|
2075
|
+
|
2076
|
+
next.after 75, ->
|
2077
|
+
expect($element).toHaveOpacity(0.5, 0.2)
|
2078
|
+
|
2079
|
+
next.after (75 + 20), ->
|
2080
|
+
expect($element).toBeDetached()
|
2081
|
+
|
2082
|
+
it 'marks the element as .up-destroying while it is animating', asyncSpec (next) ->
|
2083
|
+
$element = affix('.element')
|
2084
|
+
up.destroy($element, animation: 'fade-out', duration: 80, easing: 'linear')
|
2085
|
+
|
2086
|
+
next ->
|
2087
|
+
expect($element).toHaveClass('up-destroying')
|
2088
|
+
|
2089
|
+
it 'emits an up:fragment:destroy event while the element is still in the DOM', asyncSpec (next) ->
|
2090
|
+
$element = affix('.element')
|
2091
|
+
expect($element).toBeAttached()
|
2092
|
+
|
2093
|
+
listener = jasmine.createSpy('event listener')
|
2094
|
+
$element.on('up:fragment:destroy', listener)
|
2095
|
+
|
2096
|
+
destroyDone = up.destroy($element, animation: 'fade-out', duration: 30)
|
2097
|
+
|
2098
|
+
next ->
|
2099
|
+
expect(listener).toHaveBeenCalledWith(jasmine.objectContaining($element: $element))
|
2100
|
+
expect($element).toBeAttached()
|
2101
|
+
|
2102
|
+
next.await(destroyDone)
|
2103
|
+
|
2104
|
+
next ->
|
2105
|
+
expect($element).toBeDetached()
|
2106
|
+
|
2107
|
+
it 'emits an up:fragment:destroyed event on the former parent element after the element has been removed from the DOM', asyncSpec (next) ->
|
2108
|
+
$parent = affix('.parent')
|
2109
|
+
$element = $parent.affix('.element')
|
2110
|
+
expect($element).toBeAttached()
|
2111
|
+
|
2112
|
+
listener = jasmine.createSpy('event listener')
|
2113
|
+
|
2114
|
+
$parent.on('up:fragment:destroyed', listener)
|
2115
|
+
|
2116
|
+
destroyDone = up.destroy($element, animation: 'fade-out', duration: 30)
|
2117
|
+
|
2118
|
+
next ->
|
2119
|
+
expect(listener).not.toHaveBeenCalled()
|
2120
|
+
expect($element).toBeAttached()
|
2121
|
+
|
2122
|
+
next.await(destroyDone)
|
2123
|
+
|
2124
|
+
next ->
|
2125
|
+
expect(listener).toHaveBeenCalledWith(jasmine.objectContaining($element: $element, $parent: $parent))
|
2126
|
+
expect($element).toBeDetached()
|
2012
2127
|
|
2013
2128
|
describe 'up.reload', ->
|
2014
2129
|
|