unpoly-rails 0.20.0 → 0.21.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 +28 -0
- data/README.md +1 -1
- data/dist/unpoly.js +195 -94
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly/form.js.coffee +23 -24
- data/lib/assets/javascripts/unpoly/layout.js.coffee +16 -0
- data/lib/assets/javascripts/unpoly/link.js.coffee +3 -2
- data/lib/assets/javascripts/unpoly/log.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/motion.js.coffee +17 -19
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +83 -23
- data/lib/assets/javascripts/unpoly/util.js.coffee +18 -7
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +83 -4
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +8 -8
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +47 -0
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +76 -0
- metadata +2 -2
data/lib/unpoly/rails/version.rb
CHANGED
@@ -32,12 +32,21 @@ describe 'up.flow', ->
|
|
32
32
|
expect($('.after')).toHaveText('old-after')
|
33
33
|
done()
|
34
34
|
|
35
|
-
it 'sends an X-Up-Target HTTP
|
35
|
+
it 'sends an X-Up-Target HTTP header along with the request', ->
|
36
36
|
up.replace('.middle', '/path')
|
37
37
|
request = @lastRequest()
|
38
|
-
console.log(request.requestHeaders)
|
39
38
|
expect(request.requestHeaders['X-Up-Target']).toEqual('.middle')
|
40
39
|
|
40
|
+
it 'returns a promise that will be resolved once the server response was received and the fragments were swapped', ->
|
41
|
+
resolution = jasmine.createSpy()
|
42
|
+
promise = up.replace('.middle', '/path')
|
43
|
+
promise.then(resolution)
|
44
|
+
expect(resolution).not.toHaveBeenCalled()
|
45
|
+
expect($('.middle')).toHaveText('old-middle')
|
46
|
+
@respond()
|
47
|
+
expect(resolution).toHaveBeenCalled()
|
48
|
+
expect($('.middle')).toHaveText('new-middle')
|
49
|
+
|
41
50
|
describe 'with { data } option', ->
|
42
51
|
|
43
52
|
it "uses the given params as a non-GET request's payload", ->
|
@@ -457,6 +466,78 @@ describe 'up.flow', ->
|
|
457
466
|
expect($(elements.get(0)).text()).toEqual('text')
|
458
467
|
expect($(elements.get(1)).text()).toEqual('')
|
459
468
|
|
469
|
+
describe 'with { transition } option', ->
|
470
|
+
|
471
|
+
it 'morphs between the old and new element', (done) ->
|
472
|
+
affix('.element').text('version 1')
|
473
|
+
up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 50)
|
474
|
+
|
475
|
+
$ghost1 = $('.element.up-ghost:contains("version 1")')
|
476
|
+
expect($ghost1).toHaveLength(1)
|
477
|
+
expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
|
478
|
+
|
479
|
+
$ghost2 = $('.element.up-ghost:contains("version 2")')
|
480
|
+
expect($ghost2).toHaveLength(1)
|
481
|
+
expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
|
482
|
+
|
483
|
+
@setTimer 40, ->
|
484
|
+
expect($ghost1.css('opacity')).toBeAround(0.0, 0.2)
|
485
|
+
expect($ghost2.css('opacity')).toBeAround(1.0, 0.2)
|
486
|
+
done()
|
487
|
+
|
488
|
+
it 'marks the old fragment and its ghost as .up-destroying during the transition', ->
|
489
|
+
affix('.element').text('version 1')
|
490
|
+
up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
|
491
|
+
|
492
|
+
$version1 = $('.element:not(.up-ghost):contains("version 1")')
|
493
|
+
$version1Ghost = $('.element.up-ghost:contains("version 1")')
|
494
|
+
expect($version1).toHaveLength(1)
|
495
|
+
expect($version1Ghost).toHaveLength(1)
|
496
|
+
expect($version1).toHaveClass('up-destroying')
|
497
|
+
expect($version1Ghost).toHaveClass('up-destroying')
|
498
|
+
|
499
|
+
$version2 = $('.element:not(.up-ghost):contains("version 2")')
|
500
|
+
$version2Ghost = $('.element.up-ghost:contains("version 2")')
|
501
|
+
expect($version2).toHaveLength(1)
|
502
|
+
expect($version2Ghost).toHaveLength(1)
|
503
|
+
expect($version2).not.toHaveClass('up-destroying')
|
504
|
+
expect($version2Ghost).not.toHaveClass('up-destroying')
|
505
|
+
|
506
|
+
it 'cancels an existing transition by instantly jumping to the last frame', ->
|
507
|
+
affix('.element').text('version 1')
|
508
|
+
up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
|
509
|
+
|
510
|
+
$ghost1 = $('.element.up-ghost:contains("version 1")')
|
511
|
+
expect($ghost1).toHaveLength(1)
|
512
|
+
expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
|
513
|
+
|
514
|
+
$ghost2 = $('.element.up-ghost:contains("version 2")')
|
515
|
+
expect($ghost2).toHaveLength(1)
|
516
|
+
expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
|
517
|
+
|
518
|
+
up.extract('.element', '<div class="element">version 3</div>', transition: 'cross-fade', duration: 200)
|
519
|
+
|
520
|
+
$ghost1 = $('.element.up-ghost:contains("version 1")')
|
521
|
+
expect($ghost1).toHaveLength(0)
|
522
|
+
|
523
|
+
$ghost2 = $('.element.up-ghost:contains("version 2")')
|
524
|
+
expect($ghost2).toHaveLength(1)
|
525
|
+
expect($ghost2.css('opacity')).toBeAround(1.0, 0.1)
|
526
|
+
|
527
|
+
$ghost3 = $('.element.up-ghost:contains("version 3")')
|
528
|
+
expect($ghost3).toHaveLength(1)
|
529
|
+
expect($ghost3.css('opacity')).toBeAround(0.0, 0.1)
|
530
|
+
|
531
|
+
it 'delays the resolution of the returned promise until the transition is over', (done) ->
|
532
|
+
affix('.element').text('version 1')
|
533
|
+
resolution = jasmine.createSpy()
|
534
|
+
promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 30)
|
535
|
+
promise.then(resolution)
|
536
|
+
expect(resolution).not.toHaveBeenCalled()
|
537
|
+
@setTimer 50, ->
|
538
|
+
expect(resolution).toHaveBeenCalled()
|
539
|
+
done()
|
540
|
+
|
460
541
|
describe 'handling of [up-keep] elements', ->
|
461
542
|
|
462
543
|
squish = (string) ->
|
@@ -509,8 +590,6 @@ describe 'up.flow', ->
|
|
509
590
|
up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
|
510
591
|
expect($('.keeper')).toHaveText('old-inside')
|
511
592
|
|
512
|
-
it "does not compile the kept element a second time"
|
513
|
-
|
514
593
|
it "only emits an event up:fragment:kept, but not an event up:fragment:inserted", ->
|
515
594
|
insertedListener = jasmine.createSpy('subscriber to up:fragment:inserted')
|
516
595
|
up.on('up:fragment:inserted', insertedListener)
|
@@ -282,12 +282,12 @@ describe 'up.form', ->
|
|
282
282
|
expect($labels[0]).not.toHaveText('Validation message')
|
283
283
|
expect($labels[1]).toHaveText('Validation message')
|
284
284
|
|
285
|
-
describe '[up-
|
285
|
+
describe '[up-switch]', ->
|
286
286
|
|
287
287
|
describe 'on a select', ->
|
288
288
|
|
289
289
|
beforeEach ->
|
290
|
-
@$select = affix('select[up-
|
290
|
+
@$select = affix('select[up-switch=".target"]')
|
291
291
|
@$blankOption = @$select.affix('option').text('<Please select something>').val('')
|
292
292
|
@$fooOption = @$select.affix('option[value="foo"]').text('Foo')
|
293
293
|
@$barOption = @$select.affix('option[value="bar"]').text('Bar')
|
@@ -331,7 +331,7 @@ describe 'up.form', ->
|
|
331
331
|
describe 'on a checkbox', ->
|
332
332
|
|
333
333
|
beforeEach ->
|
334
|
-
@$checkbox = affix('input[type="checkbox"][value="1"][up-
|
334
|
+
@$checkbox = affix('input[type="checkbox"][value="1"][up-switch=".target"]')
|
335
335
|
|
336
336
|
it "shows the target element iff its up-show-for attribute is :checked and the checkbox is checked", ->
|
337
337
|
$target = affix('.target[up-show-for=":checked"]')
|
@@ -372,10 +372,10 @@ describe 'up.form', ->
|
|
372
372
|
|
373
373
|
beforeEach ->
|
374
374
|
@$buttons = affix('.radio-buttons')
|
375
|
-
@$blankButton = @$buttons.affix('input[type="radio"][name="group"][up-
|
376
|
-
@$fooButton = @$buttons.affix('input[type="radio"][name="group"][up-
|
377
|
-
@$barButton = @$buttons.affix('input[type="radio"][name="group"][up-
|
378
|
-
@$bazkButton = @$buttons.affix('input[type="radio"][name="group"][up-
|
375
|
+
@$blankButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('')
|
376
|
+
@$fooButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('foo')
|
377
|
+
@$barButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('bar')
|
378
|
+
@$bazkButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('baz')
|
379
379
|
|
380
380
|
it "shows the target element iff its up-show-for attribute contains the selected button value", ->
|
381
381
|
$target = affix('.target[up-show-for="something bar other"]')
|
@@ -433,7 +433,7 @@ describe 'up.form', ->
|
|
433
433
|
describe 'on a text input', ->
|
434
434
|
|
435
435
|
beforeEach ->
|
436
|
-
@$textInput = affix('input[type="text"][up-
|
436
|
+
@$textInput = affix('input[type="text"][up-switch=".target"]')
|
437
437
|
|
438
438
|
it "shows the target element iff its up-show-for attribute contains the input value", ->
|
439
439
|
$target = affix('.target[up-show-for="something bar other"]')
|
@@ -92,6 +92,53 @@ describe 'up.syntax', ->
|
|
92
92
|
expect($baz.attr('up-keep')).toBeMissing()
|
93
93
|
expect($bam.attr('up-keep')).toEqual('.partner')
|
94
94
|
|
95
|
+
describe 'with { priority } option', ->
|
96
|
+
|
97
|
+
it 'runs compilers with higher priority first', ->
|
98
|
+
traces = []
|
99
|
+
up.compiler '.element', { priority: 1 }, -> traces.push('foo')
|
100
|
+
up.compiler '.element', { priority: 2 }, -> traces.push('bar')
|
101
|
+
up.compiler '.element', { priority: 0 }, -> traces.push('baz')
|
102
|
+
up.compiler '.element', { priority: 3 }, -> traces.push('bam')
|
103
|
+
up.compiler '.element', { priority: -1 }, -> traces.push('qux')
|
104
|
+
up.hello(affix('.element'))
|
105
|
+
expect(traces).toEqual ['qux', 'baz', 'foo', 'bar', 'bam']
|
106
|
+
|
107
|
+
it 'considers priority-less compilers to be priority zero', ->
|
108
|
+
traces = []
|
109
|
+
up.compiler '.element', { priority: 1 }, -> traces.push('foo')
|
110
|
+
up.compiler '.element', -> traces.push('bar')
|
111
|
+
up.compiler '.element', { priority: -1 }, -> traces.push('baz')
|
112
|
+
up.hello(affix('.element'))
|
113
|
+
expect(traces).toEqual ['baz', 'bar', 'foo']
|
114
|
+
|
115
|
+
it 'runs two compilers with the same priority in the order in which they were registered', ->
|
116
|
+
traces = []
|
117
|
+
up.compiler '.element', { priority: 1 }, -> traces.push('foo')
|
118
|
+
up.compiler '.element', { priority: 1 }, -> traces.push('bar')
|
119
|
+
up.hello(affix('.element'))
|
120
|
+
expect(traces).toEqual ['foo', 'bar']
|
121
|
+
|
122
|
+
describe 'up.macro', ->
|
123
|
+
|
124
|
+
it 'registers compilers that are run before other compilers', ->
|
125
|
+
traces = []
|
126
|
+
up.compiler '.element', { priority: 10 }, -> traces.push('foo')
|
127
|
+
up.compiler '.element', { priority: -1000 }, -> traces.push('bar')
|
128
|
+
up.macro '.element', -> traces.push('baz')
|
129
|
+
up.hello(affix('.element'))
|
130
|
+
expect(traces).toEqual ['baz', 'bar' , 'foo']
|
131
|
+
|
132
|
+
it 'allows to macros to have priorities of their own', ->
|
133
|
+
traces = []
|
134
|
+
up.macro '.element', { priority: 1 }, -> traces.push('foo')
|
135
|
+
up.macro '.element', { priority: 2 }, -> traces.push('bar')
|
136
|
+
up.macro '.element', { priority: 0 }, -> traces.push('baz')
|
137
|
+
up.macro '.element', { priority: 3 }, -> traces.push('bam')
|
138
|
+
up.macro '.element', { priority: -1 }, -> traces.push('qux')
|
139
|
+
up.hello(affix('.element'))
|
140
|
+
expect(traces).toEqual ['qux', 'baz', 'foo', 'bar', 'bam']
|
141
|
+
|
95
142
|
describe 'up.hello', ->
|
96
143
|
|
97
144
|
it 'should have tests'
|
@@ -245,6 +245,82 @@ describe 'up.util', ->
|
|
245
245
|
string = up.util.requestDataAsQuery({ 'my+key': 'my+value' })
|
246
246
|
expect(string).toEqual('my%2Bkey=my%2Bvalue')
|
247
247
|
|
248
|
+
describe 'up.util.resolvableWhen', ->
|
249
|
+
|
250
|
+
it 'returns a promise that is resolved when all the given deferreds are resolved', ->
|
251
|
+
one = jasmine.createSpy()
|
252
|
+
two = jasmine.createSpy()
|
253
|
+
both = jasmine.createSpy()
|
254
|
+
oneDeferred = $.Deferred()
|
255
|
+
oneDeferred.then(one)
|
256
|
+
twoDeferred = $.Deferred()
|
257
|
+
twoDeferred.then(two)
|
258
|
+
|
259
|
+
bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
|
260
|
+
bothDeferred.then(both)
|
261
|
+
|
262
|
+
expect(one).not.toHaveBeenCalled()
|
263
|
+
expect(two).not.toHaveBeenCalled()
|
264
|
+
expect(both).not.toHaveBeenCalled()
|
265
|
+
|
266
|
+
oneDeferred.resolve()
|
267
|
+
expect(one).toHaveBeenCalled()
|
268
|
+
expect(two).not.toHaveBeenCalled()
|
269
|
+
expect(both).not.toHaveBeenCalled()
|
270
|
+
|
271
|
+
twoDeferred.resolve()
|
272
|
+
expect(one).toHaveBeenCalled()
|
273
|
+
expect(two).toHaveBeenCalled()
|
274
|
+
expect(both).toHaveBeenCalled()
|
275
|
+
|
276
|
+
it 'returns a promise with a .resolve method that resolves the given deferreds', ->
|
277
|
+
one = jasmine.createSpy()
|
278
|
+
two = jasmine.createSpy()
|
279
|
+
both = jasmine.createSpy()
|
280
|
+
oneDeferred = $.Deferred()
|
281
|
+
oneDeferred.then(one)
|
282
|
+
twoDeferred = $.Deferred()
|
283
|
+
twoDeferred.then(two)
|
284
|
+
|
285
|
+
bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
|
286
|
+
bothDeferred.then(both)
|
287
|
+
|
288
|
+
expect(one).not.toHaveBeenCalled()
|
289
|
+
expect(two).not.toHaveBeenCalled()
|
290
|
+
expect(both).not.toHaveBeenCalled()
|
291
|
+
|
292
|
+
bothDeferred.resolve()
|
293
|
+
expect(one).toHaveBeenCalled()
|
294
|
+
expect(two).toHaveBeenCalled()
|
295
|
+
expect(both).toHaveBeenCalled()
|
296
|
+
|
297
|
+
it 'does not resolve the given deferreds more than once', ->
|
298
|
+
oneDeferred = $.Deferred()
|
299
|
+
spyOn(oneDeferred, 'resolve')
|
300
|
+
bothDeferred = up.util.resolvableWhen(oneDeferred)
|
301
|
+
|
302
|
+
bothDeferred.resolve()
|
303
|
+
bothDeferred.resolve()
|
304
|
+
|
305
|
+
expect(oneDeferred.resolve.calls.count()).toEqual(1)
|
306
|
+
|
307
|
+
describe 'bugfix against troublesome jQuery optimization if only one deferred is given', ->
|
308
|
+
|
309
|
+
it 'does not simply return the given deferred', ->
|
310
|
+
oneDeferred = $.Deferred()
|
311
|
+
whenDeferred = up.util.resolvableWhen(oneDeferred)
|
312
|
+
# This is what $.when returns if only passed a single argument
|
313
|
+
expect(whenDeferred).not.toBe(oneDeferred.promise())
|
314
|
+
# Cover eventual implementations
|
315
|
+
expect(whenDeferred).not.toBe(oneDeferred)
|
316
|
+
expect(whenDeferred.promise()).not.toBe(oneDeferred.promise())
|
317
|
+
expect(whenDeferred.promise()).not.toBe(oneDeferred)
|
318
|
+
|
319
|
+
it 'does not create an infinite loop if the given deferred is nested twice and the first nesting is resolved', ->
|
320
|
+
oneDeferred = $.Deferred()
|
321
|
+
firstNesting = up.util.resolvableWhen(oneDeferred)
|
322
|
+
secondNesting = up.util.resolvableWhen(firstNesting)
|
323
|
+
expect(-> firstNesting.resolve()).not.toThrowError()
|
248
324
|
|
249
325
|
describe 'up.util.requestDataAsArray', ->
|
250
326
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unpoly-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henning Koch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|