unpoly-rails 0.37.0 → 0.50.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 +127 -25
- data/LICENSE +1 -1
- data/README_RAILS.md +4 -2
- data/Rakefile +6 -1
- data/dist/unpoly.js +3192 -2198
- data/dist/unpoly.min.js +4 -3
- data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
- data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
- data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
- data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
- data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
- data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
- data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
- data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
- data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
- data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
- data/lib/assets/javascripts/unpoly/form.coffee +156 -139
- data/lib/assets/javascripts/unpoly/history.coffee +22 -19
- data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
- data/lib/assets/javascripts/unpoly/link.coffee +159 -158
- data/lib/assets/javascripts/unpoly/log.coffee +5 -5
- data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
- data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
- data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
- data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
- data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
- data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
- data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
- data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
- data/lib/assets/javascripts/unpoly/util.coffee +236 -477
- data/lib/assets/javascripts/unpoly.coffee +1 -1
- data/lib/unpoly/rails/inspector.rb +67 -22
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +13 -13
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
- data/spec_app/app/controllers/binding_test_controller.rb +19 -2
- data/spec_app/app/controllers/method_test_controller.rb +16 -0
- data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
- data/spec_app/app/views/method_test/form_target.erb +17 -0
- data/spec_app/app/views/method_test/page1.erb +11 -0
- data/spec_app/app/views/method_test/page2.erb +6 -0
- data/spec_app/app/views/pages/start.erb +33 -19
- data/spec_app/config/initializers/assets.rb +5 -0
- data/spec_app/config/routes.rb +3 -0
- data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
- data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
- data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
- data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
- data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
- data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
- data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
- data/spec_app/spec/javascripts/up/classes/.keep +0 -0
- data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
- data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
- data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
- data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
- data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
- metadata +30 -7
- data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
- data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -19,12 +19,12 @@ up.dom = (($) ->
|
|
19
19
|
Configures defaults for fragment insertion.
|
20
20
|
|
21
21
|
@property up.dom.config
|
22
|
-
@param {
|
22
|
+
@param {boolean} [options.runInlineScripts=true]
|
23
23
|
Whether inline `<script>` tags inside inserted HTML fragments will be executed.
|
24
|
-
@param {
|
24
|
+
@param {boolean} [options.runLinkedScripts=false]
|
25
25
|
Whether `<script src='...'>` tags inside inserted HTML fragments will fetch and execute
|
26
26
|
the linked JavaScript file.
|
27
|
-
@param {
|
27
|
+
@param {string} [options.fallbacks=['body']]
|
28
28
|
When a fragment updates cannot find the requested element, Unpoly will try this list of alternative selectors.
|
29
29
|
|
30
30
|
The first selector that matches an element in the current page (or response) will be used.
|
@@ -33,7 +33,7 @@ up.dom = (($) ->
|
|
33
33
|
It is recommend to always keep `'body'` as the last selector in the last in the case
|
34
34
|
your server or load balancer renders an error message that does not contain your
|
35
35
|
application layout.
|
36
|
-
@param {
|
36
|
+
@param {string} [options.fallbackTransition='none']
|
37
37
|
The transition to use when using a fallback target.
|
38
38
|
@stable
|
39
39
|
###
|
@@ -55,7 +55,7 @@ up.dom = (($) ->
|
|
55
55
|
Returns the URL the given element was retrieved from.
|
56
56
|
|
57
57
|
@method up.dom.source
|
58
|
-
@param {
|
58
|
+
@param {string|Element|jQuery} selectorOrElement
|
59
59
|
@experimental
|
60
60
|
###
|
61
61
|
source = (selectorOrElement) ->
|
@@ -67,8 +67,8 @@ up.dom = (($) ->
|
|
67
67
|
to an absolute selector.
|
68
68
|
|
69
69
|
@function up.dom.resolveSelector
|
70
|
-
@param {
|
71
|
-
@param {
|
70
|
+
@param {string|Element|jQuery} selectorOrElement
|
71
|
+
@param {string|Element|jQuery} origin
|
72
72
|
The element that this selector resolution is relative to.
|
73
73
|
That element's selector will be substituted for `&`.
|
74
74
|
@internal
|
@@ -164,57 +164,59 @@ up.dom = (($) ->
|
|
164
164
|
element that replaces it.
|
165
165
|
|
166
166
|
@function up.replace
|
167
|
-
@param {
|
167
|
+
@param {string|Element|jQuery} selectorOrElement
|
168
168
|
The CSS selector to update. You can also pass a DOM element or jQuery element
|
169
169
|
here, in which case a selector will be inferred from the element's class and ID.
|
170
|
-
@param {
|
170
|
+
@param {string} url
|
171
171
|
The URL to fetch from the server.
|
172
|
-
@param {
|
172
|
+
@param {string} [options.failTarget='body']
|
173
173
|
The CSS selector to update if the server sends a non-200 status code.
|
174
|
-
@param {
|
174
|
+
@param {string} [options.fallback]
|
175
175
|
The selector to update when the original target was not found in the page.
|
176
|
-
@param {
|
176
|
+
@param {string} [options.title]
|
177
177
|
The document title after the replacement.
|
178
178
|
|
179
179
|
If the call pushes an history entry and this option is missing, the title is extracted from the response's `<title>` tag.
|
180
180
|
You can also pass `false` to explicitly prevent the title from being updated.
|
181
|
-
@param {
|
181
|
+
@param {string} [options.method='get']
|
182
182
|
The HTTP method to use for the request.
|
183
|
-
@param {Object|Array} [options.data]
|
183
|
+
@param {Object|Array|FormData} [options.data]
|
184
184
|
Parameters that should be sent as the request's payload.
|
185
185
|
|
186
186
|
Parameters can either be passed as an object (where the property names become
|
187
187
|
the param names and the property values become the param values) or as
|
188
188
|
an array of `{ name: 'param-name', value: 'param-value' }` objects
|
189
|
-
|
190
|
-
@param {
|
191
|
-
@param {
|
192
|
-
If a
|
189
|
+
|
190
|
+
@param {string} [options.transition='none']
|
191
|
+
@param {string|boolean} [options.history=true]
|
192
|
+
If a string is given, it is used as the URL the browser's location bar and history.
|
193
193
|
If omitted or true, the `url` argument will be used.
|
194
194
|
If set to `false`, the history will remain unchanged.
|
195
|
-
@param {
|
196
|
-
@param {
|
195
|
+
@param {boolean|string} [options.source=true]
|
196
|
+
@param {boolean|string} [options.reveal=false]
|
197
197
|
Whether to [reveal](/up.reveal) the element being updated, by
|
198
198
|
scrolling its containing viewport.
|
199
|
-
|
199
|
+
|
200
|
+
You can also pass a CSS selector for the element to reveal.
|
201
|
+
@param {boolean} [options.restoreScroll=false]
|
200
202
|
If set to true, Unpoly will try to restore the scroll position
|
201
203
|
of all the viewports around or below the updated element. The position
|
202
204
|
will be reset to the last known top position before a previous
|
203
205
|
history change for the current URL.
|
204
|
-
@param {
|
206
|
+
@param {boolean} [options.cache]
|
205
207
|
Whether to use a [cached response](/up.proxy) if available.
|
206
|
-
@param {
|
208
|
+
@param {string} [options.historyMethod='push']
|
207
209
|
@param {Object} [options.headers={}]
|
208
210
|
An object of additional header key/value pairs to send along
|
209
211
|
with the request.
|
210
|
-
@param {
|
212
|
+
@param {boolean} [options.requireMatch=true]
|
211
213
|
Whether to raise an error if the given selector is missing in
|
212
214
|
either the current page or in the response.
|
213
215
|
@param {Element|jQuery} [options.origin]
|
214
216
|
The element that triggered the replacement.
|
215
217
|
|
216
218
|
The element's selector will be substituted for the `&` shorthand in the target selector.
|
217
|
-
@param {
|
219
|
+
@param {string} [options.layer='auto']
|
218
220
|
The name of the layer that ought to be updated. Valid values are
|
219
221
|
`auto`, `page`, `modal` and `popup`.
|
220
222
|
|
@@ -222,23 +224,22 @@ up.dom = (($) ->
|
|
222
224
|
same layer as the element that triggered the replacement (see `options.origin`).
|
223
225
|
If that element is not known, or no match was found in that layer,
|
224
226
|
Unpoly will search in other layers, starting from the topmost layer.
|
225
|
-
@param {
|
227
|
+
@param {string} [options.failLayer='auto']
|
226
228
|
The name of the layer that ought to be updated if the server sends a non-200 status code.
|
227
229
|
|
228
230
|
@return {Promise}
|
229
|
-
A promise that will be
|
231
|
+
A promise that will be fulfilled when the page has been updated.
|
230
232
|
@stable
|
231
233
|
###
|
232
234
|
replace = (selectorOrElement, url, options) ->
|
233
235
|
options = u.options(options)
|
234
236
|
|
237
|
+
options.inspectResponse = fullLoad = -> up.browser.navigate(url, u.only(options, 'method', 'data'))
|
238
|
+
|
235
239
|
if !up.browser.canPushState() && options.history != false
|
236
|
-
unless options.preload
|
237
|
-
up.browser.loadPage(url, u.only(options, 'method', 'data'))
|
240
|
+
fullLoad() unless options.preload
|
238
241
|
return u.unresolvablePromise()
|
239
242
|
|
240
|
-
options.inspectResponse = -> up.browser.loadPage(url, u.only(options, 'method', 'data'))
|
241
|
-
|
242
243
|
successOptions = u.merge options,
|
243
244
|
humanizedTarget: 'target'
|
244
245
|
|
@@ -248,80 +249,78 @@ up.dom = (($) ->
|
|
248
249
|
u.renameKey(failureOptions, 'failTransition', 'transition')
|
249
250
|
u.renameKey(failureOptions, 'failLayer', 'layer')
|
250
251
|
|
251
|
-
|
252
|
-
|
252
|
+
try
|
253
|
+
improvedTarget = bestPreflightSelector(selectorOrElement, successOptions)
|
254
|
+
improvedFailTarget = bestPreflightSelector(options.failTarget, failureOptions)
|
255
|
+
catch e
|
256
|
+
# Since we're an async function, we should not throw exceptions but return a rejected promise.
|
257
|
+
# http://2ality.com/2016/03/promise-rejections-vs-exceptions.html
|
258
|
+
return Promise.reject(e)
|
253
259
|
|
254
260
|
request =
|
255
261
|
url: url
|
256
262
|
method: options.method
|
257
263
|
data: options.data
|
258
|
-
target:
|
259
|
-
failTarget:
|
264
|
+
target: improvedTarget
|
265
|
+
failTarget: improvedFailTarget
|
260
266
|
cache: options.cache
|
261
267
|
preload: options.preload
|
262
268
|
headers: options.headers
|
263
269
|
timeout: options.timeout
|
264
270
|
|
265
|
-
onSuccess = (
|
266
|
-
processResponse(true,
|
271
|
+
onSuccess = (response) ->
|
272
|
+
processResponse(true, improvedTarget, response, successOptions)
|
267
273
|
|
268
|
-
onFailure = (
|
269
|
-
rejection = ->
|
270
|
-
if
|
271
|
-
promise = processResponse(false, failTarget, url, request, xhr, failureOptions)
|
272
|
-
promise.then(rejection, rejection)
|
273
|
-
else
|
274
|
+
onFailure = (response) ->
|
275
|
+
rejection = -> Promise.reject(response)
|
276
|
+
if response.isFatalError()
|
274
277
|
rejection()
|
278
|
+
else
|
279
|
+
promise = processResponse(false, improvedFailTarget, response, failureOptions)
|
280
|
+
# Although processResponse() we will perform a successful replacement of options.failTarget,
|
281
|
+
# we still want to reject the promise that's returned to our API client.
|
282
|
+
u.always(promise, rejection)
|
275
283
|
|
276
|
-
promise = up.
|
277
|
-
promise = promise.then(onSuccess, onFailure)
|
284
|
+
promise = up.request(request)
|
285
|
+
promise = promise.then(onSuccess, onFailure) unless options.preload
|
278
286
|
promise
|
279
287
|
|
280
288
|
###*
|
281
289
|
@internal
|
282
290
|
###
|
283
|
-
processResponse = (isSuccess, selector,
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
url: url
|
296
|
-
method: up.protocol.methodFromXhr(xhr) # If the server redirects, we must use the signaled method (should be GET)
|
297
|
-
target: selector
|
298
|
-
up.proxy.alias(request, newRequest)
|
299
|
-
else if isReloadable
|
300
|
-
if query = u.requestDataAsQuery(options.data)
|
301
|
-
url = "#{url}?#{query}"
|
291
|
+
processResponse = (isSuccess, selector, response, options) ->
|
292
|
+
request = response.request
|
293
|
+
sourceUrl = response.url
|
294
|
+
historyUrl = sourceUrl
|
295
|
+
hash = request.hash
|
296
|
+
|
297
|
+
if options.reveal == true && hash
|
298
|
+
# If the request URL had a #hash and options.reveal is not given, we reveal that #hash.
|
299
|
+
options.reveal = hash
|
300
|
+
historyUrl += hash
|
301
|
+
|
302
|
+
isReloadable = (response.method == 'GET')
|
302
303
|
|
303
304
|
if isSuccess
|
304
305
|
if isReloadable # e.g. GET returns 200 OK
|
305
|
-
options.history =
|
306
|
-
options.source =
|
306
|
+
options.history = historyUrl unless options.history is false || u.isString(options.history)
|
307
|
+
options.source = sourceUrl unless options.source is false || u.isString(options.source)
|
307
308
|
else # e.g. POST returns 200 OK
|
308
|
-
|
309
|
+
# We allow the developer to pass GETable URLs as { history } and { source } options.
|
310
|
+
options.history = false unless u.isString(options.history)
|
309
311
|
options.source = 'keep' unless u.isString(options.source)
|
310
312
|
else
|
311
313
|
if isReloadable # e.g. GET returns 500 Internal Server Error
|
312
|
-
options.history =
|
313
|
-
options.source =
|
314
|
+
options.history = historyUrl unless options.history is false
|
315
|
+
options.source = sourceUrl unless options.source is false
|
314
316
|
else # e.g. POST returns 500 Internal Server Error
|
315
|
-
options.source = 'keep'
|
316
317
|
options.history = false
|
318
|
+
options.source = 'keep'
|
317
319
|
|
318
|
-
if shouldExtractTitle(options) &&
|
319
|
-
options.title =
|
320
|
+
if shouldExtractTitle(options) && response.title
|
321
|
+
options.title = response.title
|
320
322
|
|
321
|
-
|
322
|
-
u.resolvedPromise()
|
323
|
-
else
|
324
|
-
extract(selector, xhr.responseText, options)
|
323
|
+
extract(selector, response.text, options)
|
325
324
|
|
326
325
|
shouldExtractTitle = (options) ->
|
327
326
|
not (options.title is false || u.isString(options.title) || (options.history is false && options.title isnt true))
|
@@ -355,12 +354,12 @@ up.dom = (($) ->
|
|
355
354
|
discarded, since it didn't match the selector.
|
356
355
|
|
357
356
|
@function up.extract
|
358
|
-
@param {
|
359
|
-
@param {
|
357
|
+
@param {string|Element|jQuery} selectorOrElement
|
358
|
+
@param {string} html
|
360
359
|
@param {Object} [options]
|
361
360
|
See options for [`up.replace()`](/up.replace).
|
362
361
|
@return {Promise}
|
363
|
-
A promise that will be
|
362
|
+
A promise that will be fulfilled then the selector was updated
|
364
363
|
and all animation has finished.
|
365
364
|
@stable
|
366
365
|
###
|
@@ -374,39 +373,45 @@ up.dom = (($) ->
|
|
374
373
|
|
375
374
|
up.layout.saveScroll() unless options.saveScroll == false
|
376
375
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
options
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
376
|
+
u.rejectOnError ->
|
377
|
+
# Allow callers to create the targeted element right before we swap.
|
378
|
+
options.provideTarget?()
|
379
|
+
responseDoc = parseResponseDoc(html)
|
380
|
+
extractSteps = bestMatchingSteps(selectorOrElement, responseDoc, options)
|
381
|
+
|
382
|
+
if shouldExtractTitle(options) && responseTitle = responseDoc.title()
|
383
|
+
options.title = responseTitle
|
384
|
+
updateHistoryAndTitle(options)
|
385
|
+
|
386
|
+
swapPromises = []
|
387
|
+
for step in extractSteps
|
388
|
+
up.log.group 'Updating %s', step.selector, ->
|
389
|
+
filterScripts(step.$new, options)
|
390
|
+
swapPromise = swapElements(step.$old, step.$new, step.pseudoClass, step.transition, options)
|
391
|
+
swapPromises.push(swapPromise)
|
392
|
+
# When extracting multiple selectors, we only want to reveal the first element.
|
393
|
+
# So we set the { reveal } option to false for the next iteration.
|
394
|
+
# Note that we must copy the options hash instead of changing it in-place, since the
|
395
|
+
# async swapElements() is scheduled for the next microtask and we must not change the options
|
396
|
+
# for the previous iteration.
|
397
|
+
options = u.merge(options, reveal: false)
|
398
|
+
|
399
|
+
# Delay all further links in the promise chain until all fragments have been swapped
|
400
|
+
Promise.all(swapPromises)
|
396
401
|
|
397
402
|
bestPreflightSelector = (selector, options) ->
|
398
|
-
cascade = new up.
|
403
|
+
cascade = new up.ExtractCascade(selector, options)
|
399
404
|
cascade.bestPreflightSelector()
|
400
405
|
|
401
406
|
bestMatchingSteps = (selector, response, options) ->
|
402
407
|
options = u.merge(options, response: response)
|
403
|
-
cascade = new up.
|
408
|
+
cascade = new up.ExtractCascade(selector, options)
|
404
409
|
cascade.bestMatchingSteps()
|
405
410
|
|
406
411
|
filterScripts = ($element, options) ->
|
407
412
|
runInlineScripts = u.option(options.runInlineScripts, config.runInlineScripts)
|
408
413
|
runLinkedScripts = u.option(options.runLinkedScripts, config.runLinkedScripts)
|
409
|
-
$scripts = u.
|
414
|
+
$scripts = u.selectInSubtree($element, 'script')
|
410
415
|
for script in $scripts
|
411
416
|
$script = $(script)
|
412
417
|
isLinked = u.isPresent($script.attr('src'))
|
@@ -414,7 +419,7 @@ up.dom = (($) ->
|
|
414
419
|
unless (isLinked && runLinkedScripts) || (isInline && runInlineScripts)
|
415
420
|
$script.remove()
|
416
421
|
|
417
|
-
|
422
|
+
parseResponseDoc = (html) ->
|
418
423
|
# jQuery cannot construct transient elements that contain <html> or <body> tags
|
419
424
|
htmlElement = u.createElementFromHtml(html)
|
420
425
|
title: ->
|
@@ -471,7 +476,7 @@ up.dom = (($) ->
|
|
471
476
|
# Since we're keeping the element that was requested to be swapped,
|
472
477
|
# there is nothing left to do here.
|
473
478
|
emitFragmentKept(keepPlan)
|
474
|
-
promise =
|
479
|
+
promise = Promise.resolve()
|
475
480
|
|
476
481
|
else
|
477
482
|
# This needs to happen before prepareClean() below.
|
@@ -509,7 +514,7 @@ up.dom = (($) ->
|
|
509
514
|
|
510
515
|
# Wrap the replacement as a destroy animation, so $old will
|
511
516
|
# get marked as .up-destroying right away.
|
512
|
-
promise = destroy($old, { clean,
|
517
|
+
promise = destroy($old, { clean, beforeWipe: replacement, log: false })
|
513
518
|
|
514
519
|
promise
|
515
520
|
|
@@ -550,7 +555,7 @@ up.dom = (($) ->
|
|
550
555
|
if options.descendantsOnly
|
551
556
|
$partner = $new.find(partnerSelector)
|
552
557
|
else
|
553
|
-
$partner = u.
|
558
|
+
$partner = u.selectInSubtree($new, partnerSelector)
|
554
559
|
$partner = $partner.first()
|
555
560
|
if $partner.length && $partner.is('[up-keep]')
|
556
561
|
plan =
|
@@ -624,9 +629,9 @@ up.dom = (($) ->
|
|
624
629
|
Event listeners may call this method to prevent the element from being preserved.
|
625
630
|
@param {jQuery} event.$element
|
626
631
|
The fragment that will be kept.
|
627
|
-
@param {
|
632
|
+
@param {jQuery} event.$newElement
|
628
633
|
The discarded element.
|
629
|
-
@param {
|
634
|
+
@param {Object} event.newData
|
630
635
|
The value of the [`up-data`](/up-data) attribute of the discarded element,
|
631
636
|
parsed as a JSON object.
|
632
637
|
@stable
|
@@ -642,9 +647,9 @@ up.dom = (($) ->
|
|
642
647
|
@event up:fragment:kept
|
643
648
|
@param {jQuery} event.$element
|
644
649
|
The fragment that has been kept.
|
645
|
-
@param {
|
650
|
+
@param {jQuery} event.$newElement
|
646
651
|
The discarded element.
|
647
|
-
@param {
|
652
|
+
@param {Object} event.newData
|
648
653
|
The value of the [`up-data`](/up-data) attribute of the discarded element,
|
649
654
|
parsed as a JSON object.
|
650
655
|
@stable
|
@@ -668,9 +673,9 @@ up.dom = (($) ->
|
|
668
673
|
event.
|
669
674
|
|
670
675
|
@function up.hello
|
671
|
-
@param {
|
672
|
-
@param {
|
673
|
-
@param {
|
676
|
+
@param {string|Element|jQuery} selectorOrElement
|
677
|
+
@param {string|Element|jQuery} [options.origin]
|
678
|
+
@param {string|Element|jQuery} [options.kept]
|
674
679
|
@return {jQuery}
|
675
680
|
The compiled element
|
676
681
|
@stable
|
@@ -714,7 +719,7 @@ up.dom = (($) ->
|
|
714
719
|
|
715
720
|
autofocus = ($element) ->
|
716
721
|
selector = '[autofocus]:last'
|
717
|
-
$control = u.
|
722
|
+
$control = u.selectInSubtree($element, selector)
|
718
723
|
if $control.length && $control.get(0) != document.activeElement
|
719
724
|
$control.focus()
|
720
725
|
|
@@ -735,16 +740,16 @@ up.dom = (($) ->
|
|
735
740
|
Returns `undefined` if no element matches these conditions.
|
736
741
|
|
737
742
|
@function up.first
|
738
|
-
@param {
|
739
|
-
@param {
|
743
|
+
@param {string|Element|jQuery|Array<Element>} selectorOrElement
|
744
|
+
@param {string} options.layer
|
740
745
|
The name of the layer in which to find the element. Valid values are
|
741
746
|
`auto`, `page`, `modal` and `popup`.
|
742
|
-
@param {
|
747
|
+
@param {string|Element|jQuery} [options.origin]
|
743
748
|
An second element or selector that can be referenced as `&` in the first selector:
|
744
749
|
|
745
750
|
$input = $('input.email');
|
746
751
|
up.first('.field:has(&)', $input); // returns the .field containing $input
|
747
|
-
@return {jQuery|
|
752
|
+
@return {jQuery|undefined}
|
748
753
|
The first element that is neither a ghost or being destroyed,
|
749
754
|
or `undefined` if no such element was found.
|
750
755
|
@experimental
|
@@ -804,55 +809,61 @@ up.dom = (($) ->
|
|
804
809
|
Emits events [`up:fragment:destroy`](/up:fragment:destroy) and [`up:fragment:destroyed`](/up:fragment:destroyed).
|
805
810
|
|
806
811
|
@function up.destroy
|
807
|
-
@param {
|
808
|
-
@param {
|
812
|
+
@param {string|Element|jQuery} selectorOrElement
|
813
|
+
@param {string} [options.history]
|
809
814
|
A URL that will be pushed as a new history entry when the element begins destruction.
|
810
|
-
@param {
|
815
|
+
@param {string} [options.title]
|
811
816
|
The document title to set when the element begins destruction.
|
812
|
-
@param {
|
817
|
+
@param {string|Function} [options.animation='none']
|
813
818
|
The animation to use before the element is removed from the DOM.
|
814
|
-
@param {
|
819
|
+
@param {number} [options.duration]
|
815
820
|
The duration of the animation. See [`up.animate()`](/up.animate).
|
816
|
-
@param {
|
821
|
+
@param {number} [options.delay]
|
817
822
|
The delay before the animation starts. See [`up.animate()`](/up.animate).
|
818
|
-
@param {
|
823
|
+
@param {string} [options.easing]
|
819
824
|
The timing function that controls the animation's acceleration. [`up.animate()`](/up.animate).
|
820
|
-
@return {
|
821
|
-
A promise that will be
|
825
|
+
@return {Promise}
|
826
|
+
A promise that will be fulfilled once the element has been removed from the DOM.
|
822
827
|
@stable
|
823
828
|
###
|
824
829
|
destroy = (selectorOrElement, options) ->
|
830
|
+
$element = $(selectorOrElement)
|
825
831
|
options = u.options(options, animation: false)
|
826
832
|
|
827
|
-
$element
|
828
|
-
unless $element.is('.up-placeholder, .up-tooltip, .up-modal, .up-popup')
|
833
|
+
if shouldLogDestruction($element, options)
|
829
834
|
destroyMessage = ['Destroying fragment %o', $element.get(0)]
|
830
835
|
destroyedMessage = ['Destroyed fragment %o', $element.get(0)]
|
836
|
+
|
831
837
|
if $element.length == 0
|
832
|
-
|
833
|
-
else
|
834
|
-
|
838
|
+
Promise.resolve()
|
839
|
+
else
|
840
|
+
up.emit 'up:fragment:destroy', $element: $element, message: destroyMessage
|
835
841
|
$element.addClass('up-destroying')
|
836
842
|
# If e.g. a modal or popup asks us to restore a URL, do this
|
837
843
|
# before emitting `fragment:destroy`. This way up.navigate sees the
|
838
844
|
# new URL and can assign/remove .up-current classes accordingly.
|
839
845
|
updateHistoryAndTitle(options)
|
840
846
|
|
841
|
-
|
847
|
+
animate = ->
|
848
|
+
animateOptions = up.motion.animateOptions(options)
|
842
849
|
up.motion.animate($element, options.animation, animateOptions)
|
843
850
|
|
844
|
-
|
851
|
+
beforeWipe = options.beforeWipe || Promise.resolve()
|
852
|
+
|
853
|
+
wipe = ->
|
845
854
|
options.clean ||= -> up.syntax.clean($element)
|
846
855
|
options.clean()
|
856
|
+
up.syntax.clean($element)
|
847
857
|
# Emit this while $element is still part of the DOM, so event
|
848
858
|
# listeners bound to the document will receive the event.
|
849
859
|
up.emit 'up:fragment:destroyed', $element: $element, message: destroyedMessage
|
850
860
|
$element.remove()
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
861
|
+
|
862
|
+
animate().then(beforeWipe).then(wipe)
|
863
|
+
|
864
|
+
shouldLogDestruction = ($element, options) ->
|
865
|
+
# Don't log destruction for elements that are either Unpoly internals or frequently destroyed
|
866
|
+
options.log != false && !$element.is('.up-placeholder, .up-tooltip, .up-modal, .up-popup')
|
856
867
|
|
857
868
|
###*
|
858
869
|
Before a page fragment is being [destroyed](/up.destroy), this
|
@@ -864,8 +875,6 @@ up.dom = (($) ->
|
|
864
875
|
@event up:fragment:destroy
|
865
876
|
@param {jQuery} event.$element
|
866
877
|
The page fragment that is about to be destroyed.
|
867
|
-
@param event.preventDefault()
|
868
|
-
Event listeners may call this method to prevent the fragment from being destroyed.
|
869
878
|
@stable
|
870
879
|
###
|
871
880
|
|
@@ -895,10 +904,10 @@ up.dom = (($) ->
|
|
895
904
|
don't usually need to give an URL when reloading.
|
896
905
|
|
897
906
|
@function up.reload
|
898
|
-
@param {
|
907
|
+
@param {string|Element|jQuery} selectorOrElement
|
899
908
|
@param {Object} [options]
|
900
909
|
See options for [`up.replace()`](/up.replace)
|
901
|
-
@param {
|
910
|
+
@param {string} [options.url]
|
902
911
|
The URL from which to reload the fragment.
|
903
912
|
This defaults to the URL from which the fragment was originally loaded.
|
904
913
|
@stable
|