unpoly-rails 0.27.3 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -1
  3. data/dist/unpoly.css +29 -21
  4. data/dist/unpoly.js +291 -134
  5. data/dist/unpoly.min.css +1 -1
  6. data/dist/unpoly.min.js +3 -3
  7. data/lib/assets/javascripts/unpoly.js.coffee +1 -0
  8. data/lib/assets/javascripts/unpoly/browser.js.coffee +22 -5
  9. data/lib/assets/javascripts/unpoly/bus.js.coffee +1 -1
  10. data/lib/assets/javascripts/unpoly/flow.js.coffee +9 -6
  11. data/lib/assets/javascripts/unpoly/form.js.coffee +46 -49
  12. data/lib/assets/javascripts/unpoly/history.js.coffee +2 -2
  13. data/lib/assets/javascripts/unpoly/layout.js.coffee +3 -3
  14. data/lib/assets/javascripts/unpoly/modal.js.coffee +30 -2
  15. data/lib/assets/javascripts/unpoly/motion.js.coffee +6 -6
  16. data/lib/assets/javascripts/unpoly/navigation.js.coffee +1 -1
  17. data/lib/assets/javascripts/unpoly/popup.js.coffee +2 -2
  18. data/lib/assets/javascripts/unpoly/proxy.js.coffee +5 -9
  19. data/lib/assets/javascripts/unpoly/syntax.js.coffee +10 -6
  20. data/lib/assets/javascripts/unpoly/toast.js.coffee +66 -0
  21. data/lib/assets/javascripts/unpoly/tooltip.js.coffee +1 -1
  22. data/lib/assets/javascripts/unpoly/util.js.coffee +47 -21
  23. data/lib/assets/stylesheets/unpoly/toast.css.sass +33 -0
  24. data/lib/unpoly/rails/version.rb +1 -1
  25. data/spec_app/Gemfile.lock +1 -1
  26. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  27. data/spec_app/app/assets/stylesheets/integration_test.sass +14 -0
  28. data/spec_app/app/controllers/application_controller.rb +8 -0
  29. data/spec_app/app/controllers/error_test_controller.rb +5 -0
  30. data/spec_app/app/views/error_test/trigger.erb +72 -0
  31. data/spec_app/app/views/error_test/unexpected_response.erb +3 -0
  32. data/spec_app/app/views/pages/start.erb +4 -0
  33. data/spec_app/config/routes.rb +2 -0
  34. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  35. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +62 -0
  36. data/spec_app/spec/javascripts/up/form_spec.js.coffee +116 -3
  37. data/spec_app/spec/javascripts/up/link_spec.js.coffee +2 -2
  38. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +88 -4
  39. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +27 -1
  40. data/spec_app/spec/javascripts/up/util_spec.js.coffee +12 -0
  41. metadata +8 -3
  42. data/lib/assets/stylesheets/unpoly/error.css.sass +0 -21
