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
@@ -7,30 +7,19 @@ up.rails = (($) ->
7
7
 
8
8
  u = up.util
9
9
 
10
- willHandle = ($element) ->
11
- $element.is('[up-follow], [up-target], [up-modal], [up-popup]')
12
-
13
10
  isRails = ->
14
- u.isGiven($.rails)
11
+ !!$.rails
15
12
 
16
13
  u.each ['method', 'confirm'], (feature) ->
17
14
 
18
15
  dataAttribute = "data-#{feature}"
19
16
  upAttribute = "up-#{feature}"
20
17
 
21
- up.compiler "[#{dataAttribute}]", ($element) ->
22
- if isRails() && willHandle($element)
18
+ up.macro "[#{dataAttribute}]", ($element) ->
19
+ if isRails() && up.link.isFollowable($element)
23
20
  replacement = {}
24
21
  replacement[upAttribute] = $element.attr(dataAttribute)
25
22
  u.setMissingAttrs($element, replacement)
26
23
  $element.removeAttr(dataAttribute)
27
24
 
28
- csrfField = ->
29
- if isRails()
30
- name: $.rails.csrfParam()
31
- value: $.rails.csrfToken()
32
-
33
- csrfField: csrfField
34
- isRails: isRails
35
-
36
25
  )(jQuery)
@@ -20,12 +20,22 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
20
20
  @class up.syntax
21
21
  ###
