unpoly-rails 0.20.0 → 0.21.0
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.
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
|