@@ -182,7 +182,7 @@ up.motion = (($) ->
182
182
  $element.css(animation)
183
183
  u.resolvedDeferred()
184
184
  else
185
- u.error("Unknown animation type for %o", animation)
185
+ up.fail("Unknown animation type for %o", animation)
186
186
 
187
187
  ###*
188
188
  Extracts animation-related options from the given options hash.
@@ -203,7 +203,7 @@ up.motion = (($) ->
203
203
  consolidatedOptions
204
204
 
205
205
  findAnimation = (name) ->
206
- animations[name] or u.error("Unknown animation %o", name)
206
+ animations[name] or up.fail("Unknown animation %o", name)
207
207
 
208
208
  GHOSTING_DEFERRED_KEY = 'up-ghosting-deferred'
209
209
  GHOSTING_CLASS = 'up-ghosting'
@@ -302,7 +302,7 @@ up.motion = (($) ->
302
302
  if u.isDeferred(object)
303
303
  object
304
304
  else
305
- u.error("Did not return a promise with .then and .resolve methods: %o", source)
305
+ up.fail("Did not return a promise with .then and .resolve methods: %o", source)
306
306
 
307
307
  ###*
308
308
  Performs an animated transition between two elements.
@@ -407,14 +407,14 @@ up.motion = (($) ->
407
407
  )
408
408
  return morph($old, $new, transition, parsedOptions)
409
409
  else
410
- u.error("Unknown transition %o", transitionOrName)
410
+ up.fail("Unknown transition %o", transitionOrName)
411
411
  else
412
412
  return skipMorph($old, $new, parsedOptions)
413
413
 
414
414
  ensureMorphable = ($element, transition) ->
415
415
  if transition && $element.parents('body').length == 0
416
416
  element = $element.get(0)
417
- u.error("Can't morph a <%s> element (%o)", element.tagName, element)
417
+ up.fail("Can't morph a <%s> element (%o)", element.tagName, element)
418
418
 
419
419
  ###*
420
420
  This causes the side effects of a successful transition, but instantly.
@@ -724,7 +724,7 @@ up.motion = (($) ->
724
724
  animation: animation
725
725
  config: config
726
726
  isEnabled: isEnabled
727
- defaults: -> u.error('up.motion.defaults(...) no longer exists. Set values on he up.motion.config property instead.')
727
+ defaults: -> up.fail('up.motion.defaults(...) no longer exists. Set values on he up.motion.config property instead.')
728
728
  none: none
729
729
  when: resolvableWhen
730
730
  prependCopy: prependCopy
@@ -221,7 +221,7 @@ up.navigation = (($) ->
221
221
  up.on 'up:framework:reset', reset
222
222
 
223
223
  config: config
224
- defaults: -> u.error('up.navigation.defaults(...) no longer exists. Set values on he up.navigation.config property instead.')
224
+ defaults: -> up.fail('up.navigation.defaults(...) no longer exists. Set values on he up.navigation.config property instead.')
225
225
  markActive: markActive
226
226
  unmarkActive: unmarkActive
227
227
  withActiveMark: withActiveMark
@@ -144,7 +144,7 @@ up.popup = (($) ->
144
144
  css['top'] = linkBox.top - popupBox.height
145
145
  css['left'] = linkBox.left
146
146
  else
147
- u.error("Unknown position option '%s'", state.position)
147
+ up.fail("Unknown position option '%s'", state.position)
148
148
 
149
149
  state.$popup.attr('up-position', state.position)
150
150
  state.$popup.css(css)
@@ -216,7 +216,7 @@ up.popup = (($) ->
216
216
 
217
217
  attachNow = (elementOrSelector, options) ->
218
218
  $anchor = $(elementOrSelector)
219
- $anchor.length or u.error('Cannot attach popup to non-existing element %o', elementOrSelector)
219
+ $anchor.length or up.fail('Cannot attach popup to non-existing element %o', elementOrSelector)
220
220
 
221
221
  options = u.options(options)
222
222
  url = u.option(u.pluckKey(options, 'url'), $anchor.attr('up-href'), $anchor.attr('href'))
@@ -312,16 +312,12 @@ up.proxy = (($) ->
312
312
  show = function() { $element.show() };
313
313
  hide = function() { $element.hide() };
314
314
 
315
- up.on('up:proxy:slow', show);
316
- up.on('up:proxy:recover', hide);
317
-
318
315
  hide();
319
316
 
320
- // Clean up when the element is removed from the DOM
321
- return function() {
322
- up.off('up:proxy:slow', show);
323
- up.off('up:proxy:recover', hide);
324
- };
317
+ return [
318
+ up.on('up:proxy:slow', show),
319
+ up.on('up:proxy:recover', hide)
320
+ ];
325
321
 
326
322
  });
327
323
 
@@ -552,7 +548,7 @@ up.proxy = (($) ->
552
548
  isBusy: isBusy
553
549
  isCachable: isCachable
554
550
  config: config
555
- defaults: -> u.error('up.proxy.defaults(...) no longer exists. Set values on he up.proxy.config property instead.')
551
+ defaults: -> up.fail('up.proxy.defaults(...) no longer exists. Set values on he up.proxy.config property instead.')
556
552
 
557
553
  )(jQuery)
558
554
 
@@ -101,7 +101,8 @@ up.syntax = (($) ->
101
101
  or event handlers bound to the document root.
102
102
 
103
103
  Here is a version of `<clock>` that updates
104
- the time every second, and cleans up once it's done:
104
+ the time every second, and cleans up once it's done. Note how it returns
105
+ a function that calls `clearInterval`:
105
106
 
106
107
  up.compiler('clock', function($element) {
107
108
 
@@ -279,10 +280,16 @@ up.syntax = (($) ->
279
280
  if compiler.keep
280
281
  value = if u.isString(compiler.keep) then compiler.keep else ''
281
282
  $jqueryElement.attr('up-keep', value)
282
- destructor = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)])
283
- if u.isFunction(destructor)
283
+ returnValue = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)])
284
+ if destructor = discoverDestructor(returnValue)
284
285
  addDestructor($jqueryElement, destructor)
285
286
 
287
+ discoverDestructor = (returnValue) ->
288
+ if u.isFunction(returnValue)
289
+ returnValue
290
+ else if u.isArray(returnValue) && u.all(returnValue, u.isFunction)
291
+ u.sequence(returnValue...)
292
+
286
293
  addDestructor = ($jqueryElement, destructor) ->
287
294
  $jqueryElement.addClass(DESTRUCTABLE_CLASS)
288
295
  destructors = $jqueryElement.data(DESTRUCTORS_KEY) || []
@@ -446,6 +453,3 @@ up.syntax = (($) ->
446
453
 
447
454
  up.compiler = up.syntax.compiler
448
455
  up.macro = up.syntax.macro
449
-
450
- up.ready = -> up.util.error('up.ready no longer exists. Please use up.hello instead.')
451
- up.awaken = -> up.util.error('up.awaken no longer exists. Please use up.compiler instead.')
@@ -0,0 +1,66 @@
1
+ ###*
2
+ Toast alerts
3
+ ============
4
+
5
+ @class up.toast
6
+ ###
7
+ up.toast = (($) ->
8
+
9
+ u = up.util
10
+ b = up.browser
11
+
12
+ VARIABLE_FORMATTER = (arg) -> "<span class='up-toast-variable'>#{u.escapeHtml(arg)}</span>"
13
+
14
+ state = u.config
15
+ $toast: null
16
+
17
+ reset = ->
18
+ close()
19
+ state.reset()
20
+
21
+ messageToHtml = (message) ->
22
+ if u.isArray(message)
23
+ message[0] = u.escapeHtml(message[0])
24
+ message = b.sprintfWithFormattedArgs(VARIABLE_FORMATTER, message...)
25
+ else
26
+ message = u.escapeHtml(message)
27
+ message
28
+
29
+ isOpen = ->
30
+ !!state.$toast
31
+
32
+ addAction = ($actions, label, callback) ->
33
+ $action = $('<span class="up-toast-action"></span>').text(label)
34
+ $action.on 'click', callback
35
+ $action.appendTo($actions)
36
+
37
+ open = (message, options = {}) ->
38
+ close()
39
+ $toast = $('<div class="up-toast"></div>').prependTo('body')
40
+ $message = $('<div class="up-toast-message"></div>').appendTo($toast)
41
+ $actions = $('<div class="up-toast-actions"></div>').appendTo($toast)
42
+
43
+ message = messageToHtml(message)
44
+ $message.html(message)
45
+
46
+ if action = (options.action || options.inspect)
47
+ addAction($actions, action.label, action.callback)
48
+
49
+ addAction($actions, 'Close', close)
50
+
51
+ state.$toast = $toast
52
+
53
+ close = ->
54
+ if isOpen()
55
+ state.$toast.remove()
56
+ state.$toast = null
57
+
58
+ # The framework is reset between tests
59
+ up.on 'up:framework:reset', reset
60
+
61
+ open: open
62
+ close: close
63
+ reset: reset
64
+ isOpen: isOpen
65
+
66
+ )(jQuery)
@@ -102,7 +102,7 @@ up.tooltip = (($) ->
102
102
  css['top'] = linkBox.top + linkBox.height
103
103
  css['left'] = linkBox.left + 0.5 * (linkBox.width - tooltipBox.width)
104
104
  else
105
- u.error("Unknown position option '%s'", state.position)
105
+ up.fail("Unknown position option '%s'", state.position)
106
106
 
107
107
  state.$tooltip.attr('up-position', state.position)
108
108
  state.$tooltip.css(css)
@@ -545,7 +545,7 @@ up.util = (($) ->
545
545
 
546
546
  Returns the array.
547
547
 
548
- @function up.util.isDeferred
548
+ @function up.util.toArray
549
549
  @param object
550
550
  @return {Array}
551
551
  @stable
@@ -1597,7 +1597,7 @@ up.util = (($) ->
1597
1597
  if isFormData(data)
1598
1598
  # Until FormData#entries is implemented in all major browsers
1599
1599
  # we must give up here
1600
- up.error('Cannot convert FormData into an array')
1600
+ up.fail('Cannot convert FormData into an array')
1601
1601
  else
1602
1602
  query = requestDataAsQuery(data)
1603
1603
  array = []
@@ -1620,7 +1620,7 @@ up.util = (($) ->
1620
1620
  if isFormData(data)
1621
1621
  # Until FormData#entries is implemented in all major browsers
1622
1622
  # we must give up here
1623
- up.error('Cannot convert FormData into a query string')
1623
+ up.fail('Cannot convert FormData into a query string')
1624
1624
  else if isPresent(data)
1625
1625
  query = $.param(data)
1626
1626
  query = query.replace(/\+/g, '%20')
@@ -1672,28 +1672,34 @@ up.util = (($) ->
1672
1672
  data
1673
1673
 
1674
1674
  ###*
1675
- Throws a fatal error with the given message.
1675
+ Throws an [exception](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
1676
+ with the given message.
1677
+
1678
+ The message will also be printed to the [error log](/up.log.error).
1676
1679
 
1677
- - The error will be printed to the [error console](https://developer.mozilla.org/en-US/docs/Web/API/Console/error)
1678
- - An [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) (exception) will be thrown, unwinding the current call stack
1679
- - The error message will be printed in a corner of the screen
1680
+ Also a notification will be shown at the bottom of the screen.
1680
1681
 
1681
1682
  \#\#\#\# Examples
1682
1683
 
1683
- up.error('Division by zero')
1684
- up.error('Unexpected result %o', result)
1684
+ up.fail('Division by zero')
1685
+ up.fail('Unexpected result %o', result)
1685
1686
 
1687
+ @function up.fail
1686
1688
  @experimental
1687
1689
  ###
1688
- error = (args...) ->
1689
- up.log.error(args...)
1690
- whenReady().then ->
1691
- $error = presence($('.up-error')) || $('<div class="up-error"></div>').prependTo('body')
1692
- formatter = (arg) ->
1693
- "<span class='up-error-variable'>#{escapeHtml(arg)}</span>"
1694
- asHtml = up.browser.sprintfWithFormattedArgs(formatter, args...)
1695
- $error.html(asHtml)
1696
- asString = up.browser.sprintf(args...)
1690
+ fail = (args...) ->
1691
+ if isArray(args[0])
1692
+ messageArgs = args[0]
1693
+ toastOptions = args[1] || {}
1694
+ else
1695
+ messageArgs = args
1696
+ toastOptions = {}
1697
+
1698
+ up.log.error(messageArgs...)
1699
+
1700
+ whenReady().then -> up.toast.open(messageArgs, toastOptions)
1701
+
1702
+ asString = up.browser.sprintf(messageArgs...)
1697
1703
  throw new Error(asString)
1698
1704
 
1699
1705
  ESCAPE_HTML_ENTITY_MAP =
@@ -1816,6 +1822,25 @@ up.util = (($) ->
1816
1822
  @queue = map(newTasks, previewable)
1817
1823
  @poke()
1818
1824
 
1825
+ ###*
1826
+ @function up.util.submittedValue
1827
+ @internal
1828
+ ###
1829
+ submittedValue = (fieldOrSelector) ->
1830
+ $field = $(fieldOrSelector)
1831
+ if $field.is('[type=checkbox], [type=radio]') && !$field.is(':checked')
1832
+ undefined
1833
+ else
1834
+ $field.val()
1835
+
1836
+ ###*
1837
+ @function up.util.sequence
1838
+ @internal
1839
+ ###
1840
+ sequence: (functions...) ->
1841
+ ->
1842
+ map functions, (f) -> f()
1843
+
1819
1844
  isDetached: isDetached
1820
1845
  requestDataAsArray: requestDataAsArray
1821
1846
  requestDataAsQuery: requestDataAsQuery
@@ -1838,7 +1863,7 @@ up.util = (($) ->
1838
1863
  merge: merge
1839
1864
  options: options
1840
1865
  option: option
1841
- error: error
1866
+ fail: fail
1842
1867
  each: each
1843
1868
  map: map
1844
1869
  times: times
@@ -1911,7 +1936,7 @@ up.util = (($) ->
1911
1936
  cache: cache
1912
1937
  unwrapElement: unwrapElement
1913
1938
  multiSelector: multiSelector
1914
- error: error
1939
+ error: fail
1915
1940
  pluckData: pluckData
1916
1941
  pluckKey: pluckKey
1917
1942
  extractOptions: extractOptions
@@ -1922,7 +1947,8 @@ up.util = (($) ->
1922
1947
  identity: identity
1923
1948
  escapeHtml: escapeHtml
1924
1949
  DivertibleChain: DivertibleChain
1950
+ submittedValue: submittedValue
1925
1951
 
1926
1952
  )($)
1927
1953
 
1928
- up.error = up.util.error
1954
+ up.fail = up.util.fail
@@ -0,0 +1,33 @@
1
+ $highlight-color: #28b
2
+ $text-color: #333
3
+
4
+ .up-toast
5
+ border-top: 3px solid $highlight-color
6
+ background-color: white
7
+ color: $text-color
8
+ padding: 10px
9
+ font-family: monospace
10
+ font-size: 14px
11
+ line-height: 15px
12
+ position: fixed
13
+ left: 0
14
+ bottom: 0
15
+ right: 0
16
+ z-index: 99999999
17
+
18
+ .up-toast-variable
19
+ font-weight: normal
20
+ color: $text-color + 80
21
+
22
+ .up-toast-actions
23
+ margin-top: 7px
24
+
25
+ .up-toast-action
26
+ display: inline-block
27
+ word-spacing: -4px
28
+ text-decoration: underline
29
+ color: $highlight-color
30
+ cursor: pointer
31
+
32
+ &+.up-toast-action
33
+ margin-left: 12px
@@ -4,6 +4,6 @@ module Unpoly
4
4
  # The current version of the unpoly-rails gem.
5
5
  # This version number is also used for releases of the Unpoly
6
6
  # frontend code.
7
- VERSION = '0.27.3'
7
+ VERSION = '0.28.0'
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- unpoly-rails (0.27.2)
4
+ unpoly-rails (0.27.3)
5
5
  rails (>= 3)
6
6
 
7
7
  GEM
@@ -1,2 +1,3 @@
1
1
  #= require jquery
2
+ #= require jquery_ujs
2
3
  #= require unpoly
@@ -1,10 +1,19 @@
1
1
  //= require unpoly
2
2
 
3
+ $block-spacing: 25px
4
+
5
+ =block-spacing
6
+ margin-top: $block-spacing
7
+ margin-bottom: $block-spacing
8
+
3
9
  body
4
10
  margin: 0
5
11
  background: image-url('grid.png') repeat
6
12
  font-family: arial, sans-serif
7
13
 
14
+ h1, h2, h3, h4, p
15
+ +block-spacing
16
+
8
17
  .page
9
18
  min-height: 3000px
10
19
 
@@ -24,6 +33,11 @@ body
24
33
  .example
25
34
  margin: 50px
26
35
 
36
+ .area
37
+ +block-spacing
38
+ padding: 25px
39
+ border: 1px dashed #666
40
+
27
41
  .button
28
42
  display: inline-block
29
43
  height: 50px
@@ -3,4 +3,12 @@ class ApplicationController < ActionController::Base
3
3
  # For APIs, you may want to use :null_session instead.
4
4
  protect_from_forgery with: :exception
5
5
 
6
+ before_action :print_authenticity_token
7
+
8
+ private
9
+
10
+ def print_authenticity_token
11
+ Rails.logger.info "*** Real token is #{send(:real_csrf_token, session)}"
12
+ end
13
+
6
14
  end