upjs-rails 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -1
- data/dist/up.js +929 -374
- data/dist/up.min.js +2 -2
- data/lib/assets/javascripts/up/browser.js.coffee +31 -14
- data/lib/assets/javascripts/up/bus.js.coffee +87 -22
- data/lib/assets/javascripts/up/flow.js.coffee +119 -43
- data/lib/assets/javascripts/up/form.js.coffee +188 -57
- data/lib/assets/javascripts/up/link.js.coffee +57 -21
- data/lib/assets/javascripts/up/modal.js.coffee +77 -63
- data/lib/assets/javascripts/up/motion.js.coffee +10 -9
- data/lib/assets/javascripts/up/popup.js.coffee +54 -40
- data/lib/assets/javascripts/up/proxy.js.coffee +46 -17
- data/lib/assets/javascripts/up/rails.js.coffee +22 -4
- data/lib/assets/javascripts/up/syntax.js.coffee +2 -2
- data/lib/assets/javascripts/up/util.js.coffee +100 -16
- data/lib/upjs/rails/inspector.rb +3 -3
- data/lib/upjs/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -4
- data/spec_app/app/controllers/test_controller.rb +2 -2
- data/spec_app/spec/controllers/test_controller_spec.rb +5 -5
- data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/knife.js.coffee +0 -1
- data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +4 -5
- data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +8 -0
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +26 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +203 -91
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +244 -49
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +8 -2
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +83 -30
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +23 -17
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +4 -4
- data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +26 -16
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +45 -13
- data/spec_app/spec/javascripts/up/rails_spec.js.coffee +48 -0
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +48 -0
- metadata +5 -2
@@ -88,6 +88,16 @@ up.proxy = (($) ->
|
|
88
88
|
|
89
89
|
Note that your browser might [impose its own request limit](http://www.browserscope.org/?category=network)
|
90
90
|
regardless of what you configure here.
|
91
|
+
@param {Array<String>} [config.wrapMethods]
|
92
|
+
An array of uppercase HTTP method names. AJAX requests with one of these methods
|
93
|
+
will be converted into a `POST` request and carry their original method as a `_method`
|
94
|
+
parameter. This is to [prevent unexpected redirect behavior](https://makandracards.com/makandra/38347).
|
95
|
+
@param {String} [config.wrapMethodParam]
|
96
|
+
The name of the POST parameter when wrapping HTTP methods in a `POST` request.
|
97
|
+
@param {Array<String>} [config.safeMethods]
|
98
|
+
An array of uppercase HTTP method names that are considered idempotent.
|
99
|
+
The proxy cache will only cache idempotent requests and will clear the entire
|
100
|
+
cache after a non-idempotent request.
|
91
101
|
@stable
|
92
102
|
###
|
93
103
|
config = u.config
|
@@ -96,13 +106,16 @@ up.proxy = (($) ->
|
|
96
106
|
cacheSize: 70
|
97
107
|
cacheExpiry: 1000 * 60 * 5
|
98
108
|
maxRequests: 4
|
109
|
+
wrapMethods: ['PATCH', 'PUT', 'DELETE']
|
110
|
+
wrapMethodParam: '_method'
|
111
|
+
safeMethods: ['GET', 'OPTIONS', 'HEAD']
|
99
112
|
|
100
113
|
cacheKey = (request) ->
|
101
114
|
normalizeRequest(request)
|
102
115
|
[ request.url,
|
103
116
|
request.method,
|
104
117
|
request.data,
|
105
|
-
request.
|
118
|
+
request.target
|
106
119
|
].join('|')
|
107
120
|
|
108
121
|
cache = u.cache
|
@@ -125,11 +138,11 @@ up.proxy = (($) ->
|
|
125
138
|
get = (request) ->
|
126
139
|
request = normalizeRequest(request)
|
127
140
|
candidates = [request]
|
128
|
-
unless request.
|
129
|
-
requestForHtml = u.merge(request,
|
141
|
+
unless request.target is 'html'
|
142
|
+
requestForHtml = u.merge(request, target: 'html')
|
130
143
|
candidates.push(requestForHtml)
|
131
|
-
unless request.
|
132
|
-
requestForBody = u.merge(request,
|
144
|
+
unless request.target is 'body'
|
145
|
+
requestForBody = u.merge(request, target: 'body')
|
133
146
|
candidates.push(requestForBody)
|
134
147
|
for candidate in candidates
|
135
148
|
if response = cache.get(candidate)
|
@@ -141,7 +154,7 @@ up.proxy = (($) ->
|
|
141
154
|
@function up.proxy.set
|
142
155
|
@param {String} request.url
|
143
156
|
@param {String} [request.method='GET']
|
144
|
-
@param {String} [request.
|
157
|
+
@param {String} [request.target='body']
|
145
158
|
@param {Promise} response
|
146
159
|
A promise for the response that is API-compatible with the
|
147
160
|
promise returned by [`jQuery.ajax`](http://api.jquery.com/jquery.ajax/).
|
@@ -158,7 +171,7 @@ up.proxy = (($) ->
|
|
158
171
|
@function up.proxy.remove
|
159
172
|
@param {String} request.url
|
160
173
|
@param {String} [request.method='GET']
|
161
|
-
@param {String} [request.
|
174
|
+
@param {String} [request.target='body']
|
162
175
|
@experimental
|
163
176
|
###
|
164
177
|
remove = cache.remove
|
@@ -200,7 +213,7 @@ up.proxy = (($) ->
|
|
200
213
|
unless request._normalized
|
201
214
|
request.method = u.normalizeMethod(request.method)
|
202
215
|
request.url = u.normalizeUrl(request.url) if request.url
|
203
|
-
request.
|
216
|
+
request.target ||= 'body'
|
204
217
|
request._normalized = true
|
205
218
|
request
|
206
219
|
|
@@ -221,13 +234,15 @@ up.proxy = (($) ->
|
|
221
234
|
@function up.proxy.ajax
|
222
235
|
@param {String} request.url
|
223
236
|
@param {String} [request.method='GET']
|
224
|
-
@param {String} [request.
|
237
|
+
@param {String} [request.target='body']
|
225
238
|
@param {Boolean} [request.cache]
|
226
239
|
Whether to use a cached response, if available.
|
227
240
|
If set to `false` a network connection will always be attempted.
|
228
241
|
@param {Object} [request.headers={}]
|
229
242
|
An object of additional header key/value pairs to send along
|
230
243
|
with the request.
|
244
|
+
@param {Object} [request.data={}]
|
245
|
+
An object of request parameters.
|
231
246
|
@return
|
232
247
|
A promise for the response that is API-compatible with the
|
233
248
|
promise returned by [`jQuery.ajax`](http://api.jquery.com/jquery.ajax/).
|
@@ -238,7 +253,7 @@ up.proxy = (($) ->
|
|
238
253
|
forceCache = (options.cache == true)
|
239
254
|
ignoreCache = (options.cache == false)
|
240
255
|
|
241
|
-
request = u.only(options, 'url', 'method', 'data', '
|
256
|
+
request = u.only(options, 'url', 'method', 'data', 'target', 'headers', '_normalized')
|
242
257
|
|
243
258
|
pending = true
|
244
259
|
|
@@ -276,8 +291,6 @@ up.proxy = (($) ->
|
|
276
291
|
|
277
292
|
promise
|
278
293
|
|
279
|
-
SAFE_HTTP_METHODS = ['GET', 'OPTIONS', 'HEAD']
|
280
|
-
|
281
294
|
###*
|
282
295
|
Returns `true` if the proxy is not currently waiting
|
283
296
|
for a request to finish. Returns `false` otherwise.
|
@@ -372,9 +385,25 @@ up.proxy = (($) ->
|
|
372
385
|
deferred.promise()
|
373
386
|
|
374
387
|
load = (request) ->
|
375
|
-
u.debug('
|
388
|
+
u.debug('Fetching %o via %o', request.url, request.method)
|
376
389
|
up.emit('up:proxy:load', request)
|
377
|
-
|
390
|
+
|
391
|
+
# We will modify the request below for features like method wrapping.
|
392
|
+
# Let's not change the original request which would confuse API clients
|
393
|
+
# and cache key logic.
|
394
|
+
request = u.copy(request)
|
395
|
+
|
396
|
+
request.headers ||= {}
|
397
|
+
request.headers['X-Up-Target'] = request.target
|
398
|
+
request.data = u.requestDataAsArray(request.data)
|
399
|
+
|
400
|
+
if u.contains(config.wrapMethods, request.method)
|
401
|
+
request.data.push
|
402
|
+
name: config.wrapMethodParam
|
403
|
+
value: request.method
|
404
|
+
request.method = 'POST'
|
405
|
+
|
406
|
+
promise = $.ajax(request)
|
378
407
|
promise.always ->
|
379
408
|
up.emit('up:proxy:received', request)
|
380
409
|
pokeQueue()
|
@@ -393,7 +422,7 @@ up.proxy = (($) ->
|
|
393
422
|
@event up:proxy:load
|
394
423
|
@param event.url
|
395
424
|
@param event.method
|
396
|
-
@param event.
|
425
|
+
@param event.target
|
397
426
|
@experimental
|
398
427
|
###
|
399
428
|
|
@@ -404,13 +433,13 @@ up.proxy = (($) ->
|
|
404
433
|
@event up:proxy:received
|
405
434
|
@param event.url
|
406
435
|
@param event.method
|
407
|
-
@param event.
|
436
|
+
@param event.target
|
408
437
|
@experimental
|
409
438
|
###
|
410
439
|
|
411
440
|
isIdempotent = (request) ->
|
412
441
|
normalizeRequest(request)
|
413
|
-
u.contains(
|
442
|
+
u.contains(config.safeMethods, request.method)
|
414
443
|
|
415
444
|
checkPreload = ($link) ->
|
416
445
|
delay = parseInt(u.presentAttr($link, 'up-delay')) || config.preloadDelay
|
@@ -10,9 +10,27 @@ up.rails = (($) ->
|
|
10
10
|
willHandle = ($element) ->
|
11
11
|
$element.is('[up-follow], [up-target], [up-modal], [up-popup]')
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
isRails = ->
|
14
|
+
u.isGiven($.rails)
|
15
|
+
|
16
|
+
u.each ['method', 'confirm'], (feature) ->
|
17
|
+
|
18
|
+
dataAttribute = "data-#{feature}"
|
19
|
+
upAttribute = "up-#{feature}"
|
20
|
+
|
21
|
+
up.compiler "[#{dataAttribute}]", ($element) ->
|
22
|
+
if isRails() && willHandle($element)
|
23
|
+
replacement = {}
|
24
|
+
replacement[upAttribute] = $element.attr(dataAttribute)
|
25
|
+
u.setMissingAttrs($element, replacement)
|
26
|
+
$element.removeAttr(dataAttribute)
|
27
|
+
|
28
|
+
csrfField = ->
|
29
|
+
if isRails()
|
30
|
+
name: $.rails.csrfParam()
|
31
|
+
value: $.rails.csrfToken()
|
32
|
+
|
33
|
+
csrfField: csrfField
|
34
|
+
isRails: isRails
|
17
35
|
|
18
36
|
)(jQuery)
|
@@ -332,8 +332,8 @@ up.syntax = (($) ->
|
|
332
332
|
###
|
333
333
|
|
334
334
|
up.on 'ready', (-> hello(document.body))
|
335
|
-
up.on 'up:fragment:inserted', (event) -> compile(
|
336
|
-
up.on 'up:fragment:destroy', (event) -> runDestroyers(
|
335
|
+
up.on 'up:fragment:inserted', (event, $element) -> compile($element)
|
336
|
+
up.on 'up:fragment:destroy', (event, $element) -> runDestroyers($element)
|
337
337
|
up.on 'up:framework:boot', snapshot
|
338
338
|
up.on 'up:framework:reset', reset
|
339
339
|
|
@@ -23,18 +23,6 @@ up.util = (($) ->
|
|
23
23
|
cached = true
|
24
24
|
cache = func(args...)
|
25
25
|
|
26
|
-
###*
|
27
|
-
@function up.util.ajax
|
28
|
-
@internal
|
29
|
-
###
|
30
|
-
ajax = (request) ->
|
31
|
-
request = copy(request)
|
32
|
-
if request.selector
|
33
|
-
request.headers ||= {}
|
34
|
-
request.headers['X-Up-Selector'] = request.selector
|
35
|
-
# Delegate to jQuery
|
36
|
-
$.ajax(request)
|
37
|
-
|
38
26
|
###*
|
39
27
|
Returns if the given port is the default port for the given protocol.
|
40
28
|
|
@@ -247,8 +235,8 @@ up.util = (($) ->
|
|
247
235
|
selector = "##{id}"
|
248
236
|
else if name = presence($element.attr("name"))
|
249
237
|
selector = "[name='#{name}']"
|
250
|
-
else if
|
251
|
-
|
238
|
+
else if classes = presence(nonUpClasses($element))
|
239
|
+
console.log("using klass!", classes)
|
252
240
|
selector = ''
|
253
241
|
for klass in classes
|
254
242
|
selector += ".#{klass}"
|
@@ -256,6 +244,11 @@ up.util = (($) ->
|
|
256
244
|
selector = $element.prop('tagName').toLowerCase()
|
257
245
|
selector
|
258
246
|
|
247
|
+
nonUpClasses = ($element) ->
|
248
|
+
classString = $element.attr('class') || ''
|
249
|
+
classes = classString.split(' ')
|
250
|
+
select classes, (klass) -> isPresent(klass) && !klass.match(/^up-/)
|
251
|
+
|
259
252
|
# jQuery's implementation of $(...) cannot create elements that have
|
260
253
|
# an <html> or <body> tag. So we're using native elements.
|
261
254
|
# Also IE9 cannot set innerHTML on a <html> or <head> element.
|
@@ -761,6 +754,30 @@ up.util = (($) ->
|
|
761
754
|
matches.push(element)
|
762
755
|
matches
|
763
756
|
|
757
|
+
###*
|
758
|
+
Returns all elements from the given array that do not return
|
759
|
+
a truthy value when passed to the given function.
|
760
|
+
|
761
|
+
@function up.util.reject
|
762
|
+
@param {Array<T>} array
|
763
|
+
@return {Array<T>}
|
764
|
+
@stable
|
765
|
+
###
|
766
|
+
reject = (array, tester) ->
|
767
|
+
select(array, (element) -> !tester(element))
|
768
|
+
|
769
|
+
###*
|
770
|
+
Returns the intersection of the given two arrays.
|
771
|
+
|
772
|
+
Implementation is not optimized. Don't use it for large arrays.
|
773
|
+
|
774
|
+
@function up.util.intersect
|
775
|
+
@internal
|
776
|
+
###
|
777
|
+
intersect = (array1, array2) ->
|
778
|
+
select array1, (element) ->
|
779
|
+
contains(array2, element)
|
780
|
+
|
764
781
|
###*
|
765
782
|
Returns the first [present](/up.util.isPresent) element attribute
|
766
783
|
among the given list of attribute names.
|
@@ -1098,6 +1115,21 @@ up.util = (($) ->
|
|
1098
1115
|
filtered[property] = object[property]
|
1099
1116
|
filtered
|
1100
1117
|
|
1118
|
+
###*
|
1119
|
+
Returns a copy of the given object that contains all except
|
1120
|
+
the given properties.
|
1121
|
+
|
1122
|
+
@function up.util.except
|
1123
|
+
@param {Object} object
|
1124
|
+
@param {Array} keys...
|
1125
|
+
@stable
|
1126
|
+
###
|
1127
|
+
except = (object, properties...) ->
|
1128
|
+
filtered = copy(object)
|
1129
|
+
for property in properties
|
1130
|
+
delete filtered[property]
|
1131
|
+
filtered
|
1132
|
+
|
1101
1133
|
###*
|
1102
1134
|
@function up.util.isUnmodifiedKeyEvent
|
1103
1135
|
@internal
|
@@ -1179,7 +1211,20 @@ up.util = (($) ->
|
|
1179
1211
|
joined.resolve = ->
|
1180
1212
|
each deferreds, (deferred) -> deferred.resolve?()
|
1181
1213
|
joined
|
1182
|
-
|
1214
|
+
|
1215
|
+
# resolvableSequence = (first, callbacks...) ->
|
1216
|
+
# sequence = $.Deferred().promise()
|
1217
|
+
# values = [first]
|
1218
|
+
# current = first
|
1219
|
+
# for callback in callbacks
|
1220
|
+
# current = current.then ->
|
1221
|
+
# value = callback()
|
1222
|
+
# values.push(value) if u.isPromise(value)
|
1223
|
+
# value
|
1224
|
+
# sequence.resolve = ->
|
1225
|
+
# each values, (deferred) -> deferred.resolve?()
|
1226
|
+
# sequence
|
1227
|
+
|
1183
1228
|
###*
|
1184
1229
|
On the given element, set attributes that are still missing.
|
1185
1230
|
|
@@ -1446,6 +1491,43 @@ up.util = (($) ->
|
|
1446
1491
|
# else
|
1447
1492
|
# error('Could not parse argument names of %o', fun)
|
1448
1493
|
|
1494
|
+
###*
|
1495
|
+
Normalizes the given params object to the form returned by
|
1496
|
+
[`jQuery.serializeArray`](https://api.jquery.com/serializeArray/).
|
1497
|
+
|
1498
|
+
@function up.util.requestDataAsArray
|
1499
|
+
@param {Object|Array|Undefined|Null} data
|
1500
|
+
@internal
|
1501
|
+
###
|
1502
|
+
requestDataAsArray = (data) ->
|
1503
|
+
if isMissing(data)
|
1504
|
+
[]
|
1505
|
+
else if isArray(data)
|
1506
|
+
data
|
1507
|
+
else if isObject(data)
|
1508
|
+
{ name: name, value: value } for name, value of data
|
1509
|
+
else
|
1510
|
+
error('Unknown options.data type for %o', data)
|
1511
|
+
|
1512
|
+
###*
|
1513
|
+
Returns an URL-encoded query string for the given params object.
|
1514
|
+
|
1515
|
+
@function up.util.requestDataAsQueryString
|
1516
|
+
@param {Object|Array|Undefined|Null} data
|
1517
|
+
@internal
|
1518
|
+
###
|
1519
|
+
requestDataAsQueryString = (data) ->
|
1520
|
+
array = requestDataAsArray(data)
|
1521
|
+
query = ''
|
1522
|
+
if isPresent(array)
|
1523
|
+
query += '?'
|
1524
|
+
each array, (field, index) ->
|
1525
|
+
query += '&' unless index == 0
|
1526
|
+
query += encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value)
|
1527
|
+
query
|
1528
|
+
|
1529
|
+
requestDataAsArray: requestDataAsArray
|
1530
|
+
requestDataAsQueryString: requestDataAsQueryString
|
1449
1531
|
offsetParent: offsetParent
|
1450
1532
|
fixedToAbsolute: fixedToAbsolute
|
1451
1533
|
presentAttr: presentAttr
|
@@ -1456,7 +1538,6 @@ up.util = (($) ->
|
|
1456
1538
|
createElementFromHtml: createElementFromHtml
|
1457
1539
|
$createElementFromSelector: $createElementFromSelector
|
1458
1540
|
selectorForElement: selectorForElement
|
1459
|
-
ajax: ajax
|
1460
1541
|
extend: extend
|
1461
1542
|
copy: copy
|
1462
1543
|
merge: merge
|
@@ -1471,6 +1552,8 @@ up.util = (($) ->
|
|
1471
1552
|
any: any
|
1472
1553
|
detect: detect
|
1473
1554
|
select: select
|
1555
|
+
reject: reject
|
1556
|
+
intersect: intersect
|
1474
1557
|
compact: compact
|
1475
1558
|
uniq: uniq
|
1476
1559
|
last: last
|
@@ -1514,6 +1597,7 @@ up.util = (($) ->
|
|
1514
1597
|
methodFromXhr: methodFromXhr
|
1515
1598
|
clientSize: clientSize
|
1516
1599
|
only: only
|
1600
|
+
except: except
|
1517
1601
|
trim: trim
|
1518
1602
|
unresolvableDeferred: unresolvableDeferred
|
1519
1603
|
unresolvablePromise: unresolvablePromise
|
data/lib/upjs/rails/inspector.rb
CHANGED
@@ -16,7 +16,7 @@ module Upjs
|
|
16
16
|
# [page fragment update](http://upjs.io/up.replace) triggered by an
|
17
17
|
# Up.js frontend.
|
18
18
|
def up?
|
19
|
-
|
19
|
+
target.present?
|
20
20
|
end
|
21
21
|
|
22
22
|
##
|
@@ -29,8 +29,8 @@ module Upjs
|
|
29
29
|
#
|
30
30
|
# Server-side code is free to optimize its response by only returning HTML
|
31
31
|
# that matches this selector.
|
32
|
-
def
|
33
|
-
request.headers['X-Up-
|
32
|
+
def target
|
33
|
+
request.headers['X-Up-Target']
|
34
34
|
end
|
35
35
|
|
36
36
|
##
|
data/lib/upjs/rails/version.rb
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -2,13 +2,13 @@ describe TestController do
|
|
2
2
|
|
3
3
|
describe '#up?' do
|
4
4
|
|
5
|
-
it 'returns true if the request has an X-Up-
|
6
|
-
request.headers['X-Up-
|
5
|
+
it 'returns true if the request has an X-Up-Target header' do
|
6
|
+
request.headers['X-Up-Target'] = 'body'
|
7
7
|
get :is_up
|
8
8
|
expect(response.body).to eq('true')
|
9
9
|
end
|
10
10
|
|
11
|
-
it 'returns false if the request has no X-Up-
|
11
|
+
it 'returns false if the request has no X-Up-Target header' do
|
12
12
|
get :is_up
|
13
13
|
expect(response.body).to eq('false')
|
14
14
|
end
|
@@ -20,8 +20,8 @@ describe TestController do
|
|
20
20
|
describe '#selector' do
|
21
21
|
|
22
22
|
it 'returns the CSS selector that is requested via Up.js' do
|
23
|
-
request.headers['X-Up-
|
24
|
-
get :
|
23
|
+
request.headers['X-Up-Target'] = '.foo'
|
24
|
+
get :up_target
|
25
25
|
expect(response.body).to eq('.foo')
|
26
26
|
end
|
27
27
|
|