unpoly-rails 0.27.3 → 0.28.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 (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