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
@@ -0,0 +1,52 @@
1
+ u = up.util
2
+
3
+ class up.FollowVariant
4
+
5
+ constructor: (selector, options) ->
6
+ @followNow = options.follow
7
+ @preloadNow = options.preload
8
+ @selectors = selector.split(/\s*,\s*/)
9
+
10
+ onClick: (event, $link) =>
11
+ if @shouldProcessLinkEvent(event, $link)
12
+ if $link.is('[up-instant]')
13
+ # If the link was already processed on mousedown, we still need
14
+ # to prevent this later click event's chain.
15
+ up.bus.haltEvent(event)
16
+ else
17
+ up.bus.consumeAction(event)
18
+ @followLink($link)
19
+ else
20
+ # For tests
21
+ up.link.allowDefault(event)
22
+
23
+ onMousedown: (event, $link) =>
24
+ if @shouldProcessLinkEvent(event, $link)
25
+ up.bus.consumeAction(event)
26
+ @followLink($link)
27
+
28
+ fullSelector: (additionalClause = '') =>
29
+ parts = []
30
+ @selectors.forEach (variantSelector) ->
31
+ ['a', '[up-href]'].forEach (tagSelector) ->
32
+ parts.push "#{tagSelector}#{variantSelector}#{additionalClause}"
33
+ parts.join(', ')
34
+
35
+ registerEvents: ->
36
+ up.on 'click', @fullSelector(), (args...) =>
37
+ @onClick(args...)
38
+ up.on 'mousedown', @fullSelector('[up-instant]'), (args...) =>
39
+ @onMousedown(args...)
40
+
41
+ shouldProcessLinkEvent: (event, $link) =>
42
+ u.isUnmodifiedMouseEvent(event) && !up.link.childClicked(event, $link)
43
+
44
+ followLink: ($link, options = {}) =>
45
+ up.feedback.start $link, options, =>
46
+ @followNow($link, options)
47
+
48
+ preloadLink: ($link, options = {}) =>
49
+ @preloadNow($link, options)
50
+
51
+ matchesLink: ($link) =>
52
+ $link.is(@fullSelector())
@@ -0,0 +1,95 @@
1
+ u = up.util
2
+
3
+ class up.MotionTracker
4
+
5
+ constructor: (name) ->
6
+ @className = "up-#{name}"
7
+ @dataKey = "up-#{name}-finished"
8
+ @selector = ".#{@className}"
9
+ @finishEvent = "up:#{name}:finish"
10
+
11
+ ###*
12
+ Finishes all animations in the given element's ancestors and descendants,
13
+ then calls the animator.
14
+
15
+ @method claim
16
+ @param {jQuery} $element
17
+ @param {Function(jQuery): Promise} animator
18
+ @return {Promise} A promise that is fulfilled when the new animation ends.
19
+ ###
20
+ claim: ($element, animator) =>
21
+ @finish($element).then =>
22
+ @start($element, animator)
23
+
24
+ ###*
25
+ Calls the given animator to animate the given element.
26
+
27
+ The animation returned by the animator is tracked so it can be
28
+ [`finished`](/up.MotionTracker.finish) later.
29
+
30
+ No animations are finished before starting the new animation.
31
+ Use [`claim()`](/up.MotionTracker.claim) for that.
32
+
33
+ @method claim
34
+ @param {jQuery} $element
35
+ @param {Function(jQuery): Promise} animator
36
+ @return {Promise} A promise that is fulfilled when the new animation ends.
37
+ ###
38
+ start: ($element, animator) ->
39
+ promise = animator($element)
40
+ @markElement($element, promise)
41
+ promise.then => @unmarkElement($element)
42
+ promise
43
+
44
+ ###*
45
+ @method finish
46
+ @param {jQuery} [elements]
47
+ If no element is given, finishes all animations in the documnet.
48
+ If an element is given, only finishes animations in its subtree and ancestors.
49
+ @return {Promise} A promise that is fulfilled when animations have finished.
50
+ ###
51
+ finish: (elements) =>
52
+ $elements = @expandFinishRequest(elements)
53
+ allFinished = u.map($elements, @finishOneElement)
54
+ Promise.all(allFinished)
55
+
56
+ expandFinishRequest: (elements) ->
57
+ if elements
58
+ u.selectInDynasty($(elements), @selector)
59
+ else
60
+ $(@selector)
61
+
62
+ finishOneElement: (element) =>
63
+ $element = $(element)
64
+
65
+ # Animating code is expected to listen to this event, fast-forward
66
+ # the animation and resolve their promise. All built-ins like
67
+ # `up.animate`, `up.morph`, or `up.scroll` behave that way.
68
+ $element.trigger(@finishEvent)
69
+
70
+ # If animating code ignores the event, we cannot force the animation to
71
+ # finish from here. We will wait for the animation to end naturally before
72
+ # starting the next animation.
73
+ @whenElementFinished($element)
74
+
75
+ whenElementFinished: ($element) =>
76
+ # There are some cases related to element ghosting where an element
77
+ # has the class, but not the data value. In that case simply return
78
+ # a resolved promise.
79
+ $element.data(@dataKey) || Promise.resolve()
80
+
81
+ markElement: ($element, promise) =>
82
+ $element.addClass(@className)
83
+ $element.data(@dataKey, promise)
84
+
85
+ unmarkElement: ($element) =>
86
+ $element.removeClass(@className)
87
+ $element.removeData(@dataKey)
88
+
89
+ forwardFinishEvent: ($original, $ghost, duration) =>
90
+ @start $original, =>
91
+ doForward = => $ghost.trigger(@finishEvent)
92
+ # Forward the finish event to the $ghost that is actually animating
93
+ $original.on @finishEvent, doForward
94
+ # Our own pseudo-animation finishes when the actual animation on $ghost finishes
95
+ duration.then => $original.off @finishEvent, doForward
@@ -0,0 +1,16 @@
1
+ u = up.util
2
+
3
+ class up.Record
4
+
5
+ fields: ->
6
+ throw 'Return an array of property names'
7
+
8
+ constructor: (options) ->
9
+ u.assign(@, @attributes(options))
10
+
11
+ attributes: (source = @) =>
12
+ u.only(source, @fields()...)
13
+
14
+ copy: (changes = {}) =>
15
+ attributesWithChanges = u.merge(@attributes(), changes)
16
+ new @constructor(attributesWithChanges)
@@ -0,0 +1,228 @@
1
+ #= require ./record
2
+
3
+ u = up.util
4
+
5
+ ###*
6
+ Instances of `up.Request` normalizes properties of an [`AJAX request`](/up.request)
7
+ such as the requested URL, form parameters and HTTP method.
8
+
9
+ @class up.Request
10
+ ###
11
+ class up.Request extends up.Record
12
+
13
+ ###*
14
+ The HTTP method for the request.
15
+
16
+ @property up.Request#method
17
+ @param {string} method
18
+ @stable
19
+ ###
20
+
21
+ ###*
22
+ The URL for the request.
23
+
24
+ @property up.Request#url
25
+ @param {string} url
26
+ @stable
27
+ ###
28
+
29
+ ###*
30
+ Parameters that should be sent as the request's payload.
31
+
32
+ Parameters may be passed as one of the following forms:
33
+
34
+ 1. An object where keys are param names and the values are param values
35
+ 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
36
+ 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
37
+
38
+ @property up.Request#data
39
+ @param {String} data
40
+ @stable
41
+ ###
42
+
43
+ ###*
44
+ The CSS selector that will be sent as an [`X-Up-Target` header](/up.protocol#optimizing-responses).
45
+
46
+ @property up.Request#target
47
+ @param {string} target
48
+ @stable
49
+ ###
50
+
51
+ ###*
52
+ The CSS selector that will be sent as an [`X-Up-Fail-Target` header](/up.protocol#optimizing-responses).
53
+
54
+ @property up.Request#failTarget
55
+ @param {string} failTarget
56
+ @stable
57
+ ###
58
+
59
+ ###*
60
+ An object of additional HTTP headers.
61
+
62
+ @property up.Request#headers
63
+ @param {object} headers
64
+ @stable
65
+ ###
66
+
67
+ ###*
68
+ A timeout in milliseconds.
69
+
70
+ If [`up.proxy.config.maxRequests`](/up.proxy.config#config.maxRequests) is set,
71
+ the timeout will not include the time spent waiting in the queue.
72
+
73
+ @property up.Request#timeout
74
+ @param {object|undefined} timeout
75
+ @stable
76
+ ###
77
+ fields: ->
78
+ [
79
+ 'method',
80
+ 'url',
81
+ 'data',
82
+ 'target',
83
+ 'failTarget',
84
+ 'headers',
85
+ 'timeout'
86
+ ]
87
+
88
+ ###*
89
+ @constructor up.Request
90
+ @param {string} [attributes]
91
+ ###
92
+ constructor: (options) ->
93
+ super(options)
94
+ @normalize()
95
+
96
+ normalize: =>
97
+ @method = u.normalizeMethod(@method)
98
+ @headers ||= {}
99
+ @extractHashFromUrl()
100
+ if @data && !u.methodAllowsPayload(@method) && !u.isFormData(@data)
101
+ @transferDataToUrl()
102
+
103
+ extractHashFromUrl: =>
104
+ urlParts = u.parseUrl(@url)
105
+ # Remember the #hash for later revealing.
106
+ # It will be lost during normalization.
107
+ @hash = urlParts.hash
108
+ @url = u.normalizeUrl(urlParts, hash: false)
109
+
110
+ transferDataToUrl: =>
111
+ # GET methods are not allowed to have a payload, so we transfer { data } params to the URL.
112
+ query = u.requestDataAsQuery(@data)
113
+ separator = if u.contains(@url, '?') then '&' else '?'
114
+ @url += separator + query
115
+ # Now that we have transfered the params into the URL, we delete them from the { data } option.
116
+ @data = undefined
117
+
118
+ isSafe: =>
119
+ up.proxy.isSafeMethod(@method)
120
+
121
+ send: =>
122
+ # We will modify this request below.
123
+ # This would confuse API clients and cache key logic in up.proxy.
124
+ new Promise (resolve, reject) =>
125
+ xhr = new XMLHttpRequest()
126
+
127
+ xhrHeaders = u.copy(@headers)
128
+ xhrData = @data
129
+ xhrMethod = @method
130
+ xhrUrl = @url
131
+
132
+ [xhrMethod, xhrData] = up.proxy.wrapMethod(xhrMethod, xhrData)
133
+
134
+ if u.isFormData(xhrData)
135
+ delete xhrHeaders['Content-Type'] # let the browser set the content type
136
+ else if u.isPresent(xhrData)
137
+ xhrData = u.requestDataAsQuery(xhrData, purpose: 'form')
138
+ xhrHeaders['Content-Type'] = 'application/x-www-form-urlencoded'
139
+ else
140
+ # XMLHttpRequest expects null for an empty body
141
+ xhrData = null
142
+
143
+ xhrHeaders[up.protocol.config.targetHeader] = @target if @target
144
+ xhrHeaders[up.protocol.config.failTargetHeader] = @failTarget if @failTarget
145
+
146
+ if csrfToken = @csrfToken()
147
+ xhrHeaders[up.protocol.config.csrfHeader] = csrfToken
148
+
149
+ xhr.open(xhrMethod, xhrUrl)
150
+
151
+ for header, value of xhrHeaders
152
+ xhr.setRequestHeader(header, value)
153
+
154
+ resolveWithResponse = =>
155
+ response = @buildResponse(xhr)
156
+ if response.isSuccess()
157
+ resolve(response)
158
+ else
159
+ reject(response)
160
+
161
+ # Convert from XHR API to promise API
162
+ xhr.onload = resolveWithResponse
163
+ xhr.onerror = resolveWithResponse
164
+ xhr.ontimeout = resolveWithResponse
165
+
166
+ xhr.timeout = @timeout if @timeout
167
+
168
+ xhr.send(xhrData)
169
+
170
+ navigate: =>
171
+ $form = $('<form class="up-page-loader"></form>')
172
+
173
+ addField = (field) -> $('<input type="hidden">').attr(field).appendTo($form)
174
+
175
+ if @method == 'GET'
176
+ formMethod = 'GET'
177
+ else
178
+ # Browser forms can only have GET or POST methods.
179
+ # When we want to make a request with another method, most backend
180
+ # frameworks allow to pass the method as a param.
181
+ addField(name: up.protocol.config.methodParam, value: @method)
182
+ formMethod = 'POST'
183
+
184
+ $form.attr(method: formMethod, action: @url)
185
+
186
+ if (csrfParam = up.protocol.csrfParam()) && (csrfToken = @csrfToken())
187
+ addField(name: csrfParam, value: csrfToken)
188
+
189
+ u.each u.requestDataAsArray(@data), addField
190
+
191
+ $form.hide().appendTo('body')
192
+ up.browser.submitForm($form)
193
+
194
+ # Returns a csrfToken if this request requires it
195
+ csrfToken: =>
196
+ if !@isSafe() && !u.isCrossDomain(@url)
197
+ up.protocol.csrfToken()
198
+
199
+ buildResponse: (xhr) =>
200
+ responseAttrs =
201
+ method: @method
202
+ url: @url
203
+ text: xhr.responseText
204
+ status: xhr.status
205
+ request: @
206
+ xhr: xhr
207
+
208
+ if urlFromServer = up.protocol.locationFromXhr(xhr)
209
+ responseAttrs.url = urlFromServer
210
+ # If the server changes a URL, it is expected to signal a new method as well.
211
+ responseAttrs.method = up.protocol.methodFromXhr(xhr) ? 'GET'
212
+
213
+ responseAttrs.title = up.protocol.titleFromXhr(xhr)
214
+
215
+ new up.Response(responseAttrs)
216
+
217
+ isCachable: =>
218
+ @isSafe() && !u.isFormData(@data)
219
+
220
+ cacheKey: =>
221
+ [@url, @method, u.requestDataAsQuery(@data), @target].join('|')
222
+
223
+ @wrap: (object) ->
224
+ if object instanceof @
225
+ # This object has gone through instantiation and normalization before.
226
+ object
227
+ else
228
+ new @(object)
@@ -0,0 +1,138 @@
1
+ #= require ./record
2
+
3
+ u = up.util
4
+
5
+ ###*
6
+ Instances of `up.Response` describe the server response to an [`AJAX request`](/up.request).
7
+
8
+ \#\#\# Example
9
+
10
+ up.request('/foo').then(function(response) {
11
+ console.log(response.status); // 200
12
+ console.log(response.text); // "<html><body>..."
13
+ });
14
+
15
+ @class up.Response
16
+ ###
17
+ class up.Response extends up.Record
18
+
19
+ ###*
20
+ The HTTP method used for the response.
21
+
22
+ This is usually the HTTP method used by the request.
23
+ However, after a redirect the server should signal a `GET` method using
24
+ an [`X-Up-Method: GET` header](/up.protocol#redirect-detection).
25
+
26
+ @property up.Response#method
27
+ @param {string} method
28
+ @stable
29
+ ###
30
+
31
+ ###*
32
+ The URL used for the response.
33
+
34
+ This is usually the requested URL.
35
+ However, after a redirect the server should signal a the new URL
36
+ using an [`X-Up-Location: /new-url` header](/up.protocol#redirect-detection).
37
+
38
+ @property up.Response#url
39
+ @param {string} method
40
+ @stable
41
+ ###
42
+
43
+ ###*
44
+ The response body as a `string`.
45
+
46
+ @property up.Response#text
47
+ @param {string} text
48
+ @stable
49
+ ###
50
+
51
+ ###*
52
+ The response's
53
+ [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
54
+ as a `number`.
55
+
56
+ A successful response will usually have a `200` or `201' status code.
57
+
58
+ @property up.Response#status
59
+ @param {number} status
60
+ @stable
61
+ ###
62
+
63
+ ###*
64
+ The [request](/up.Request) that triggered this response.
65
+
66
+ @property up.Response#request
67
+ @param {up.Request} request
68
+ @experimental
69
+ ###
70
+
71
+ ###*
72
+ The [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
73
+ object that was used to create this response.
74
+
75
+ @property up.Response#xhr
76
+ @param {XMLHttpRequest} xhr
77
+ @experimental
78
+ ###
79
+
80
+ ###*
81
+ A [document title pushed by the server](/up.protocol#pushing-a-document-title-to-the-client).
82
+
83
+ If the server pushed no title via HTTP header, this will be `undefined`.
84
+
85
+ @property up.Response#title
86
+ @param {string} [title]
87
+ @stable
88
+ ###
89
+ fields: ->
90
+ [
91
+ 'method',
92
+ 'url',
93
+ 'text',
94
+ 'status',
95
+ 'request',
96
+ 'xhr',
97
+ 'title'
98
+ ]
99
+
100
+ constructor: (options) ->
101
+ super(options)
102
+
103
+ ###*
104
+ Returns whether the server responded with a 2xx HTTP status.
105
+
106
+ @function up.Response#isSuccess
107
+ @return {boolean}
108
+ @experimental
109
+ ###
110
+ isSuccess: =>
111
+ @status && (@status >= 200 && @status <= 299)
112
+
113
+ ###*
114
+ Returns whether the response was not [successful](/up.Request.prototype.isSuccess).
115
+
116
+ This also returns `true` when the request encountered a [fatal error](/up.Request.prototype.isFatalError)
117
+ like a timeout or loss of network connectivity.
118
+
119
+ @function up.Response#isError
120
+ @return {boolean}
121
+ @experimental
122
+ ###
123
+ isError: =>
124
+ !@isSuccess()
125
+
126
+ ###*
127
+ Returns whether the request encountered a [fatal error](/up.Request.prototype.isFatalError)
128
+ like a timeout or loss of network connectivity.
129
+
130
+ When the server produces an error message with an HTTP status like `500`,
131
+ this is not considered a fatal error and `false` is returned.
132
+
133
+ @function up.Response#isFatalError
134
+ @return {boolean}
135
+ @experimental
136
+ ###
137
+ isFatalError: =>
138
+ @isError() && u.isBlank(@text)