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.

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -3,7 +3,7 @@ Utility functions
3
3
  =================
4
4
 
5
5
  Unpoly comes with a number of utility functions
6
- that might save you from loading something like [Underscore.js](http://underscorejs.org/).
6
+ that might save you from loading something like [Lodash](https://lodash.com/).
7
7
 
8
8
  @class up.util
9
9
  ###
@@ -54,29 +54,34 @@ up.util = (($) ->
54
54
  By default hashes are ignored, search queries are included.
55
55
 
56
56
  @function up.util.normalizeUrl
57
- @param {Boolean} [options.hash=false]
57
+ @param {boolean} [options.hash=false]
58
58
  Whether to include an `#hash` anchor in the normalized URL
59
- @param {Boolean} [options.search=true]
59
+ @param {boolean} [options.search=true]
60
60
  Whether to include a `?query` string in the normalized URL
61
- @param {Boolean} [options.stripTrailingSlash=false]
61
+ @param {boolean} [options.stripTrailingSlash=false]
62
62
  Whether to strip a trailing slash from the pathname
63
63
  @internal
64
64
  ###
65
65
  normalizeUrl = (urlOrAnchor, options) ->
66
- anchor = parseUrl(urlOrAnchor)
67
- normalized = anchor.protocol + "//" + anchor.hostname
68
- normalized += ":#{anchor.port}" unless isStandardPort(anchor.protocol, anchor.port)
69
- pathname = anchor.pathname
66
+ parts = parseUrl(urlOrAnchor)
67
+ normalized = parts.protocol + "//" + parts.hostname
68
+ normalized += ":#{parts.port}" unless isStandardPort(parts.protocol, parts.port)
69
+ pathname = parts.pathname
70
70
  # Some IEs don't include a leading slash in the #pathname property.
71
71
  # We have seen this in IE11 and W3Schools claims it happens in IE9 or earlier
72
72
  # http://www.w3schools.com/jsref/prop_anchor_pathname.asp
73
73
  pathname = "/#{pathname}" unless pathname[0] == '/'
74
74
  pathname = pathname.replace(/\/$/, '') if options?.stripTrailingSlash == true
75
75
  normalized += pathname
76
- normalized += anchor.hash if options?.hash == true
77
- normalized += anchor.search unless options?.search == false
76
+ normalized += parts.hash if options?.hash == true
77
+ normalized += parts.search unless options?.search == false
78
78
  normalized
79
79
 
80
+ isCrossDomain = (targetUrl) ->
81
+ currentUrl = parseUrl(location.href)
82
+ targetUrl = parseUrl(targetUrl)
83
+ currentUrl.protocol != targetUrl.protocol || currentUrl.host != targetUrl.host
84
+
80
85
  ###*
81
86
  Parses the given URL into components such as hostname and path.
82
87
 
@@ -91,16 +96,19 @@ up.util = (($) ->
91
96
  @experimental
92
97
  ###
93
98
  parseUrl = (urlOrAnchor) ->
94
- anchor = null
95
- if isString(urlOrAnchor)
96
- anchor = $('<a>').attr(href: urlOrAnchor).get(0)
97
- # In IE11 the #hostname and #port properties of such a link are empty
98
- # strings. However, we can fix this by assigning the anchor its own
99
- # href because computer:
100
- # https://gist.github.com/jlong/2428561#comment-1461205
101
- anchor.href = anchor.href if isBlank(anchor.hostname)
102
- else
103
- anchor = unJQuery(urlOrAnchor)
99
+ # In case someone passed us a $link, unwrap it
100
+ urlOrAnchor = unJQuery(urlOrAnchor)
101
+
102
+ # If we are handed a parsed URL, just return it
103
+ if urlOrAnchor.pathname
104
+ return urlOrAnchor
105
+
106
+ anchor = $('<a>').attr(href: urlOrAnchor).get(0)
107
+ # In IE11 the #hostname and #port properties of such a link are empty
108
+ # strings. However, we can fix this by assigning the anchor its own
109
+ # href because computer:
110
+ # https://gist.github.com/jlong/2428561#comment-1461205
111
+ anchor.href = anchor.href if isBlank(anchor.hostname)
104
112
  anchor
105
113
 
106
114
  ###*
@@ -113,6 +121,13 @@ up.util = (($) ->
113
121
  else
114
122
  'GET'
115
123
 
124
+ ###*
125
+ @function up.util.methodAllowsPayload
126
+ @internal
127
+ ###
128
+ methodAllowsPayload = (method) ->
129
+ method != 'GET' && method != 'HEAD'
130
+
116
131
  ###*
117
132
  @function $createElementFromSelector
118
133
  @internal
@@ -169,7 +184,7 @@ up.util = (($) ->
169
184
  - The element's tag names
170
185
 
171
186
  @function up.util.selectorForElement
172
- @param {String|Element|jQuery}
187
+ @param {string|Element|jQuery}
173
188
  The element for which to create a selector.
174
189
  @experimental
175
190
  ###
@@ -231,9 +246,9 @@ up.util = (($) ->
231
246
  Returns a new string with whitespace removed from the beginning
232
247
  and end of the given string.
233
248
 
234
- @param {String}
249
+ @param {string}
235
250
  A string that might have whitespace at the beginning and end.
236
- @return {String}
251
+ @return {string}
237
252
  The trimmed string.
238
253
  @stable
239
254
  ###
@@ -244,8 +259,8 @@ up.util = (($) ->
244
259
  of the given array.
245
260
 
246
261
  @function up.util.each
247
- @param {Array} array
248
- @param {Function<Object, Number>} block
262
+ @param {Array<T>} array
263
+ @param {Function(T, number)} block
249
264
  A function that will be called with each element and (optional) iteration index.
250
265
  @stable
251
266
  ###
@@ -256,8 +271,8 @@ up.util = (($) ->
256
271
  Translate all items in an array to new array of items.
257
272
 
258
273
  @function up.util.map
259
- @param {Array} array
260
- @param {Function<Object, Number>} block
274
+ @param {Array<T>} array
275
+ @param {Function(T, number): any} block
261
276
  A function that will be called with each element and (optional) iteration index.
262
277
  @return {Array}
263
278
  A new array containing the result of each function call.
@@ -269,7 +284,7 @@ up.util = (($) ->
269
284
  Calls the given function for the given number of times.
270
285
 
271
286
  @function up.util.times
272
- @param {Number} count
287
+ @param {number} count
273
288
  @param {Function} block
274
289
  @stable
275
290
  ###
@@ -281,7 +296,7 @@ up.util = (($) ->
281
296
 
282
297
  @function up.util.isNull
283
298
  @param object
284
- @return {Boolean}
299
+ @return {boolean}
285
300
  @stable
286
301
  ###
287
302
  isNull = (object) ->
@@ -292,18 +307,18 @@ up.util = (($) ->
292
307
 
293
308
  @function up.util.isUndefined
294
309
  @param object
295
- @return {Boolean}
310
+ @return {boolean}
296
311
  @stable
297
312
  ###
298
313
  isUndefined = (object) ->
299
- object == `void(0)`
314
+ object == undefined
300
315
 
301
316
  ###*
302
317
  Returns whether the given argument is not `undefined`.
303
318
 
304
319
  @function up.util.isDefined
305
320
  @param object
306
- @return {Boolean}
321
+ @return {boolean}
307
322
  @stable
308
323
  ###
309
324
  isDefined = (object) ->
@@ -318,7 +333,7 @@ up.util = (($) ->
318
333
 
319
334
  @function up.util.isMissing
320
335
  @param object
321
- @return {Boolean}
336
+ @return {boolean}
322
337
  @stable
323
338
  ###
324
339
  isMissing = (object) ->
@@ -333,7 +348,7 @@ up.util = (($) ->
333
348
 
334
349
  @function up.util.isGiven
335
350
  @param object
336
- @return {Boolean}
351
+ @return {boolean}
337
352
  @stable
338
353
  ###
339
354
  isGiven = (object) ->
@@ -357,13 +372,13 @@ up.util = (($) ->
357
372
 
358
373
  @function up.util.isBlank
359
374
  @param object
360
- @return {Boolean}
375
+ @return {boolean}
361
376
  @stable
362
377
  ###
363
378
  isBlank = (object) ->
364
379
  isMissing(object) || # null or undefined
365
380
  (isObject(object) && Object.keys(object).length == 0) ||
366
- (object.length == 0) # String, Array, jQuery
381
+ (object.length == 0) # string, Array, jQuery
367
382
 
368
383
  ###*
369
384
  Returns the given argument if the argument is [present](/up.util.isPresent),
@@ -371,9 +386,9 @@ up.util = (($) ->
371
386
 
372
387
  @function up.util.presence
373
388
  @param object
374
- @param {Function<T>} [tester=up.util.isPresent]
389
+ @param {Function(T): boolean} [tester=up.util.isPresent]
375
390
  The function that will be used to test whether the argument is present.
376
- @return {T|Undefined}
391
+ @return {T|undefined}
377
392
  @stable
378
393
  ###
379
394
  presence = (object, tester = isPresent) ->
@@ -384,7 +399,7 @@ up.util = (($) ->
384
399
 
385
400
  @function up.util.isPresent
386
401
  @param object
387
- @return {Boolean}
402
+ @return {boolean}
388
403
  @stable
389
404
  ###
390
405
  isPresent = (object) ->
@@ -395,7 +410,7 @@ up.util = (($) ->
395
410
 
396
411
  @function up.util.isFunction
397
412
  @param object
398
- @return {Boolean}
413
+ @return {boolean}
399
414
  @stable
400
415
  ###
401
416
  isFunction = (object) ->
@@ -406,11 +421,11 @@ up.util = (($) ->
406
421
 
407
422
  @function up.util.isString
408
423
  @param object
409
- @return {Boolean}
424
+ @return {boolean}
410
425
  @stable
411
426
  ###
412
427
  isString = (object) ->
413
- typeof(object) == 'string'
428
+ typeof(object) == 'string' || object instanceof String
414
429
 
415
430
  ###*
416
431
  Returns whether the given argument is a number.
@@ -420,43 +435,46 @@ up.util = (($) ->
420
435
 
421
436
  @function up.util.isNumber
422
437
  @param object
423
- @return {Boolean}
438
+ @return {boolean}
424
439
  @stable
425
440
  ###
426
441
  isNumber = (object) ->
427
- typeof(object) == 'number'
442
+ typeof(object) == 'number' || object instanceof Number
428
443
 
429
444
  ###*
430
- Returns whether the given argument is an object, but not a function.
445
+ Returns whether the given argument is an options hash,
446
+
447
+ Differently from [`up.util.isObject()`], this returns false for
448
+ functions, jQuery collections, promises, `FormData` instances and arrays.
431
449
 
432
- @function up.util.isHash
450
+ @function up.util.isOptions
433
451
  @param object
434
- @return {Boolean}
435
- @stable
452
+ @return {boolean}
453
+ @internal
436
454
  ###
437
- isHash = (object) ->
438
- typeof(object) == 'object' && !!object
455
+ isOptions = (object) ->
456
+ typeof(object) == 'object' && !isNull(object) && !isJQuery(object) && !isPromise(object) && !isFormData(object) && !isArray(object)
439
457
 
440
458
  ###*
441
459
  Returns whether the given argument is an object.
442
460
 
443
461
  This also returns `true` for functions, which may behave like objects in JavaScript.
444
- For an alternative that returns `false` for functions, see [`up.util.isHash()`](/up.util.isHash).
445
462
 
446
463
  @function up.util.isObject
447
464
  @param object
448
- @return {Boolean}
465
+ @return {boolean}
449
466
  @stable
450
467
  ###
451
468
  isObject = (object) ->
452
- isHash(object) || (typeof object == 'function')
469
+ typeOfResult = typeof(object)
470
+ (typeOfResult == 'object' && !isNull(object)) || typeOfResult == 'function'
453
471
 
454
472
  ###*
455
473
  Returns whether the given argument is a DOM element.
456
474
 
457
475
  @function up.util.isElement
458
476
  @param object
459
- @return {Boolean}
477
+ @return {boolean}
460
478
  @stable
461
479
  ###
462
480
  isElement = (object) ->
@@ -467,7 +485,7 @@ up.util = (($) ->
467
485
 
468
486
  @function up.util.isJQuery
469
487
  @param object
470
- @return {Boolean}
488
+ @return {boolean}
471
489
  @stable
472
490
  ###
473
491
  isJQuery = (object) ->
@@ -478,34 +496,22 @@ up.util = (($) ->
478
496
 
479
497
  @function up.util.isPromise
480
498
  @param object
481
- @return {Boolean}
499
+ @return {boolean}
482
500
  @stable
483
501
  ###
484
502
  isPromise = (object) ->
485
503
  isObject(object) && isFunction(object.then)
486
504
 
487
- ###*
488
- Returns whether the given argument is an object with `then` and `resolve` methods.
489
-
490
- @function up.util.isDeferred
491
- @param object
492
- @return {Boolean}
493
- @stable
494
- ###
495
- isDeferred = (object) ->
496
- isPromise(object) && isFunction(object.resolve)
497
-
498
505
  ###*
499
506
  Returns whether the given argument is an array.
500
507
 
501
508
  @function up.util.isArray
502
509
  @param object
503
- @return {Boolean}
510
+ @return {boolean}
504
511
  @stable
505
512
  ###
506
513
  # https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
507
- isArray = Array.isArray ||
508
- (object) -> Object.prototype.toString.call(object) == '[object Array]'
514
+ isArray = Array.isArray
509
515
 
510
516
  ###*
511
517
  Returns whether the given argument is a `FormData` instance.
@@ -514,7 +520,7 @@ up.util = (($) ->
514
520
 
515
521
  @function up.util.isFormData
516
522
  @param object
517
- @return {Boolean}
523
+ @return {boolean}
518
524
  @internal
519
525
  ###
520
526
  isFormData = (object) ->
@@ -534,9 +540,7 @@ up.util = (($) ->
534
540
  Array.prototype.slice.call(object)
535
541
 
536
542
  ###*
537
- Shallow-copies the given array or object into a new array or object.
538
-
539
- Returns the new array or object.
543
+ Returns a shallow copy of the given array or object.
540
544
 
541
545
  @function up.util.copy
542
546
  @param {Object|Array} object
@@ -545,9 +549,12 @@ up.util = (($) ->
545
549
  ###
546
550
  copy = (object) ->
547
551
  if isArray(object)
548
- object.slice()
549
- else if isHash(object)
550
- assign({}, object)
552
+ object = object.slice()
553
+ else if isObject(object) && !isFunction(object)
554
+ object = assign({}, object)
555
+ else
556
+ up.fail('Cannot copy %o', object)
557
+ object
551
558
 
552
559
  ###*
553
560
  If given a jQuery collection, returns the underlying array of DOM element.
@@ -619,8 +626,8 @@ up.util = (($) ->
619
626
 
620
627
  @function up.util.detect
621
628
  @param {Array<T>} array
622
- @param {Function<T>} tester
623
- @return {T|Undefined}
629
+ @param {Function(T): boolean} tester
630
+ @return {T|undefined}
624
631
  @stable
625
632
  ###
626
633
  detect = (array, tester) ->
@@ -637,8 +644,8 @@ up.util = (($) ->
637
644
 
638
645
  @function up.util.any
639
646
  @param {Array<T>} array
640
- @param {Function<T>} tester
641
- @return {Boolean}
647
+ @param {Function(T): boolean} tester
648
+ @return {boolean}
642
649
  @experimental
643
650
  ###
644
651
  any = (array, tester) ->
@@ -655,8 +662,8 @@ up.util = (($) ->
655
662
 
656
663
  @function up.util.all
657
664
  @param {Array<T>} array
658
- @param {Function<T>} tester
659
- @return {Boolean}
665
+ @param {Function(T): boolean} tester
666
+ @return {boolean}
660
667
  @experimental
661
668
  ###
662
669
  all = (array, tester) ->
@@ -747,23 +754,17 @@ up.util = (($) ->
747
754
  detect(values, isPresent)
748
755
 
749
756
  ###*
750
- Waits for the given number of milliseconds, the nruns the given callback.
757
+ Waits for the given number of milliseconds, the runs the given callback.
751
758
 
752
- If the number of milliseconds is zero, the callback is run in the current execution frame.
753
- See [`up.util.nextFrame()`](/up.util.nextFrame) for running a function in the next executation frame.
759
+ Instead of `up.util.setTimer(0, fn)` you can also use [`up.util.nextFrame(fn)`](/up.util.nextFrame).
754
760
 
755
761
  @function up.util.setTimer
756
- @param {Number} millis
762
+ @param {number} millis
757
763
  @param {Function} callback
758
- @experimental
764
+ @stable
759
765
  ###
760
766
  setTimer = (millis, callback) ->
761
- if millis > 0
762
- setTimeout(callback, millis)
763
- else
764
- callback()
765
- undefined
766
-
767
+ setTimeout(callback, millis)
767
768
 
768
769
  ###*
769
770
  Schedules the given function to be called in the
@@ -776,6 +777,16 @@ up.util = (($) ->
776
777
  nextFrame = (block) ->
777
778
  setTimeout(block, 0)
778
779
 
780
+ ###*
781
+ Queue a function to be executed in the next microtask.
782
+
783
+ @function up.util.queueMicrotask
784
+ @param {Function} task
785
+ @internal
786
+ ###
787
+ microtask = (task) ->
788
+ Promise.resolve().then(task)
789
+
779
790
  ###*
780
791
  Returns the last element of the given array.
781
792
 
@@ -909,124 +920,7 @@ up.util = (($) ->
909
920
  element = unJQuery(element)
910
921
  element.offsetHeight
911
922
 
912
- ###*
913
- Animates the given element's CSS properties using CSS transitions.
914
-
915
- If the element is already being animated, the previous animation
916
- will instantly jump to its last frame before the new animation begins.
917
-
918
- To improve performance, the element will be forced into compositing for
919
- the duration of the animation.
920
-
921
- @function up.util.cssAnimate
922
- @param {Element|jQuery|String} elementOrSelector
923
- The element to animate.
924
- @param {Object} lastFrame
925
- The CSS properties that should be transitioned to.
926
- @param {Number} [options.duration=300]
927
- The duration of the animation, in milliseconds.
928
- @param {Number} [options.delay=0]
929
- The delay before the animation starts, in milliseconds.
930
- @param {String} [options.easing='ease']
931
- The timing function that controls the animation's acceleration.
932
- See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
933
- for a list of pre-defined timing functions.
934
- @return {Deferred}
935
- A promise for the animation's end.
936
- @internal
937
- ###
938
923
  cssAnimate = (elementOrSelector, lastFrame, opts) ->
939
- $element = $(elementOrSelector)
940
-
941
- # Don't name the local variable `options` since that would override
942
- # the `options` function in our scope. We really need `let` :(
943
- opts = options(opts,
944
- duration: 300,
945
- delay: 0,
946
- easing: 'ease'
947
- )
948
-
949
- if opts.duration == 0
950
- # In case the duration is zero we 1) spare ourselves all the trouble below,
951
- # and 2) return a deferred that actually resolve, since a CSS transition with
952
- # a zero duration never fires a transitionEnd event.
953
- $element.css(lastFrame)
954
- return resolvedDeferred()
955
-
956
- # We don't finish an existing animation here, since the public API
957
- # we expose as `up.motion.animate()` already does this.
958
- deferred = $.Deferred()
959
-
960
- transitionProperties = Object.keys(lastFrame)
961
- transition =
962
- 'transition-property': transitionProperties.join(', ')
963
- 'transition-duration': "#{opts.duration}ms"
964
- 'transition-delay': "#{opts.delay}ms"
965
- 'transition-timing-function': opts.easing
966
- oldTransition = $element.css(Object.keys(transition))
967
-
968
- onTransitionEnd = (event) ->
969
- completedProperty = event.originalEvent.propertyName
970
- # Check if the transitionend event was caused by our own transition,
971
- # and not by some other transition that happens to live on the same element.
972
- if contains(transitionProperties, completedProperty)
973
- deferred.resolve() # unless isDetached($element)
974
-
975
- $element.on('transitionend', onTransitionEnd)
976
-
977
- deferred.then ->
978
- $element.removeClass('up-animating')
979
- $element.off('transitionend', onTransitionEnd)
980
-
981
- $element.removeData(ANIMATION_DEFERRED_KEY)
982
- withoutCompositing()
983
-
984
- # To interrupt the running transition we *must* set it to 'none' exactly.
985
- # We cannot simply restore the old transition properties because browsers
986
- # would simply keep transitioning.
987
- $element.css('transition': 'none')
988
-
989
- # Restoring a previous transition involves forcing a repaint, so we only do it if
990
- # we know the element was transitioning before.
991
- # Note that the default transition for elements is actually "all 0s ease 0s"
992
- # instead of "none", although that has the same effect as "none".
993
- hadTransitionBefore = !(oldTransition['transition-property'] == 'none' || (oldTransition['transition-property'] == 'all' && oldTransition['transition-duration'][0] == '0'))
994
- if hadTransitionBefore
995
- # If there is no repaint between the "none" transition and restoring
996
- # the previous transition, the browser will simply keep transitioning.
997
- forceRepaint($element) # :(
998
- $element.css(oldTransition)
999
-
1000
- $element.addClass('up-animating')
1001
- withoutCompositing = forceCompositing($element)
1002
- $element.css(transition)
1003
- $element.data(ANIMATION_DEFERRED_KEY, deferred)
1004
- $element.css(lastFrame)
1005
-
1006
- # Return the whole deferred and not just return a thenable.
1007
- # Other code will need the possibility to cancel the animation
1008
- # by resolving the deferred.
1009
- deferred
1010
-
1011
- ANIMATION_DEFERRED_KEY = 'up-animation-deferred'
1012
-
1013
- ###*
1014
- Completes the animation for the given element by jumping
1015
- to the last frame instantly. All callbacks chained to
1016
- the original animation's promise will be called.
1017
-
1018
- Does nothing if the given element is not currently animating.
1019
-
1020
- Also see [`up.motion.finish()`](/up.motion.finish).
1021
-
1022
- @function up.util.finishCssAnimate
1023
- @param {Element|jQuery|String} elementOrSelector
1024
- @internal
1025
- ###
1026
- finishCssAnimate = (elementOrSelector) ->
1027
- $(elementOrSelector).each ->
1028
- if existingAnimation = pluckData(this, ANIMATION_DEFERRED_KEY)
1029
- existingAnimation.resolve()
1030
924
 
1031
925
  ###*
1032
926
  @internal
@@ -1100,12 +994,23 @@ up.util = (($) ->
1100
994
  ###*
1101
995
  Looks for the given selector in the element and its descendants.
1102
996
 
1103
- @function up.util.findWithSelf
997
+ @function up.util.selectInSubtree
1104
998
  @internal
1105
999
  ###
1106
- findWithSelf = ($element, selector) ->
1000
+ selectInSubtree = ($element, selector) ->
1107
1001
  $element.find(selector).addBack(selector)
1108
1002
 
1003
+ ###*
1004
+ Looks for the given selector in the element, its descendants and its ancestors.
1005
+
1006
+ @function up.util.selectInDynasty
1007
+ @internal
1008
+ ###
1009
+ selectInDynasty = ($element, selector) ->
1010
+ $subtree = selectInSubtree($element, selector)
1011
+ $ancestors = $element.parents(selector)
1012
+ $subtree.add($ancestors)
1013
+
1109
1014
  ###*
1110
1015
  Returns whether the given keyboard event involved the ESC key.
1111
1016
 
@@ -1119,7 +1024,7 @@ up.util = (($) ->
1119
1024
  Returns whether the given array or string contains the given element or substring.
1120
1025
 
1121
1026
  @function up.util.contains
1122
- @param {Array|String} arrayOrString
1027
+ @param {Array|string} arrayOrString
1123
1028
  @param elementOrSubstring
1124
1029
  @stable
1125
1030
  ###
@@ -1190,75 +1095,6 @@ up.util = (($) ->
1190
1095
  isLeftButton = isUndefined(event.button) || event.button == 0
1191
1096
  isLeftButton && isUnmodifiedKeyEvent(event)
1192
1097
 
1193
- ###*
1194
- Returns a [Deferred object](https://api.jquery.com/category/deferred-object/) that is
1195
- already resolved.
1196
-
1197
- @function up.util.resolvedDeferred
1198
- @param {Array<Object>} [args...]
1199
- The resolution values that will be passed to callbacks
1200
- @return {Deferred}
1201
- @stable
1202
- ###
1203
- resolvedDeferred = (args...) ->
1204
- deferred = $.Deferred()
1205
- deferred.resolve(args...)
1206
- deferred
1207
-
1208
- ###*
1209
- Returns a promise that is already resolved.
1210
-
1211
- @function up.util.resolvedPromise
1212
- @param {Array<Object>} [args...]
1213
- The resolution values that will be passed to callbacks
1214
- @return {Promise}
1215
- @stable
1216
- ###
1217
- resolvedPromise = (args...) ->
1218
- resolvedDeferred(args...).promise()
1219
-
1220
- ###*
1221
- Returns a promise that is already rejected.
1222
-
1223
- @function up.util.rejectedPromise
1224
- @param {Array<Object>} [args...]
1225
- The rejection values that will be passed to callbacks
1226
- @return {Promise}
1227
- @stable
1228
- ###
1229
- rejectedPromise = (args...) ->
1230
- deferred = $.Deferred()
1231
- deferred.reject(args...)
1232
- deferred.promise()
1233
-
1234
- ###*
1235
- Returns whether the given argument is a resolved jQuery promise.
1236
-
1237
- @return {Boolean}
1238
- @internal
1239
- ###
1240
- isResolvedPromise = (promise) ->
1241
- isPromise(promise) && promise.state?() == 'resolved'
1242
-
1243
- ###*
1244
- Returns whether the given argument is a resolved jQuery promise.
1245
-
1246
- @return {Boolean}
1247
- @internal
1248
- ###
1249
- isResolvedPromise = (promise) ->
1250
- isPromise(promise) && promise.state?() == 'resolved'
1251
-
1252
- ###*
1253
- Returns a [Deferred object](https://api.jquery.com/category/deferred-object/) that will never be resolved.
1254
-
1255
- @function up.util.unresolvableDeferred
1256
- @return {Deferred}
1257
- @experimental
1258
- ###
1259
- unresolvableDeferred = ->
1260
- $.Deferred()
1261
-
1262
1098
  ###*
1263
1099
  Returns a promise that will never be resolved.
1264
1100
 
@@ -1266,7 +1102,7 @@ up.util = (($) ->
1266
1102
  @experimental
1267
1103
  ###
1268
1104
  unresolvablePromise = ->
1269
- unresolvableDeferred().promise()
1105
+ new Promise(noop)
1270
1106
 
1271
1107
  ###*
1272
1108
  Returns an empty jQuery collection.
@@ -1277,39 +1113,6 @@ up.util = (($) ->
1277
1113
  nullJQuery = ->
1278
1114
  $()
1279
1115
 
1280
- ###*
1281
- Returns a new promise that resolves once all promises in arguments resolve.
1282
-
1283
- Other then [`$.when` from jQuery](https://api.jquery.com/jquery.when/),
1284
- the combined promise will have a `resolve` method. This `resolve` method
1285
- will resolve all the wrapped promises.
1286
-
1287
- @function up.util.resolvableWhen
1288
- @internal
1289
- ###
1290
- resolvableWhen = (deferreds...) ->
1291
- # Pass an additional resolved deferred to $.when so $.when will
1292
- # not return the given deferred if only one deferred is passed.
1293
- joined = $.when(resolvedDeferred(), deferreds...)
1294
- joined.resolve = memoize(->
1295
- each deferreds, (deferred) ->
1296
- deferred.resolve()
1297
- )
1298
- joined
1299
-
1300
- # resolvableSequence = (first, callbacks...) ->
1301
- # sequence = $.Deferred().promise()
1302
- # values = [first]
1303
- # current = first
1304
- # for callback in callbacks
1305
- # current = current.then ->
1306
- # value = callback()
1307
- # values.push(value) if u.isPromise(value)
1308
- # value
1309
- # sequence.resolve = ->
1310
- # each values, (deferred) -> deferred.resolve?()
1311
- # sequence
1312
-
1313
1116
  ###*
1314
1117
  On the given element, set attributes that are still missing.
1315
1118
 
@@ -1372,7 +1175,7 @@ up.util = (($) ->
1372
1175
  $result = $result.add($matches)
1373
1176
  $result
1374
1177
 
1375
- obj.findWithSelf = ($start) ->
1178
+ obj.selectInSubtree = ($start) ->
1376
1179
  $matches = obj.find($start)
1377
1180
  $matches = $matches.add($start) if obj.doesMatch($start)
1378
1181
  $matches
@@ -1407,113 +1210,6 @@ up.util = (($) ->
1407
1210
  else
1408
1211
  value
1409
1212
 
1410
- ###*
1411
- @function up.util.cache
1412
- @param {Number|Function} [config.size]
1413
- Maximum number of cache entries.
1414
- Set to `undefined` to not limit the cache size.
1415
- @param {Number|Function} [config.expiry]
1416
- The number of milliseconds after which a cache entry
1417
- will be discarded.
1418
- @param {String} [config.log]
1419
- A prefix for log entries printed by this cache object.
1420
- @param {Function<Object>} [config.key]
1421
- A function that takes an argument and returns a `String` key
1422
- for storage. If omitted, `toString()` is called on the argument.
1423
- @internal
1424
- ###
1425
- cache = (config = {}) ->
1426
-
1427
- store = undefined
1428
-
1429
- maxKeys = -> evalOption(config.size)
1430
- expiryMillis = -> evalOption(config.expiry)
1431
-
1432
- normalizeStoreKey = (key) ->
1433
- if config.key
1434
- config.key(key)
1435
- else
1436
- key.toString()
1437
-
1438
- isEnabled = ->
1439
- maxKeys() isnt 0 && expiryMillis() isnt 0
1440
-
1441
- clear = ->
1442
- store = {}
1443
-
1444
- clear()
1445
-
1446
- log = (args...) ->
1447
- if config.logPrefix
1448
- args[0] = "[#{config.logPrefix}] #{args[0]}"
1449
- up.puts(args...)
1450
-
1451
- keys = ->
1452
- Object.keys(store)
1453
-
1454
- makeRoomForAnotherKey = ->
1455
- storeKeys = copy(keys())
1456
- max = maxKeys()
1457
- if max && storeKeys.length >= max
1458
- oldestKey = null
1459
- oldestTimestamp = null
1460
- each storeKeys, (key) ->
1461
- promise = store[key] # we don't need to call cacheKey here
1462
- timestamp = promise.timestamp
1463
- if !oldestTimestamp || oldestTimestamp > timestamp
1464
- oldestKey = key
1465
- oldestTimestamp = timestamp
1466
- delete store[oldestKey] if oldestKey
1467
-
1468
- alias = (oldKey, newKey) ->
1469
- value = get(oldKey, silent: true)
1470
- if isDefined(value)
1471
- set(newKey, value)
1472
-
1473
- timestamp = ->
1474
- (new Date()).valueOf()
1475
-
1476
- set = (key, value) ->
1477
- if isEnabled()
1478
- makeRoomForAnotherKey()
1479
- storeKey = normalizeStoreKey(key)
1480
- store[storeKey] =
1481
- timestamp: timestamp()
1482
- value: value
1483
-
1484
- remove = (key) ->
1485
- storeKey = normalizeStoreKey(key)
1486
- delete store[storeKey]
1487
-
1488
- isFresh = (entry) ->
1489
- millis = expiryMillis()
1490
- if millis
1491
- timeSinceTouch = timestamp() - entry.timestamp
1492
- timeSinceTouch < millis
1493
- else
1494
- true
1495
-
1496
- get = (key, options = {}) ->
1497
- storeKey = normalizeStoreKey(key)
1498
- if entry = store[storeKey]
1499
- if isFresh(entry)
1500
- log("Cache hit for '%s'", key) unless options.silent
1501
- entry.value
1502
- else
1503
- log("Discarding stale cache entry for '%s'", key) unless options.silent
1504
- remove(key)
1505
- undefined
1506
- else
1507
- log("Cache miss for '%s'", key) unless options.silent
1508
- undefined
1509
-
1510
- alias: alias
1511
- get: get
1512
- set: set
1513
- remove: remove
1514
- clear: clear
1515
- keys: keys
1516
-
1517
1213
  ###*
1518
1214
  @function up.util.config
1519
1215
  @param {Object|Function} blueprint
@@ -1615,10 +1311,12 @@ up.util = (($) ->
1615
1311
  [`jQuery.serializeArray`](https://api.jquery.com/serializeArray/).
1616
1312
 
1617
1313
  @function up.util.requestDataAsArray
1618
- @param {Object|Array|Undefined|Null} data
1314
+ @param {Object|Array|undefined|null} data
1619
1315
  @internal
1620
1316
  ###
1621
1317
  requestDataAsArray = (data) ->
1318
+ if isArray(data)
1319
+ data
1622
1320
  if isFormData(data)
1623
1321
  # Until FormData#entries is implemented in all major browsers we must give up here.
1624
1322
  # However, up.form will prefer to serialize forms as arrays, so we should be good
@@ -1638,11 +1336,16 @@ up.util = (($) ->
1638
1336
  ###*
1639
1337
  Returns an URL-encoded query string for the given params object.
1640
1338
 
1339
+ The returned string does **not** include a leading `?` character.
1340
+
1641
1341
  @function up.util.requestDataAsQuery
1642
- @param {Object|Array|Undefined|Null} data
1342
+ @param {Object|Array|undefined|null} data
1643
1343
  @internal
1644
1344
  ###
1645
- requestDataAsQuery = (data) ->
1345
+ requestDataAsQuery = (data, opts) ->
1346
+ opts = options(opts, purpose: 'url')
1347
+ if isString(data)
1348
+ data
1646
1349
  if isFormData(data)
1647
1350
  # Until FormData#entries is implemented in all major browsers we must give up here.
1648
1351
  # However, up.form will prefer to serialize forms as arrays, so we should be good
@@ -1650,7 +1353,13 @@ up.util = (($) ->
1650
1353
  up.fail('Cannot convert FormData into a query string')
1651
1354
  else if isPresent(data)
1652
1355
  query = $.param(data)
1653
- query = query.replace(/\+/g, '%20')
1356
+ switch opts.purpose
1357
+ when 'url'
1358
+ query = query.replace(/\+/g, '%20')
1359
+ when 'form'
1360
+ query = query.replace(/\%20/g, '+')
1361
+ else
1362
+ up.fail('Unknown purpose %o', opts.purpose)
1654
1363
  query
1655
1364
  else
1656
1365
  ""
@@ -1681,7 +1390,7 @@ up.util = (($) ->
1681
1390
  # We try to stick with an array representation, whose contents we can inspect.
1682
1391
  # We cannot inspect FormData on IE11 because it has no support for `FormData.entries`.
1683
1392
  # Inspection is needed to generate a cache key (see `up.proxy`) and to make
1684
- # vanilla requests when `pushState` is unavailable (see `up.browser.loadPage`).
1393
+ # vanilla requests when `pushState` is unavailable (see `up.browser.navigate`).
1685
1394
  data = if hasFileInputs then new FormData($form.get(0)) else $form.serializeArray()
1686
1395
  appendRequestData(data, buttonName, buttonValue) if isPresent(buttonName)
1687
1396
  data
@@ -1691,28 +1400,27 @@ up.util = (($) ->
1691
1400
  Adds a key/value pair to the given request data representation.
1692
1401
 
1693
1402
  This mutates the given `data` if `data` is a `FormData`, an object
1694
- or an array. When `data` is `String` a new string with the appended key/value
1403
+ or an array. When `data` is a string a new string with the appended key/value
1695
1404
  pair is returned.
1696
1405
 
1697
1406
  @function up.util.appendRequestData
1698
- @param {FormData|Object|Array|Undefined|Null} data
1699
- @param {String} key
1700
- @param {String|Blob|File} value
1407
+ @param {FormData|Object|Array|undefined|null} data
1408
+ @param {string} key
1409
+ @param {string|Blob|File} value
1701
1410
  @internal
1702
1411
  ###
1703
- appendRequestData = (data, name, value) ->
1704
- if isFormData(data)
1705
- data.append(name, value)
1706
- else if isArray(data)
1412
+ appendRequestData = (data, name, value, opts) ->
1413
+ data ||= []
1414
+
1415
+ if isArray(data)
1707
1416
  data.push(name: name, value: value)
1417
+ else if isFormData(data)
1418
+ data.append(name, value)
1708
1419
  else if isObject(data)
1709
1420
  data[name] = value
1710
- else if isString(data) || isMissing(data)
1711
- newPair = requestDataAsQuery([ name: name, value: value ])
1712
- if isPresent(data)
1713
- data = [data, newPair].join('&')
1714
- else
1715
- data = newPair
1421
+ else if isString(data)
1422
+ newPair = requestDataAsQuery([ name: name, value: value ], opts)
1423
+ data = [data, newPair].join('&')
1716
1424
  data
1717
1425
 
1718
1426
  ###*
@@ -1729,12 +1437,12 @@ up.util = (($) ->
1729
1437
  up.fail('Unexpected result %o', result)
1730
1438
 
1731
1439
  @function up.fail
1732
- @param {String} message
1440
+ @param {string} message
1733
1441
  A message with details about the error.
1734
1442
 
1735
1443
  The message can contain [substitution marks](https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions)
1736
1444
  like `%s` or `%o`.
1737
- @param {Array<String>} vars...
1445
+ @param {Array<string>} vars...
1738
1446
  A list of variables to replace any substitution marks in the error message.
1739
1447
  @experimental
1740
1448
  ###
@@ -1763,7 +1471,7 @@ up.util = (($) ->
1763
1471
  Escapes the given string of HTML by replacing control chars with their HTML entities.
1764
1472
 
1765
1473
  @function up.util.escapeHtml
1766
- @param {String} string
1474
+ @param {string} string
1767
1475
  The text that should be escaped
1768
1476
  @experimental
1769
1477
  ###
@@ -1786,7 +1494,7 @@ up.util = (($) ->
1786
1494
 
1787
1495
  extractOptions = (args) ->
1788
1496
  lastArg = last(args)
1789
- if isHash(lastArg) && !isJQuery(lastArg)
1497
+ if isOptions(lastArg)
1790
1498
  args.pop()
1791
1499
  else
1792
1500
  {}
@@ -1800,11 +1508,9 @@ up.util = (($) ->
1800
1508
 
1801
1509
  whenReady = memoize ->
1802
1510
  if $.isReady
1803
- resolvedPromise()
1511
+ Promise.resolve()
1804
1512
  else
1805
- deferred = $.Deferred()
1806
- $ -> deferred.resolve()
1807
- deferred.promise()
1513
+ new Promise (resolve) -> $(resolve)
1808
1514
 
1809
1515
  identity = (arg) -> arg
1810
1516
 
@@ -1835,13 +1541,12 @@ up.util = (($) ->
1835
1541
  @internal
1836
1542
  ###
1837
1543
  previewable = (fun) ->
1838
- deferred = $.Deferred()
1544
+ deferred = newDeferred()
1839
1545
  preview = (args...) ->
1840
1546
  funValue = fun(args...)
1841
- if isPromise(funValue)
1842
- funValue.then -> deferred.resolve(funValue)
1843
- else
1844
- deferred.resolve(funValue)
1547
+ # If funValue is again a Promise, it will defer resolution of `deferred`
1548
+ # until `funValue` is resolved.
1549
+ deferred.resolve(funValue)
1845
1550
  funValue
1846
1551
  preview.promise = deferred.promise()
1847
1552
  preview
@@ -1862,8 +1567,8 @@ up.util = (($) ->
1862
1567
  @currentTask = undefined
1863
1568
 
1864
1569
  promise: =>
1865
- promises = map @allTasks(), (task) -> task.promise
1866
- $.when(promises...)
1570
+ lastTask = last(@allTasks())
1571
+ lastTask?.promise || Promise.resolve()
1867
1572
 
1868
1573
  allTasks: =>
1869
1574
  tasks = []
@@ -1874,14 +1579,16 @@ up.util = (($) ->
1874
1579
  poke: =>
1875
1580
  unless @currentTask # don't start a new task while we're still running one
1876
1581
  if @currentTask = @queue.shift()
1582
+ # console.debug('[DivertibleChain.poke] currentTask is now %o', @currentTask)
1877
1583
  promise = @currentTask()
1878
- promise.always =>
1584
+ always promise, =>
1879
1585
  @currentTask = undefined
1880
1586
  @poke()
1881
1587
 
1882
1588
  asap: (newTasks...) =>
1883
1589
  @queue = map(newTasks, previewable)
1884
1590
  @poke()
1591
+ @promise()
1885
1592
 
1886
1593
  ###*
1887
1594
  @function up.util.submittedValue
@@ -1911,7 +1618,7 @@ up.util = (($) ->
1911
1618
  # @internal
1912
1619
  # ###
1913
1620
  # race = (promises...) ->
1914
- # raceDone = $.Deferred()
1621
+ # raceDone = newDeferred()
1915
1622
  # each promises, (promise) ->
1916
1623
  # promise.then -> raceDone.resolve()
1917
1624
  # raceDone.promise()
@@ -1921,11 +1628,11 @@ up.util = (($) ->
1921
1628
  @internal
1922
1629
  ###
1923
1630
  promiseTimer = (ms) ->
1924
- deferred = $.Deferred()
1925
- timeout = setTimer ms, ->
1926
- deferred.resolve()
1927
- deferred.cancel = -> clearTimeout(timeout)
1928
- deferred
1631
+ timeout = undefined
1632
+ promise = new Promise (resolve, reject) ->
1633
+ timeout = setTimer(ms, resolve)
1634
+ promise.cancel = -> clearTimeout(timeout)
1635
+ promise
1929
1636
 
1930
1637
  ###*
1931
1638
  Returns `'left'` if the center of the given element is in the left 50% of the screen.
@@ -1978,9 +1685,61 @@ up.util = (($) ->
1978
1685
  flattened.push(object)
1979
1686
  flattened
1980
1687
 
1688
+ ###*
1689
+ Returns whether the given value is truthy.
1690
+
1691
+ @function up.util.isTruthy
1692
+ @internal
1693
+ ###
1981
1694
  isTruthy = (object) ->
1982
1695
  !!object
1983
1696
 
1697
+ ###*
1698
+ Sets the given callback as both fulfillment and rejection handler for the given promise.
1699
+
1700
+ @function up.util.always
1701
+ @internal
1702
+ ###
1703
+ always = (promise, callback) ->
1704
+ promise.then(callback, callback)
1705
+
1706
+ ###*
1707
+ @function up.util.newDeferred
1708
+ @internal
1709
+ ###
1710
+ newDeferred = ->
1711
+ resolve = undefined
1712
+ reject = undefined
1713
+ nativePromise = new Promise (givenResolve, givenReject) ->
1714
+ resolve = givenResolve
1715
+ reject = givenReject
1716
+ nativePromise.resolve = resolve
1717
+ nativePromise.reject = reject
1718
+ nativePromise.promise = -> nativePromise # just return self
1719
+ nativePromise
1720
+
1721
+ ###*
1722
+ Calls the given block. If the block throws an exception,
1723
+ a rejected promise is returned instead.
1724
+
1725
+ @function up.util.rejectOnError
1726
+ @internal
1727
+ ###
1728
+ rejectOnError = (block) ->
1729
+ try
1730
+ block()
1731
+ catch error
1732
+ Promise.reject(error)
1733
+
1734
+ ###*
1735
+ Returns whether the given element is a descendant of the `<body>` element.
1736
+
1737
+ @function up.util.isBodyDescendant
1738
+ @internal
1739
+ ###
1740
+ isBodyDescendant = (element) ->
1741
+ $(element).parents('body').length > 0
1742
+
1984
1743
  requestDataAsArray: requestDataAsArray
1985
1744
  requestDataAsQuery: requestDataAsQuery
1986
1745
  appendRequestData: appendRequestData
@@ -1993,6 +1752,7 @@ up.util = (($) ->
1993
1752
  parseUrl: parseUrl
1994
1753
  normalizeUrl: normalizeUrl
1995
1754
  normalizeMethod: normalizeMethod
1755
+ methodAllowsPayload: methodAllowsPayload
1996
1756
  createElementFromHtml: createElementFromHtml
1997
1757
  $createElementFromSelector: $createElementFromSelector
1998
1758
  $createPlaceholder: $createPlaceholder
@@ -2031,9 +1791,7 @@ up.util = (($) ->
2031
1791
  isElement: isElement
2032
1792
  isJQuery: isJQuery
2033
1793
  isPromise: isPromise
2034
- isResolvedPromise: isResolvedPromise
2035
- isDeferred: isDeferred
2036
- isHash: isHash
1794
+ isOptions: isOptions
2037
1795
  isArray: isArray
2038
1796
  isFormData: isFormData
2039
1797
  isUnmodifiedKeyEvent: isUnmodifiedKeyEvent
@@ -2045,12 +1803,12 @@ up.util = (($) ->
2045
1803
  measure: measure
2046
1804
  temporaryCss: temporaryCss
2047
1805
  cssAnimate: cssAnimate
2048
- finishCssAnimate: finishCssAnimate
2049
1806
  forceCompositing: forceCompositing
2050
1807
  forceRepaint: forceRepaint
2051
1808
  escapePressed: escapePressed
2052
1809
  copyAttributes: copyAttributes
2053
- findWithSelf: findWithSelf
1810
+ selectInSubtree: selectInSubtree
1811
+ selectInDynasty: selectInDynasty
2054
1812
  contains: contains
2055
1813
  toArray: toArray
2056
1814
  castedAttr: castedAttr
@@ -2058,12 +1816,7 @@ up.util = (($) ->
2058
1816
  only: only
2059
1817
  except: except
2060
1818
  trim: trim
2061
- unresolvableDeferred: unresolvableDeferred
2062
1819
  unresolvablePromise: unresolvablePromise
2063
- resolvedPromise: resolvedPromise
2064
- rejectedPromise: rejectedPromise
2065
- resolvedDeferred: resolvedDeferred
2066
- resolvableWhen: resolvableWhen
2067
1820
  setMissingAttrs: setMissingAttrs
2068
1821
  remove: remove
2069
1822
  memoize: memoize
@@ -2071,7 +1824,6 @@ up.util = (($) ->
2071
1824
  documentHasVerticalScrollbar: documentHasVerticalScrollbar
2072
1825
  config: config
2073
1826
  openConfig: openConfig
2074
- cache: cache
2075
1827
  unwrapElement: unwrapElement
2076
1828
  multiSelector: multiSelector
2077
1829
  error: fail
@@ -2095,6 +1847,13 @@ up.util = (($) ->
2095
1847
  detachWith: detachWith
2096
1848
  flatten: flatten
2097
1849
  isTruthy: isTruthy
1850
+ newDeferred: newDeferred
1851
+ always: always
1852
+ rejectOnError: rejectOnError
1853
+ isBodyDescendant: isBodyDescendant
1854
+ isCrossDomain: isCrossDomain
1855
+ microtask: microtask
1856
+
2098
1857
 
2099
1858
  )(jQuery)
2100
1859