unpoly-rails 0.22.0 → 0.22.1
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 +14 -0
- data/README_RAILS.md +1 -1
- data/dist/unpoly.js +271 -157
- data/dist/unpoly.min.js +2 -2
- data/lib/assets/javascripts/unpoly/bus.js.coffee +52 -26
- data/lib/assets/javascripts/unpoly/flow.js.coffee +10 -5
- data/lib/assets/javascripts/unpoly/history.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/layout.js.coffee +17 -5
- data/lib/assets/javascripts/unpoly/link.js.coffee +60 -61
- data/lib/assets/javascripts/unpoly/log.js.coffee +8 -0
- data/lib/assets/javascripts/unpoly/motion.js.coffee +20 -9
- data/lib/assets/javascripts/unpoly/proxy.js.coffee +42 -37
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +46 -7
- data/lib/assets/javascripts/unpoly/util.js.coffee +4 -3
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +104 -9
- metadata +2 -2
@@ -5,7 +5,7 @@ Caching and preloading
|
|
5
5
|
All HTTP requests go through the Unpoly proxy.
|
6
6
|
It caches a [limited](/up.proxy.config) number of server responses
|
7
7
|
for a [limited](/up.proxy.config) amount of time,
|
8
|
-
making requests to these URLs return
|
8
|
+
making requests to these URLs return instantly.
|
9
9
|
|
10
10
|
The cache is cleared whenever the user makes a non-`GET` request
|
11
11
|
(like `POST`, `PUT` or `DELETE`).
|
@@ -15,42 +15,6 @@ links when the user hovers over the click area](/up-preload) (or puts the mouse/
|
|
15
15
|
down before releasing). This way the response will already be cached when
|
16
16
|
the user performs the click.
|
17
17
|
|
18
|
-
Spinners
|
19
|
-
--------
|
20
|
-
|
21
|
-
You can [listen](/up.on) to the [`up:proxy:slow`](/up:proxy:slow)
|
22
|
-
and [`up:proxy:recover`](/up:proxy:recover) events to implement a spinner
|
23
|
-
that appears during a long-running request,
|
24
|
-
and disappears once the response has been received:
|
25
|
-
|
26
|
-
<div class="spinner">Please wait!</div>
|
27
|
-
|
28
|
-
Here is the Javascript to make it alive:
|
29
|
-
|
30
|
-
up.compiler('.spinner', function($element) {
|
31
|
-
|
32
|
-
show = function() { $element.show() };
|
33
|
-
hide = function() { $element.hide() };
|
34
|
-
|
35
|
-
showOff = up.on('up:proxy:slow', show);
|
36
|
-
hideOff = up.on('up:proxy:recover', hide);
|
37
|
-
|
38
|
-
hide();
|
39
|
-
|
40
|
-
// Clean up when the element is removed from the DOM
|
41
|
-
return function() {
|
42
|
-
showOff();
|
43
|
-
hideOff();
|
44
|
-
};
|
45
|
-
|
46
|
-
});
|
47
|
-
|
48
|
-
The `up:proxy:slow` event will be emitted after a delay of 300 ms
|
49
|
-
to prevent the spinner from flickering on and off.
|
50
|
-
You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.config) like this:
|
51
|
-
|
52
|
-
up.proxy.config.slowDelay = 150;
|
53
|
-
|
54
18
|
@class up.proxy
|
55
19
|
###
|
56
20
|
up.proxy = (($) ->
|
@@ -349,6 +313,43 @@ up.proxy = (($) ->
|
|
349
313
|
Note that if additional requests are made while Unpoly is already busy
|
350
314
|
waiting, **no** additional `up:proxy:slow` events will be triggered.
|
351
315
|
|
316
|
+
|
317
|
+
\#\#\#\# Spinners
|
318
|
+
|
319
|
+
You can [listen](/up.on) to the `up:proxy:slow`
|
320
|
+
and [`up:proxy:recover`](/up:proxy:recover) events to implement a spinner
|
321
|
+
that appears during a long-running request,
|
322
|
+
and disappears once the response has been received:
|
323
|
+
|
324
|
+
<div class="spinner">Please wait!</div>
|
325
|
+
|
326
|
+
Here is the Javascript to make it alive:
|
327
|
+
|
328
|
+
up.compiler('.spinner', function($element) {
|
329
|
+
|
330
|
+
show = function() { $element.show() };
|
331
|
+
hide = function() { $element.hide() };
|
332
|
+
|
333
|
+
showOff = up.on('up:proxy:slow', show);
|
334
|
+
hideOff = up.on('up:proxy:recover', hide);
|
335
|
+
|
336
|
+
hide();
|
337
|
+
|
338
|
+
// Clean up when the element is removed from the DOM
|
339
|
+
return function() {
|
340
|
+
showOff();
|
341
|
+
hideOff();
|
342
|
+
};
|
343
|
+
|
344
|
+
});
|
345
|
+
|
346
|
+
The `up:proxy:slow` event will be emitted after a delay of 300 ms
|
347
|
+
to prevent the spinner from flickering on and off.
|
348
|
+
You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.config) like this:
|
349
|
+
|
350
|
+
up.proxy.config.slowDelay = 150;
|
351
|
+
|
352
|
+
|
352
353
|
@event up:proxy:slow
|
353
354
|
@stable
|
354
355
|
###
|
@@ -363,6 +364,10 @@ up.proxy = (($) ->
|
|
363
364
|
This event is [emitted]/(up.emit) when [AJAX requests](/up.ajax)
|
364
365
|
have [taken long to finish](/up:proxy:slow), but have finished now.
|
365
366
|
|
367
|
+
See [`up:proxy:slow`](/up:proxy:slow) for more documentation on
|
368
|
+
how to use this event for implementing a spinner that shows during
|
369
|
+
long-running requests.
|
370
|
+
|
366
371
|
@event up:proxy:recover
|
367
372
|
@stable
|
368
373
|
###
|
@@ -46,11 +46,11 @@ up.syntax = (($) ->
|
|
46
46
|
Registers a function to be called whenever an element with
|
47
47
|
the given selector is inserted into the DOM.
|
48
48
|
|
49
|
-
|
49
|
+
up.compiler('.action', function($element) {
|
50
50
|
// your code here
|
51
51
|
});
|
52
52
|
|
53
|
-
|
53
|
+
The functions will be called on elements maching `.action` when
|
54
54
|
the page loads, or whenever a matching fragment is [updated through Unpoly](/up.replace)
|
55
55
|
later.
|
56
56
|
|
@@ -135,7 +135,7 @@ up.syntax = (($) ->
|
|
135
135
|
{ lat: 48.75, lng: 11.45, title: 'Ingolstadt' }
|
136
136
|
]"></div>
|
137
137
|
|
138
|
-
The JSON will parsed and handed to your
|
138
|
+
The JSON will parsed and handed to your compiler as a second argument:
|
139
139
|
|
140
140
|
up.compiler('.google-map', function($element, pins) {
|
141
141
|
|
@@ -195,7 +195,7 @@ up.syntax = (($) ->
|
|
195
195
|
@param {Function($element, data)} compiler
|
196
196
|
The function to call when a matching element is inserted.
|
197
197
|
The function takes the new element as the first argument (as a jQuery object).
|
198
|
-
If the element has an `up-data` attribute, its value is parsed as JSON
|
198
|
+
If the element has an [`up-data`](/up-data) attribute, its value is parsed as JSON
|
199
199
|
and passed as a second argument.
|
200
200
|
|
201
201
|
The function may return a destructor function that destroys the compiled
|
@@ -324,11 +324,21 @@ up.syntax = (($) ->
|
|
324
324
|
destroyer()
|
325
325
|
|
326
326
|
###*
|
327
|
-
Checks if the given element has an `up-data` attribute.
|
327
|
+
Checks if the given element has an [`up-data`](/up-data) attribute.
|
328
328
|
If yes, parses the attribute value as JSON and returns the parsed object.
|
329
329
|
|
330
330
|
Returns an empty object if the element has no `up-data` attribute.
|
331
331
|
|
332
|
+
\#\#\#\# Example
|
333
|
+
|
334
|
+
You have an element with JSON data serialized into an `up-data` attribute:
|
335
|
+
|
336
|
+
<span class="person" up-data="{ age: 18, name: 'Bob' }">Bob</span>
|
337
|
+
|
338
|
+
Calling `up.syntax.data` will deserialize the JSON string into a Javascript object:
|
339
|
+
|
340
|
+
up.syntax.data('.person') // returns { age: 18, name: 'Bob' }
|
341
|
+
|
332
342
|
@function up.syntax.data
|
333
343
|
@param {String|Element|jQuery} elementOrSelector
|
334
344
|
@return
|
@@ -338,15 +348,44 @@ up.syntax = (($) ->
|
|
338
348
|
@experimental
|
339
349
|
###
|
340
350
|
|
341
|
-
|
351
|
+
###*
|
342
352
|
If an element annotated with [`up-data`] is inserted into the DOM,
|
343
353
|
Up will parse the JSON and pass the resulting object to any matching
|
344
|
-
[`up.compiler`](/up.
|
354
|
+
[`up.compiler`](/up.compiler) handlers.
|
355
|
+
|
356
|
+
For instance, a container for a [Google Map](https://developers.google.com/maps/documentation/javascript/tutorial)
|
357
|
+
might attach the location and names of its marker pins:
|
358
|
+
|
359
|
+
<div class="google-map" up-data="[
|
360
|
+
{ lat: 48.36, lng: 10.99, title: 'Friedberg' },
|
361
|
+
{ lat: 48.75, lng: 11.45, title: 'Ingolstadt' }
|
362
|
+
]"></div>
|
363
|
+
|
364
|
+
The JSON will parsed and handed to your compiler as a second argument:
|
365
|
+
|
366
|
+
up.compiler('.google-map', function($element, pins) {
|
367
|
+
|
368
|
+
var map = new google.maps.Map($element);
|
369
|
+
|
370
|
+
pins.forEach(function(pin) {
|
371
|
+
var position = new google.maps.LatLng(pin.lat, pin.lng);
|
372
|
+
new google.maps.Marker({
|
373
|
+
position: position,
|
374
|
+
map: map,
|
375
|
+
title: pin.title
|
376
|
+
});
|
377
|
+
});
|
378
|
+
|
379
|
+
});
|
345
380
|
|
346
381
|
Similarly, when an event is triggered on an element annotated with
|
347
382
|
[`up-data`], the parsed object will be passed to any matching
|
348
383
|
[`up.on`](/up.on) handlers.
|
349
384
|
|
385
|
+
up.on('click', '.google-map', function(event, $element, pins) {
|
386
|
+
console.log("There are %d pins on the clicked map", pins.length);
|
387
|
+
});
|
388
|
+
|
350
389
|
@selector [up-data]
|
351
390
|
@param {JSON} up-data
|
352
391
|
A serialized JSON string
|
@@ -943,6 +943,7 @@ up.util = (($) ->
|
|
943
943
|
if opts.relative == true
|
944
944
|
coordinates = $element.position()
|
945
945
|
else
|
946
|
+
# A relative context element is given
|
946
947
|
$context = $(opts.relative)
|
947
948
|
elementCoords = $element.offset()
|
948
949
|
if $context.is(document)
|
@@ -968,9 +969,9 @@ up.util = (($) ->
|
|
968
969
|
box.height = $element.outerHeight()
|
969
970
|
|
970
971
|
if opts.full
|
971
|
-
viewport =
|
972
|
-
box.right = viewport.width - (box.left + box.width)
|
973
|
-
box.bottom = viewport.height - (box.top + box.height)
|
972
|
+
$viewport = up.layout.viewportOf($element)
|
973
|
+
box.right = $viewport.width() - (box.left + box.width)
|
974
|
+
box.bottom = $viewport.height() - (box.top + box.height)
|
974
975
|
box
|
975
976
|
|
976
977
|
###*
|
data/lib/unpoly/rails/version.rb
CHANGED
@@ -49,15 +49,16 @@ describe 'up.link', ->
|
|
49
49
|
# so we don't lose the Jasmine runner interface.
|
50
50
|
up.history.config.popTargets = ['.container']
|
51
51
|
|
52
|
-
respondWith = (html) =>
|
52
|
+
respondWith = (html, title) =>
|
53
53
|
@lastRequest().respondWith
|
54
54
|
status: 200
|
55
55
|
contentType: 'text/html'
|
56
56
|
responseText: "<div class='container'><div class='target'>#{html}</div></div>"
|
57
|
+
responseHeaders: { 'X-Up-Title': title }
|
57
58
|
|
58
|
-
followAndRespond = ($link, html) ->
|
59
|
+
followAndRespond = ($link, html, title) ->
|
59
60
|
promise = up.follow($link)
|
60
|
-
respondWith(html)
|
61
|
+
respondWith(html, title)
|
61
62
|
promise
|
62
63
|
|
63
64
|
$link1 = affix('a[href="/one"][up-target=".target"]')
|
@@ -66,35 +67,41 @@ describe 'up.link', ->
|
|
66
67
|
$container = affix('.container')
|
67
68
|
$target = affix('.target').appendTo($container).text('original text')
|
68
69
|
|
69
|
-
followAndRespond($link1, 'text from one').then =>
|
70
|
+
followAndRespond($link1, 'text from one', 'title from one').then =>
|
70
71
|
expect($('.target')).toHaveText('text from one')
|
71
72
|
expect(location.pathname).toEqual('/one')
|
73
|
+
expect(document.title).toEqual('title from one')
|
72
74
|
|
73
|
-
followAndRespond($link2, 'text from two').then =>
|
75
|
+
followAndRespond($link2, 'text from two', 'title from two').then =>
|
74
76
|
expect($('.target')).toHaveText('text from two')
|
75
77
|
expect(location.pathname).toEqual('/two')
|
78
|
+
expect(document.title).toEqual('title from two')
|
76
79
|
|
77
|
-
followAndRespond($link3, 'text from three').then =>
|
80
|
+
followAndRespond($link3, 'text from three', 'title from three').then =>
|
78
81
|
expect($('.target')).toHaveText('text from three')
|
79
82
|
expect(location.pathname).toEqual('/three')
|
83
|
+
expect(document.title).toEqual('title from three')
|
80
84
|
|
81
85
|
history.back()
|
82
86
|
@setTimer 50, =>
|
83
|
-
respondWith('restored text from two')
|
87
|
+
respondWith('restored text from two', 'restored title from two')
|
84
88
|
expect($('.target')).toHaveText('restored text from two')
|
85
89
|
expect(location.pathname).toEqual('/two')
|
90
|
+
expect(document.title).toEqual('restored title from two')
|
86
91
|
|
87
92
|
history.back()
|
88
93
|
@setTimer 50, =>
|
89
|
-
respondWith('restored text from one')
|
94
|
+
respondWith('restored text from one', 'restored title from one')
|
90
95
|
expect($('.target')).toHaveText('restored text from one')
|
91
96
|
expect(location.pathname).toEqual('/one')
|
97
|
+
expect(document.title).toEqual('restored title from one')
|
92
98
|
|
93
99
|
history.forward()
|
94
100
|
@setTimer 50, =>
|
95
101
|
# Since the response is cached, we don't have to respond
|
96
|
-
expect($('.target')).toHaveText('restored text from two')
|
102
|
+
expect($('.target')).toHaveText('restored text from two', 'restored title from two')
|
97
103
|
expect(location.pathname).toEqual('/two')
|
104
|
+
expect(document.title).toEqual('restored title from two')
|
98
105
|
|
99
106
|
done()
|
100
107
|
|
@@ -190,6 +197,27 @@ describe 'up.link', ->
|
|
190
197
|
up.follow($link)
|
191
198
|
expect(up.browser.loadPage).toHaveBeenCalledWith('/path', { method: 'PUT' })
|
192
199
|
|
200
|
+
describe 'up.link.makeFollowable', ->
|
201
|
+
|
202
|
+
it "adds [up-follow] to a link that wouldn't otherwise be handled by Unpoly", ->
|
203
|
+
$link = affix('a[href="/path"]').text('label')
|
204
|
+
up.link.makeFollowable($link)
|
205
|
+
expect($link.attr('up-follow')).toEqual('')
|
206
|
+
|
207
|
+
it "does not add [up-follow] to a link that is already [up-target]", ->
|
208
|
+
$link = affix('a[href="/path"][up-target=".target"]').text('label')
|
209
|
+
up.link.makeFollowable($link)
|
210
|
+
expect($link.attr('up-follow')).toBeMissing()
|
211
|
+
|
212
|
+
it "does not add [up-follow] to a link that is already [up-modal]", ->
|
213
|
+
$link = affix('a[href="/path"][up-modal=".target"]').text('label')
|
214
|
+
up.link.makeFollowable($link)
|
215
|
+
expect($link.attr('up-follow')).toBeMissing()
|
216
|
+
|
217
|
+
it "does not add [up-follow] to a link that is already [up-popup]", ->
|
218
|
+
$link = affix('a[href="/path"][up-popup=".target"]').text('label')
|
219
|
+
up.link.makeFollowable($link)
|
220
|
+
expect($link.attr('up-follow')).toBeMissing()
|
193
221
|
|
194
222
|
describe 'up.visit', ->
|
195
223
|
|
@@ -301,6 +329,48 @@ describe 'up.link', ->
|
|
301
329
|
Trigger.mousedown(@$link, metaKey: true)
|
302
330
|
expect(@followSpy).not.toHaveBeenCalled()
|
303
331
|
|
332
|
+
describe '[up-dash]', ->
|
333
|
+
|
334
|
+
it "is a shortcut for [up-preload], [up-instant] and [up-target], using [up-dash]'s value as [up-target]", ->
|
335
|
+
$link = affix('a[href="/path"][up-dash=".target"]').text('label')
|
336
|
+
up.hello($link)
|
337
|
+
expect($link.attr('up-preload')).toEqual('')
|
338
|
+
expect($link.attr('up-instant')).toEqual('')
|
339
|
+
expect($link.attr('up-target')).toEqual('.target')
|
340
|
+
|
341
|
+
it "adds [up-follow] attribute if [up-dash]'s value is 'true'", ->
|
342
|
+
$link = affix('a[href="/path"][up-dash="true"]').text('label')
|
343
|
+
up.hello($link)
|
344
|
+
expect($link.attr('up-follow')).toEqual('')
|
345
|
+
|
346
|
+
it "adds [up-follow] attribute if [up-dash] is present, but has no value", ->
|
347
|
+
$link = affix('a[href="/path"][up-dash]').text('label')
|
348
|
+
up.hello($link)
|
349
|
+
expect($link.attr('up-follow')).toEqual('')
|
350
|
+
|
351
|
+
it "does not add an [up-follow] attribute if [up-dash] is 'true', but [up-target] is present", ->
|
352
|
+
$link = affix('a[href="/path"][up-dash="true"][up-target=".target"]').text('label')
|
353
|
+
up.hello($link)
|
354
|
+
expect($link.attr('up-follow')).toBeMissing()
|
355
|
+
expect($link.attr('up-target')).toEqual('.target')
|
356
|
+
|
357
|
+
it "does not add an [up-follow] attribute if [up-dash] is 'true', but [up-modal] is present", ->
|
358
|
+
$link = affix('a[href="/path"][up-dash="true"][up-modal=".target"]').text('label')
|
359
|
+
up.hello($link)
|
360
|
+
expect($link.attr('up-follow')).toBeMissing()
|
361
|
+
expect($link.attr('up-modal')).toEqual('.target')
|
362
|
+
|
363
|
+
it "does not add an [up-follow] attribute if [up-dash] is 'true', but [up-popup] is present", ->
|
364
|
+
$link = affix('a[href="/path"][up-dash="true"][up-popup=".target"]').text('label')
|
365
|
+
up.hello($link)
|
366
|
+
expect($link.attr('up-follow')).toBeMissing()
|
367
|
+
expect($link.attr('up-popup')).toEqual('.target')
|
368
|
+
|
369
|
+
it "removes the [up-dash] attribute when it's done", ->
|
370
|
+
$link = affix('a[href="/path"]').text('label')
|
371
|
+
up.hello($link)
|
372
|
+
expect($link.attr('up-dash')).toBeMissing()
|
373
|
+
|
304
374
|
describe '[up-expand]', ->
|
305
375
|
|
306
376
|
it 'copies up-related attributes of a contained link', ->
|
@@ -332,6 +402,31 @@ describe 'up.link', ->
|
|
332
402
|
up.hello($area)
|
333
403
|
expect($area.attr('up-follow')).toEqual('')
|
334
404
|
|
405
|
+
it 'can be used to enlarge the click area of a link', ->
|
406
|
+
$area = affix('div[up-expand] a[href="/path"]')
|
407
|
+
up.hello($area)
|
408
|
+
spyOn(up, 'replace')
|
409
|
+
$area.get(0).click()
|
410
|
+
expect(up.replace).toHaveBeenCalled()
|
411
|
+
|
412
|
+
it 'does not trigger multiple replaces when the user clicks on the expanded area of an up-instant link (bugfix)', ->
|
413
|
+
$area = affix('div[up-expand] a[href="/path"][up-instant]')
|
414
|
+
up.hello($area)
|
415
|
+
spyOn(up, 'replace')
|
416
|
+
Trigger.mousedown($area)
|
417
|
+
Trigger.click($area)
|
418
|
+
expect(up.replace.calls.count()).toEqual(1)
|
419
|
+
|
420
|
+
it 'does not add an up-follow attribute if the expanded link is [up-dash] with a selector (bugfix)', ->
|
421
|
+
$area = affix('div[up-expand] a[href="/path"][up-dash=".element"]')
|
422
|
+
up.hello($area)
|
423
|
+
expect($area.attr('up-follow')).toBeMissing()
|
424
|
+
|
425
|
+
it 'does not an up-follow attribute if the expanded link is [up-dash] without a selector (bugfix)', ->
|
426
|
+
$area = affix('div[up-expand] a[href="/path"][up-dash]')
|
427
|
+
up.hello($area)
|
428
|
+
expect($area.attr('up-follow')).toEqual('')
|
429
|
+
|
335
430
|
describe 'with a CSS selector in the property value', ->
|
336
431
|
|
337
432
|
it "expands the contained link that matches the selector", ->
|
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.22.
|
4
|
+
version: 0.22.1
|
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-03-
|
11
|
+
date: 2016-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|