22
22
  up.syntax = (($) ->
23
-
23
+
24
24
  u = up.util
25
25
 
26
26
  DESTRUCTIBLE_CLASS = 'up-destructible'
27
27
  DESTRUCTORS_KEY = 'up-destructors'
28
28
 
29
+ SYSTEM_MACRO_PRIORITIES = {
30
+ '[up-back]': -100 # sets [up-href] to previous URL
31
+ '[up-drawer]': -200 # sets [up-modal] and makes link followable
32
+ '[up-dash]': -200 # sets [up-href] unless already set, also other [up-*] attributes
33
+ '[up-expand]': -300 # distributes [up-*] attributes to parents
34
+ '[data-method]': -400, # converts [data-method] to [up-method] only if link has followable [up-*] attributes
35
+ '[data-confirm]': -400, # converts [data-conform] to [up-confirm] only if link has followable [up-*] attributes
36
+ }
37
+
38
+ isBooting = true
29
39
  compilers = []
30
40
  macros = []
31
41
 
@@ -176,17 +186,17 @@ up.syntax = (($) ->
176
186
 
177
187
 
178
188
  @function up.compiler
179
- @param {String} selector
189
+ @param {string} selector
180
190
  The selector to match.
181
- @param {Number} [options.priority=0]
182
- The priority of this compilers.
191
+ @param {number} [options.priority=0]
192
+ The priority of this compiler.
183
193
  Compilers with a higher priority are run first.
184
194
  Two compilers with the same priority are run in the order they were registered.
185
- @param {Boolean} [options.batch=false]
195
+ @param {boolean} [options.batch=false]
186
196
  If set to `true` and a fragment insertion contains multiple
187
197
  elements matching the selector, `compiler` is only called once
188
198
  with a jQuery collection containing all matching elements.
189
- @param {Boolean} [options.keep=false]
199
+ @param {boolean} [options.keep=false]
190
200
  If set to `true` compiled fragment will be [persisted](/up-keep) during
191
201
  [page updates](/a-up-target).
192
202
 
@@ -207,8 +217,10 @@ up.syntax = (($) ->
207
217
  The function may also return an array of destructor functions.
208
218
  @stable
209
219
  ###
210
- compiler = (args...) ->
211
- insertCompiler(compilers, args...)
220
+ compiler = (selector, args...) ->
221
+ callback = args.pop()
222
+ options = u.options(args[0])
223
+ insertCompiler(compilers, selector, options, callback)
212
224
 
213
225
  ###*
214
226
  Registers a [compiler](/up.compiler) that is run before all other compilers.
@@ -239,45 +251,50 @@ up.syntax = (($) ->
239
251
  $link.attr('up-duration', '300');
240
252
  });
241
253
 
242
- Examples for built-in macros are [`up-dash`](/up-dash) and [`up-expand`](/up-expand).
254
+ Examples for built-in macros are [`a[up-dash]`](/a-up-dash) and [`[up-expand]`](/up-expand).
243
255
 
244
256
  @function up.macro
245
- @param {String} selector
257
+ @param {string} selector
246
258
  The selector to match.
247
259
  @param {Object} options
248
260
  See options for [`up.compiler()`](/up.compiler).
249
- @param {Function($element, data)} compiler
261
+ @param {Function($element, data)} macro
250
262
  The function to call when a matching element is inserted.
251
263
  See [`up.compiler()`](/up.compiler) for details.
252
264
  @stable
253
265
  ###
254
- macro = (args...) ->
255
- insertCompiler(macros, args...)
256
-
257
- buildCompiler = (selector, args...) ->
266
+ macro = (selector, args...) ->
258
267
  callback = args.pop()
259
- options = u.options(args[0], priority: 0)
260
- if options.priority == 'first'
261
- options.priority = Number.POSITIVE_INFINITY
262
- else if options.priority == 'last'
263
- options.priority = Number.NEGATIVE_INFINITY
268
+ options = u.options(args[0])
269
+ if isBooting
270
+ options.priority = detectSystemMacroPriority(selector) ||
271
+ up.fail('Unregistered priority for system macro %o', selector)
272
+ insertCompiler(macros, selector, options, callback)
273
+
274
+ detectSystemMacroPriority = (fullMacroSelector) ->
275
+ for substr, priority of SYSTEM_MACRO_PRIORITIES
276
+ if fullMacroSelector.indexOf(substr) >= 0
277
+ return priority
278
+
279
+ buildCompiler = (selector, options, callback) ->
264
280
  selector: selector
265
281
  callback: callback
266
- priority: options.priority
282
+ isSystem: isBooting
283
+ priority: options.priority || 0
267
284
  batch: options.batch
268
285
  keep: options.keep
269
286
 
270
- insertCompiler = (queue, args...) ->
287
+ insertCompiler = (queue, selector, options, callback) ->
271
288
  # Silently discard any compilers that are registered on unsupported browsers
272
289
  return unless up.browser.isSupported()
273
- newCompiler = buildCompiler(args...)
290
+ newCompiler = buildCompiler(selector, options, callback)
274
291
  index = 0
275
292
  while (oldCompiler = queue[index]) && (oldCompiler.priority >= newCompiler.priority)
276
293
  index += 1
277
294
  queue.splice(index, 0, newCompiler)
278
295
 
279
296
  applyCompiler = (compiler, $jqueryElement, nativeElement) ->
280
- up.puts ("Compiling '%s' on %o" unless compiler.isDefault), compiler.selector, nativeElement
297
+ up.puts ("Compiling '%s' on %o" unless compiler.isSystem), compiler.selector, nativeElement
281
298
  if compiler.keep
282
299
  value = if u.isString(compiler.keep) then compiler.keep else ''
283
300
  $jqueryElement.attr('up-keep', value)
@@ -325,7 +342,7 @@ up.syntax = (($) ->
325
342
  up.log.group "Compiling fragment %o", $fragment.get(0), ->
326
343
  for queue in [macros, compilers]
327
344
  for compiler in queue
328
- $matches = u.findWithSelf($fragment, compiler.selector)
345
+ $matches = u.selectInSubtree($fragment, compiler.selector)
329
346
 
330
347
  # Exclude all elements that are descendants of the subtrees we want to keep.
331
348
  $matches = $matches.filter ->
@@ -334,7 +351,7 @@ up.syntax = (($) ->
334
351
  $match.closest(skipSubtree).length == 0
335
352
 
336
353
  if $matches.length
337
- up.log.group ("Compiling '%s' on %d element(s)" unless compiler.isDefault), compiler.selector, $matches.length, ->
354
+ up.log.group ("Compiling '%s' on %d element(s)" unless compiler.isSystem), compiler.selector, $matches.length, ->
338
355
  if compiler.batch
339
356
  applyCompiler(compiler, $matches, $matches.get())
340
357
  else
@@ -358,13 +375,12 @@ up.syntax = (($) ->
358
375
  @internal
359
376
  ###
360
377
  prepareClean = ($fragment) ->
361
- destructors = []
362
- u.findWithSelf($fragment, ".#{DESTRUCTIBLE_CLASS}").each ->
363
- # Although destructible elements should always have an array of destructors, we might be
364
- # destroying a clone of such an element. E.g. Unpoly creates a clone when keeping an
365
- # [up-keep] element, and that clone still has the .up-destructible class.
366
- if destructor = $(this).data(DESTRUCTORS_KEY)
367
- destructors.push(destructor)
378
+ $candidates = u.selectInSubtree($fragment, ".#{DESTRUCTIBLE_CLASS}")
379
+ destructors = u.map $candidates, (candidate) -> $(candidate).data(DESTRUCTORS_KEY)
380
+ # Although destructible elements should always have an destructor function, we might be
381
+ # destroying a clone of such an element. E.g. Unpoly creates a clone when keeping an
382
+ # [up-keep] element, and that clone still has the .up-destructible class.
383
+ destructors = u.compact destructors
368
384
  u.sequence(destructors...)
369
385
 
370
386
  ###*
@@ -384,7 +400,7 @@ up.syntax = (($) ->
384
400
  up.syntax.data('.person') // returns { age: 18, name: 'Bob' }
385
401
 
386
402
  @function up.syntax.data
387
- @param {String|Element|jQuery} elementOrSelector
403
+ @param {string|Element|jQuery} elementOrSelector
388
404
  @return
389
405
  The JSON-decoded value of the `up-data` attribute.
390
406
 
@@ -443,17 +459,6 @@ up.syntax = (($) ->
443
459
  else
444
460
  {}
445
461
 
446
- ###*
447
- Makes a snapshot of the currently registered event listeners,
448
- to later be restored through `reset`.
449
-
450
- @internal
451
- ###
452
- snapshot = ->
453
- setDefault = (compiler) -> compiler.isDefault = true
454
- u.each(compilers, setDefault)
455
- u.each(macros, setDefault)
456
-
457
462
  ###*
458
463
  Resets the list of registered compiler directives to the
459
464
  moment when the framework was booted.
@@ -461,11 +466,11 @@ up.syntax = (($) ->
461
466
  @internal
462
467
  ###
463
468
  reset = ->
464
- isDefault = (compiler) -> compiler.isDefault
465
- compilers = u.select(compilers, isDefault)
466
- macros = u.select(macros, isDefault)
469
+ isSystem = (compiler) -> compiler.isSystem
470
+ compilers = u.select(compilers, isSystem)
471
+ macros = u.select(macros, isSystem)
467
472
 
468
- up.on 'up:framework:booted', snapshot
473
+ up.on 'up:framework:booted', -> isBooting = false
469
474
  up.on 'up:framework:reset', reset
470
475
 
471
476
  compiler: compiler
@@ -37,20 +37,20 @@ up.tooltip = (($) ->
37
37
  Configures defaults for future tooltips.
38
38
 
39
39
  @property up.tooltip.config
40
- @param {String} [config.position]
40
+ @param {string} [config.position]
41
41
  The default position of tooltips relative to the element.
42
42
  Can be `'top'`, `'right'`, `'bottom'` or `'left'`.
43
- @param {String} [config.openAnimation='fade-in']
43
+ @param {string} [config.openAnimation='fade-in']
44
44
  The animation used to open a tooltip.
45
- @param {String} [config.closeAnimation='fade-out']
45
+ @param {string} [config.closeAnimation='fade-out']
46
46
  The animation used to close a tooltip.
47
- @param {Number} [config.openDuration]
47
+ @param {number} [config.openDuration]
48
48
  The duration of the open animation (in milliseconds).
49
- @param {Number} [config.closeDuration]
49
+ @param {number} [config.closeDuration]
50
50
  The duration of the close animation (in milliseconds).
51
- @param {String} [config.openEasing]
51
+ @param {string} [config.openEasing]
52
52
  The timing function controlling the acceleration of the opening animation.
53
- @param {String} [config.closeEasing]
53
+ @param {string} [config.closeEasing]
54
54
  The timing function controlling the acceleration of the closing animation.
55
55
  @stable
56
56
  ###
@@ -130,33 +130,28 @@ up.tooltip = (($) ->
130
130
  });
131
131
 
132
132
  @function up.tooltip.attach
133
- @param {Element|jQuery|String} elementOrSelector
134
- @param {String} [options.text]
133
+ @param {Element|jQuery|string} elementOrSelector
134
+ @param {string} [options.text]
135
135
  The text to display in the tooltip.
136
136
 
137
137
  Any HTML control characters will be escaped.
138
138
  If you need to use HTML formatting in the tooltip, use `options.html` instead.
139
- @param {String} [options.html]
139
+ @param {string} [options.html]
140
140
  The HTML to display in the tooltip unescaped.
141
141
 
142
142
  Make sure to escape any user-provided text before passing it as this option,
143
143
  or use `options.text` (which automatically escapes).
144
- @param {String} [options.position='top']
144
+ @param {string} [options.position='top']
145
145
  The position of the tooltip.
146
146
  Can be `'top'`, `'right'`, `'bottom'` or `'left'`.
147
- @param {String} [options.animation]
147
+ @param {string} [options.animation]
148
148
  The [animation](/up.motion) to use when opening the tooltip.
149
149
  @return {Promise}
150
- A promise that will be resolved when the tooltip's opening animation has finished.
150
+ A promise that will be fulfilled when the tooltip's opening animation has finished.
151
151
  @stable
152
152
  ###
153
153
  attachAsap = (elementOrSelector, options = {}) ->
154
- curriedAttachNow = -> attachNow(elementOrSelector, options)
155
- if isOpen()
156
- chain.asap(closeNow, curriedAttachNow)
157
- else
158
- chain.asap(curriedAttachNow)
159
- chain.promise()
154
+ chain.asap closeNow, (-> attachNow(elementOrSelector, options))
160
155
 
161
156
  attachNow = (elementOrSelector, options) ->
162
157
  $anchor = $(elementOrSelector)
@@ -188,13 +183,11 @@ up.tooltip = (($) ->
188
183
  @stable
189
184
  ###
190
185
  closeAsap = (options) ->
191
- if isOpen()
192
- chain.asap -> closeNow(options)
193
- chain.promise()
186
+ chain.asap -> closeNow(options)
194
187
 
195
188
  closeNow = (options) ->
196
189
  unless isOpen() # this can happen when a request fails and the chain proceeds to the next task
197
- return u.resolvedPromise()
190
+ return Promise.resolve()
198
191
 
199
192
  options = u.options(options, animation: config.closeAnimation)
200
193
  animateOptions = up.motion.animateOptions(options, duration: config.closeDuration, easing: config.closeEasing)
@@ -227,10 +220,10 @@ up.tooltip = (($) ->
227
220
  <a href="/decks" up-tooltip="Show all decks" up-position="bottom">Decks</a>
228
221
 
229
222
  @selector [up-tooltip]
230
- @param {String} [up-animation]
223
+ @param {string} [up-animation]
231
224
  The animation used to open the tooltip.
232
225
  Defaults to [`up.tooltip.config.openAnimation`](/up.tooltip.config).
233
- @param {String} [up-position]
226
+ @param {string} [up-position]
234
227
  The default position of tooltips relative to the element.
235
228
  Can be either `"top"` or `"bottom"`.
236
229
  Defaults to [`up.tooltip.config.position`](/up.tooltip.config).