unpoly-rails 0.27.1 → 0.27.2
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 +18 -0
- data/dist/unpoly.js +106 -24
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly/flow.js.coffee +58 -19
- data/lib/assets/javascripts/unpoly/form.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/link.js.coffee +16 -1
- data/lib/assets/javascripts/unpoly/modal.js.coffee +8 -1
- data/lib/assets/javascripts/unpoly/popup.js.coffee +8 -1
- data/lib/assets/javascripts/unpoly/util.js.coffee +2 -2
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/spec/javascripts/helpers/to_be_jquery.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +24 -0
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +17 -0
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +25 -6
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +58 -0
- metadata +3 -2
@@ -126,6 +126,7 @@ up.form = (($) ->
|
|
126
126
|
options.cache = u.option(options.cache, u.castedAttr($form, 'up-cache'))
|
127
127
|
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($form, 'up-restore-scroll'))
|
128
128
|
options.origin = u.option(options.origin, $form)
|
129
|
+
options.layer = u.option(options.layer, $form.attr('up-layer'), 'auto')
|
129
130
|
options.data = up.util.requestDataFromForm($form)
|
130
131
|
options = u.merge(options, up.motion.animateOptions(options, $form))
|
131
132
|
|
@@ -158,6 +158,13 @@ up.link = (($) ->
|
|
158
158
|
@param {Object} [options.headers={}]
|
159
159
|
An object of additional header key/value pairs to send along
|
160
160
|
with the request.
|
161
|
+
@param {String} [options.layer='auto']
|
162
|
+
The name of the layer that ought to be updated. Valid values are
|
163
|
+
`auto`, `page`, `modal` and `popup`.
|
164
|
+
|
165
|
+
If set to `auto` (default), Unpoly will try to find a match in the
|
166
|
+
same layer as the given link. If no match was found in that layer,
|
167
|
+
Unpoly will search in other layers, starting from the topmost layer.
|
161
168
|
@return {Promise}
|
162
169
|
A promise that will be resolved when the link destination
|
163
170
|
has been loaded and rendered.
|
@@ -179,6 +186,7 @@ up.link = (($) ->
|
|
179
186
|
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($link, 'up-restore-scroll'))
|
180
187
|
options.method = followMethod($link, options)
|
181
188
|
options.origin = u.option(options.origin, $link)
|
189
|
+
options.layer = u.option(options.layer, $link.attr('up-layer'), 'auto')
|
182
190
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
183
191
|
options = u.merge(options, up.motion.animateOptions(options, $link))
|
184
192
|
|
@@ -330,7 +338,14 @@ up.link = (($) ->
|
|
330
338
|
@param {String} [up-cache]
|
331
339
|
Whether to force the use of a cached response (`true`)
|
332
340
|
or never use the cache (`false`)
|
333
|
-
or make an educated guess (
|
341
|
+
or make an educated guess (default).
|
342
|
+
@param {String} [up-layer='auto']
|
343
|
+
The name of the layer that ought to be updated. Valid values are
|
344
|
+
`auto`, `page`, `modal` and `popup`.
|
345
|
+
|
346
|
+
If set to `auto` (default), Unpoly will try to find a match in the
|
347
|
+
same layer as the given link. If no match was found in that layer,
|
348
|
+
Unpoly will search in other layers, starting from the topmost layer.
|
334
349
|
@param [up-history]
|
335
350
|
Whether to push an entry to the browser history when following the link.
|
336
351
|
|
@@ -189,6 +189,10 @@ up.modal = (($) ->
|
|
189
189
|
else
|
190
190
|
template
|
191
191
|
|
192
|
+
discardHistory = ->
|
193
|
+
state.coveredTitle = null
|
194
|
+
state.coveredUrl = null
|
195
|
+
|
192
196
|
createFrame = (target, options) ->
|
193
197
|
$modal = $(templateHtml())
|
194
198
|
$modal.attr('up-flavor', state.flavor)
|
@@ -380,6 +384,7 @@ up.modal = (($) ->
|
|
380
384
|
options.sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'), flavorDefault('sticky', options.flavor))
|
381
385
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
382
386
|
options.method = up.link.followMethod($link, options)
|
387
|
+
options.layer = 'modal'
|
383
388
|
animateOptions = up.motion.animateOptions(options, $link, duration: flavorDefault('openDuration', options.flavor), easing: flavorDefault('openEasing', options.flavor))
|
384
389
|
|
385
390
|
# Although we usually fall back to full page loads if a browser doesn't support pushState,
|
@@ -518,7 +523,9 @@ up.modal = (($) ->
|
|
518
523
|
###
|
519
524
|
|
520
525
|
autoclose = ->
|
521
|
-
|
526
|
+
unless state.sticky
|
527
|
+
discardHistory()
|
528
|
+
closeAsap()
|
522
529
|
|
523
530
|
###*
|
524
531
|
Returns whether the given element or selector is contained
|
@@ -149,6 +149,10 @@ up.popup = (($) ->
|
|
149
149
|
state.$popup.attr('up-position', state.position)
|
150
150
|
state.$popup.css(css)
|
151
151
|
|
152
|
+
discardHistory = ->
|
153
|
+
state.coveredTitle = null
|
154
|
+
state.coveredUrl = null
|
155
|
+
|
152
156
|
createFrame = (target) ->
|
153
157
|
$popup = u.$createElementFromSelector('.up-popup')
|
154
158
|
# Create an empty element that will match the
|
@@ -224,6 +228,7 @@ up.popup = (($) ->
|
|
224
228
|
options.history = if up.browser.canPushState() then u.option(options.history, u.castedAttr($anchor, 'up-history'), config.history) else false
|
225
229
|
options.confirm = u.option(options.confirm, $anchor.attr('up-confirm'))
|
226
230
|
options.method = up.link.followMethod($anchor, options)
|
231
|
+
options.layer = 'popup'
|
227
232
|
animateOptions = up.motion.animateOptions(options, $anchor, duration: config.openDuration, easing: config.openEasing)
|
228
233
|
|
229
234
|
up.browser.whenConfirmed(options).then ->
|
@@ -328,7 +333,9 @@ up.popup = (($) ->
|
|
328
333
|
###
|
329
334
|
|
330
335
|
autoclose = ->
|
331
|
-
|
336
|
+
unless state.sticky
|
337
|
+
discardHistory()
|
338
|
+
closeAsap()
|
332
339
|
|
333
340
|
###*
|
334
341
|
Returns whether the given element or selector is contained
|
@@ -199,7 +199,7 @@ up.util = (($) ->
|
|
199
199
|
|
200
200
|
openTag = (tag) -> "<#{tag}(?: [^>]*)?>"
|
201
201
|
closeTag = (tag) -> "</#{tag}>"
|
202
|
-
anything = '(?:.|\\
|
202
|
+
anything = '(?:.|\\s)*?'
|
203
203
|
capture = (pattern) -> "(#{pattern})"
|
204
204
|
|
205
205
|
titlePattern = new RegExp(
|
@@ -618,7 +618,7 @@ up.util = (($) ->
|
|
618
618
|
merged
|
619
619
|
|
620
620
|
###*
|
621
|
-
Returns the first argument that is considered
|
621
|
+
Returns the first argument that is considered [given](/up.util.isGiven).
|
622
622
|
|
623
623
|
This function is useful when you have multiple option sources and the value can be boolean.
|
624
624
|
In that case you cannot change the sources with a `||` operator
|
data/lib/unpoly/rails/version.rb
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -566,7 +566,7 @@ describe 'up.flow', ->
|
|
566
566
|
it "throws an error if the selector can't be found on the current page", ->
|
567
567
|
html = '<div class="foo-bar">text</div>'
|
568
568
|
extract = -> up.extract('.foo-bar', html)
|
569
|
-
expect(extract).toThrowError(/Could not find selector ".foo-bar"
|
569
|
+
expect(extract).toThrowError(/Could not find selector ".foo-bar"/i)
|
570
570
|
|
571
571
|
it "throws an error if the selector can't be found in the given HTML string", ->
|
572
572
|
affix('.foo-bar')
|
@@ -365,6 +365,30 @@ describe 'up.link', ->
|
|
365
365
|
expect($('.target')).toHaveText('new text')
|
366
366
|
expect(location.pathname).toEqual('/other/path')
|
367
367
|
|
368
|
+
it 'prefers to update a container in the same layer as the clicked link', ->
|
369
|
+
up.motion.config.enabled = false
|
370
|
+
|
371
|
+
$popupOpener = affix('a[href="/popup"]')
|
372
|
+
up.popup.attach($popupOpener, html: "<div class='target'>old popup text</div>", target: '.target')
|
373
|
+
|
374
|
+
affix('.document').affix('.target').text('old document text')
|
375
|
+
$linkInDocument = affix('a[href="/foo"][up-target=".target"]')
|
376
|
+
$linkInDocument.click()
|
377
|
+
|
378
|
+
@respondWith '<div class="target">new text from document link</div>'
|
379
|
+
|
380
|
+
expect($('.document .target')).toHaveText('new text from document link')
|
381
|
+
expect($('.up-popup .target')).toHaveText('old popup text')
|
382
|
+
|
383
|
+
$linkInPopup = $('.up-popup').affix('a[href="/bar"][up-target=".target"]')
|
384
|
+
$linkInPopup.click()
|
385
|
+
|
386
|
+
@respondWith '<div class="target">new text from popup link</div>'
|
387
|
+
|
388
|
+
expect($('.document .target')).toHaveText('new text from document link')
|
389
|
+
expect($('.up-popup .target')).toHaveText('new text from popup link')
|
390
|
+
|
391
|
+
|
368
392
|
describe 'with [up-transition] modifier', ->
|
369
393
|
|
370
394
|
describeCapability 'canCssTransition', ->
|
@@ -463,6 +463,23 @@ describe 'up.modal', ->
|
|
463
463
|
expect($('.outside')).toHaveText('new outside')
|
464
464
|
expect($('.up-modal')).not.toExist()
|
465
465
|
|
466
|
+
it 'does not restore the covered URL when auto-closing', (done) ->
|
467
|
+
up.motion.config.enabled = true
|
468
|
+
up.modal.config.openDuration = 0
|
469
|
+
up.modal.config.closeDuration = 20
|
470
|
+
|
471
|
+
affix('.outside').text('old outside')
|
472
|
+
whenModalOpen = up.modal.visit('/path', target: '.inside')
|
473
|
+
@respondWith("<div class='inside'>old inside</div>") # Populate modal
|
474
|
+
|
475
|
+
whenModalOpen.then ->
|
476
|
+
up.extract('.outside', "<div class='outside'>new outside</div>",
|
477
|
+
origin: $('.inside'), history: '/new-location') # Provoke auto-close
|
478
|
+
|
479
|
+
u.setTimer 50, ->
|
480
|
+
expect(location.href).toEndWith '/new-location'
|
481
|
+
done()
|
482
|
+
|
466
483
|
it 'does not auto-close the modal when a replacement from inside the modal affects a selector inside the modal', ->
|
467
484
|
affix('.outside').text('old outside')
|
468
485
|
up.modal.visit('/path', target: '.inside')
|
@@ -300,7 +300,7 @@ describe 'up.popup', ->
|
|
300
300
|
it 'prefers to replace a selector within the popup', ->
|
301
301
|
$outside = affix('.foo').text('old outside')
|
302
302
|
$link = affix('.link')
|
303
|
-
up.popup.attach($link,
|
303
|
+
up.popup.attach($link, target: '.foo')
|
304
304
|
@respondWith("<div class='foo'>old inside</div>")
|
305
305
|
up.extract('.foo', "<div class='foo'>new text</div>")
|
306
306
|
expect($outside).toBeInDOM()
|
@@ -310,16 +310,35 @@ describe 'up.popup', ->
|
|
310
310
|
it 'auto-closes the popup when a replacement from inside the popup affects a selector behind the popup', ->
|
311
311
|
affix('.outside').text('old outside')
|
312
312
|
$link = affix('.link')
|
313
|
-
up.popup.attach($link,
|
313
|
+
up.popup.attach($link, target: '.inside')
|
314
314
|
@respondWith("<div class='inside'>old inside</div>")
|
315
315
|
up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.inside'))
|
316
316
|
expect($('.outside')).toHaveText('new outside')
|
317
317
|
expect($('.up-popup')).not.toExist()
|
318
318
|
|
319
|
+
it 'does not restore the covered URL when auto-closing', (done) ->
|
320
|
+
up.motion.config.enabled = true
|
321
|
+
up.popup.config.openDuration = 0
|
322
|
+
up.popup.config.closeDuration = 20
|
323
|
+
up.popup.config.history = true
|
324
|
+
|
325
|
+
affix('.outside').text('old outside')
|
326
|
+
$link = affix('.link')
|
327
|
+
whenPopupOpen = up.popup.attach($link, url: '/path', target: '.inside')
|
328
|
+
@respondWith("<div class='inside'>old inside</div>") # Populate popup
|
329
|
+
|
330
|
+
whenPopupOpen.then ->
|
331
|
+
up.extract('.outside', "<div class='outside'>new outside</div>",
|
332
|
+
origin: $('.inside'), history: '/new-location') # Provoke auto-close
|
333
|
+
|
334
|
+
u.setTimer 50, ->
|
335
|
+
expect(location.href).toEndWith '/new-location'
|
336
|
+
done()
|
337
|
+
|
319
338
|
it 'does not auto-close the popup when a replacement from inside the popup affects a selector inside the popup', ->
|
320
339
|
affix('.outside').text('old outside')
|
321
340
|
$link = affix('.link')
|
322
|
-
up.popup.attach($link,
|
341
|
+
up.popup.attach($link, target: '.inside')
|
323
342
|
@respondWith("<div class='inside'>old inside</div>")
|
324
343
|
up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.inside'))
|
325
344
|
expect($('.inside')).toHaveText('new inside')
|
@@ -328,7 +347,7 @@ describe 'up.popup', ->
|
|
328
347
|
it 'does not auto-close the popup when a replacement from outside the popup affects a selector outside the popup', ->
|
329
348
|
affix('.outside').text('old outside')
|
330
349
|
$link = affix('.link')
|
331
|
-
up.popup.attach($link,
|
350
|
+
up.popup.attach($link, target: '.inside')
|
332
351
|
@respondWith("<div class='inside'>old inside</div>")
|
333
352
|
up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.outside'))
|
334
353
|
expect($('.outside')).toHaveText('new outside')
|
@@ -337,7 +356,7 @@ describe 'up.popup', ->
|
|
337
356
|
it 'does not auto-close the popup when a replacement from outside the popup affects a selector inside the popup', ->
|
338
357
|
affix('.outside').text('old outside')
|
339
358
|
$link = affix('.link')
|
340
|
-
up.popup.attach($link,
|
359
|
+
up.popup.attach($link, target: '.inside')
|
341
360
|
@respondWith("<div class='inside'>old inside</div>")
|
342
361
|
up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.outside'))
|
343
362
|
expect($('.inside')).toHaveText('new inside')
|
@@ -351,7 +370,7 @@ describe 'up.popup', ->
|
|
351
370
|
it 'closes a popup on mousedown (in case an [up-instant] link removes its parent and thus a click event never fires)', ->
|
352
371
|
affix('.outside').text('old outside')
|
353
372
|
$link = affix('.link')
|
354
|
-
up.popup.attach($link,
|
373
|
+
up.popup.attach($link, target: '.inside')
|
355
374
|
@respondWith("<div class='inside'>inside</div>")
|
356
375
|
Trigger.mousedown($('body'))
|
357
376
|
expect($('.up-popup')).not.toExist()
|
@@ -2,6 +2,42 @@ describe 'up.util', ->
|
|
2
2
|
|
3
3
|
describe 'Javascript functions', ->
|
4
4
|
|
5
|
+
describe 'up.util.createElementFromHtml', ->
|
6
|
+
|
7
|
+
it 'parses a string that contains a serialized HTML document', ->
|
8
|
+
string = """
|
9
|
+
<html lang="foo">
|
10
|
+
<head>
|
11
|
+
<title>document title</title>
|
12
|
+
</head>
|
13
|
+
<body data-env='production'>
|
14
|
+
<div>line 1</div>
|
15
|
+
<div>line 2</div>
|
16
|
+
</body>
|
17
|
+
</html>
|
18
|
+
"""
|
19
|
+
|
20
|
+
element = up.util.createElementFromHtml(string)
|
21
|
+
|
22
|
+
expect(element.querySelector('head title').textContent).toEqual('document title')
|
23
|
+
expect(element.querySelectorAll('body div').length).toBe(2)
|
24
|
+
expect(element.querySelectorAll('body div')[0].textContent).toEqual('line 1')
|
25
|
+
expect(element.querySelectorAll('body div')[1].textContent).toEqual('line 2')
|
26
|
+
|
27
|
+
it 'parses a string that contains carriage returns (bugfix)', ->
|
28
|
+
string = """
|
29
|
+
<html>\r
|
30
|
+
<body>\r
|
31
|
+
<div>line</div>\r
|
32
|
+
</body>\r
|
33
|
+
</html>\r
|
34
|
+
"""
|
35
|
+
|
36
|
+
$element = up.util.createElementFromHtml(string)
|
37
|
+
expect($element.querySelector('body')).toBeGiven()
|
38
|
+
expect($element.querySelector('body div').textContent).toEqual('line')
|
39
|
+
|
40
|
+
|
5
41
|
describe 'up.util.cssAnimate', ->
|
6
42
|
|
7
43
|
it 'returns a deferred that eventually resolves if called with a duration of 0 (bugfix)', (done) ->
|
@@ -197,6 +233,12 @@ describe 'up.util', ->
|
|
197
233
|
it 'returns false for an array with at least one element', ->
|
198
234
|
expect(up.util.isBlank(['element'])).toBe(false)
|
199
235
|
|
236
|
+
it 'returns true for an empty jQuery collection', ->
|
237
|
+
expect(up.util.isBlank($([]))).toBe(true)
|
238
|
+
|
239
|
+
it 'returns false for a jQuery collection with at least one element', ->
|
240
|
+
expect(up.util.isBlank($('body'))).toBe(false)
|
241
|
+
|
200
242
|
it 'returns true for an empty object', ->
|
201
243
|
expect(up.util.isBlank({})).toBe(true)
|
202
244
|
|
@@ -254,6 +296,22 @@ describe 'up.util', ->
|
|
254
296
|
object.reset()
|
255
297
|
expect(object.reset).toBeDefined()
|
256
298
|
|
299
|
+
describe 'up.util.remove', ->
|
300
|
+
|
301
|
+
it 'removes the given string from the given array', ->
|
302
|
+
array = ['a', 'b', 'c']
|
303
|
+
up.util.remove(array, 'b')
|
304
|
+
expect(array).toEqual ['a', 'c']
|
305
|
+
|
306
|
+
it 'removes the given object from the given array', ->
|
307
|
+
obj1 = { 'key': 1 }
|
308
|
+
obj2 = { 'key': 2 }
|
309
|
+
obj3 = { 'key': 3 }
|
310
|
+
array = [obj1, obj2, obj3]
|
311
|
+
up.util.remove(array, obj2)
|
312
|
+
expect(array).toEqual [obj1, obj3]
|
313
|
+
|
314
|
+
|
257
315
|
describe 'up.util.requestDataAsQuery', ->
|
258
316
|
|
259
317
|
encodedOpeningBracket = '%5B'
|
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.27.
|
4
|
+
version: 0.27.2
|
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-07-
|
11
|
+
date: 2016-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -216,6 +216,7 @@ files:
|
|
216
216
|
- spec_app/spec/javascripts/helpers/to_be_around.js.coffee
|
217
217
|
- spec_app/spec/javascripts/helpers/to_be_blank.js.coffee
|
218
218
|
- spec_app/spec/javascripts/helpers/to_be_given.js.coffee
|
219
|
+
- spec_app/spec/javascripts/helpers/to_be_jquery.js.coffee
|
219
220
|
- spec_app/spec/javascripts/helpers/to_be_missing.js.coffee
|
220
221
|
- spec_app/spec/javascripts/helpers/to_be_present.js.coffee
|
221
222
|
- spec_app/spec/javascripts/helpers/to_contain.js.coffee
|