unpoly-rails 0.57.0 → 0.60.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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +393 -1
  3. data/Gemfile.lock +5 -2
  4. data/README.md +1 -1
  5. data/README_RAILS.md +1 -1
  6. data/Rakefile +10 -1
  7. data/design/es6.js +32 -0
  8. data/design/ie11.txt +9 -0
  9. data/design/measure_jquery/element_list.js +41 -0
  10. data/design/measure_jquery/up.on_vs_addEventListener.js +56 -0
  11. data/design/todo_jquery.txt +13 -0
  12. data/dist/unpoly-bootstrap3.js +8 -8
  13. data/dist/unpoly-bootstrap3.min.js +1 -1
  14. data/dist/unpoly.css +22 -20
  15. data/dist/unpoly.js +6990 -5336
  16. data/dist/unpoly.min.css +1 -1
  17. data/dist/unpoly.min.js +4 -4
  18. data/lib/assets/javascripts/unpoly-bootstrap3/viewport-ext.coffee +5 -0
  19. data/lib/assets/javascripts/unpoly.coffee +8 -6
  20. data/lib/assets/javascripts/unpoly/browser.coffee.erb +23 -118
  21. data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +36 -0
  22. data/lib/assets/javascripts/unpoly/classes/cache.coffee +4 -4
  23. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +45 -39
  24. data/lib/assets/javascripts/unpoly/classes/config.coffee +9 -0
  25. data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +18 -27
  26. data/lib/assets/javascripts/unpoly/classes/divertible_chain.coffee +39 -0
  27. data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +116 -0
  28. data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +8 -8
  29. data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +19 -19
  30. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +54 -31
  31. data/lib/assets/javascripts/unpoly/classes/{focus_tracker.coffee → focus_follower.coffee} +2 -2
  32. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +25 -25
  33. data/lib/assets/javascripts/unpoly/classes/html_parser.coffee +4 -11
  34. data/lib/assets/javascripts/unpoly/classes/motion_controller.coffee +157 -0
  35. data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +525 -0
  36. data/lib/assets/javascripts/unpoly/classes/record.coffee +8 -2
  37. data/lib/assets/javascripts/unpoly/classes/rect.js +21 -0
  38. data/lib/assets/javascripts/unpoly/classes/request.coffee +41 -35
  39. data/lib/assets/javascripts/unpoly/classes/response.coffee +7 -3
  40. data/lib/assets/javascripts/unpoly/classes/reveal_motion.coffee +102 -0
  41. data/lib/assets/javascripts/unpoly/classes/scroll_motion.coffee +67 -0
  42. data/lib/assets/javascripts/unpoly/classes/selector.coffee +60 -0
  43. data/lib/assets/javascripts/unpoly/classes/tether.coffee +105 -0
  44. data/lib/assets/javascripts/unpoly/classes/url_set.coffee +12 -7
  45. data/lib/assets/javascripts/unpoly/element.coffee.erb +1126 -0
  46. data/lib/assets/javascripts/unpoly/event.coffee.erb +437 -0
  47. data/lib/assets/javascripts/unpoly/feedback.coffee +73 -94
  48. data/lib/assets/javascripts/unpoly/form.coffee.erb +188 -181
  49. data/lib/assets/javascripts/unpoly/{dom.coffee.erb → fragment.coffee.erb} +250 -283
  50. data/lib/assets/javascripts/unpoly/framework.coffee +67 -0
  51. data/lib/assets/javascripts/unpoly/history.coffee +29 -28
  52. data/lib/assets/javascripts/unpoly/legacy.coffee +60 -0
  53. data/lib/assets/javascripts/unpoly/link.coffee.erb +127 -119
  54. data/lib/assets/javascripts/unpoly/log.coffee +99 -19
  55. data/lib/assets/javascripts/unpoly/modal.coffee.erb +95 -118
  56. data/lib/assets/javascripts/unpoly/motion.coffee.erb +158 -138
  57. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +0 -5
  58. data/lib/assets/javascripts/unpoly/popup.coffee.erb +119 -102
  59. data/lib/assets/javascripts/unpoly/protocol.coffee +11 -15
  60. data/lib/assets/javascripts/unpoly/proxy.coffee +62 -65
  61. data/lib/assets/javascripts/unpoly/radio.coffee +3 -5
  62. data/lib/assets/javascripts/unpoly/rails.coffee +8 -9
  63. data/lib/assets/javascripts/unpoly/syntax.coffee.erb +173 -125
  64. data/lib/assets/javascripts/unpoly/toast.coffee +25 -24
  65. data/lib/assets/javascripts/unpoly/tooltip.coffee +89 -79
  66. data/lib/assets/javascripts/unpoly/util.coffee.erb +579 -1074
  67. data/lib/assets/javascripts/unpoly/{layout.coffee.erb → viewport.coffee.erb} +334 -264
  68. data/lib/assets/stylesheets/unpoly/dom.sass +1 -1
  69. data/lib/assets/stylesheets/unpoly/layout.sass +2 -0
  70. data/lib/assets/stylesheets/unpoly/popup.sass +0 -1
  71. data/lib/assets/stylesheets/unpoly/tooltip.sass +17 -12
  72. data/lib/unpoly/rails/version.rb +1 -1
  73. data/package.json +1 -2
  74. data/spec_app/Gemfile +2 -1
  75. data/spec_app/Gemfile.lock +38 -27
  76. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  77. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -2
  78. data/spec_app/app/assets/stylesheets/integration_test.sass +14 -1
  79. data/spec_app/app/controllers/scroll_test_controller.rb +5 -0
  80. data/spec_app/app/views/css_test/modal.erb +6 -6
  81. data/spec_app/app/views/css_test/popup.erb +44 -18
  82. data/spec_app/app/views/css_test/tooltip.erb +23 -4
  83. data/spec_app/app/views/error_test/trigger.erb +1 -1
  84. data/spec_app/app/views/form_test/basics/new.erb +1 -3
  85. data/spec_app/app/views/pages/start.erb +9 -2
  86. data/spec_app/app/views/reveal_test/long1.erb +1 -1
  87. data/spec_app/app/views/reveal_test/long2.erb +1 -1
  88. data/spec_app/app/views/reveal_test/within_document_viewport.erb +24 -0
  89. data/spec_app/app/views/reveal_test/within_overflowing_div_viewport.erb +28 -0
  90. data/spec_app/app/views/scroll_test/long1.erb +30 -0
  91. data/spec_app/config/routes.rb +1 -0
  92. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +3 -0
  93. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +1 -0
  94. data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +17 -5
  95. data/spec_app/spec/javascripts/helpers/enable_logging.js.coffee +1 -1
  96. data/spec_app/spec/javascripts/helpers/fixture.js.coffee +25 -0
  97. data/spec_app/spec/javascripts/helpers/jquery_no_conflict.js +1 -0
  98. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -0
  99. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +1 -1
  100. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +2 -2
  101. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +4 -1
  102. data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +3 -0
  103. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +2 -1
  104. data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +2 -2
  105. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +18 -11
  106. data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +3 -0
  107. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +3 -0
  108. data/spec_app/spec/javascripts/helpers/spec_util.coffee +47 -0
  109. data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +3 -0
  110. data/spec_app/spec/javascripts/helpers/to_be_array.coffee +5 -0
  111. data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +6 -2
  112. data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +3 -0
  113. data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +6 -2
  114. data/spec_app/spec/javascripts/helpers/to_be_element.js.coffee +8 -0
  115. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +3 -0
  116. data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +3 -0
  117. data/spec_app/spec/javascripts/helpers/to_be_hidden.js.coffee +8 -0
  118. data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +3 -0
  119. data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +3 -0
  120. data/spec_app/spec/javascripts/helpers/to_be_scrolled_to.coffee +3 -0
  121. data/spec_app/spec/javascripts/helpers/to_be_visible.js.coffee +9 -0
  122. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +3 -0
  123. data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +3 -0
  124. data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +1 -2
  125. data/spec_app/spec/javascripts/helpers/to_equal_node_list.coffee +7 -0
  126. data/spec_app/spec/javascripts/helpers/to_equal_via_is_equal.js.coffee +7 -0
  127. data/spec_app/spec/javascripts/helpers/to_have_class.js.coffee +10 -0
  128. data/spec_app/spec/javascripts/helpers/to_have_descendant.js.coffee +10 -0
  129. data/spec_app/spec/javascripts/helpers/to_have_length.js.coffee +8 -0
  130. data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +7 -3
  131. data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +3 -0
  132. data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +1 -0
  133. data/spec_app/spec/javascripts/helpers/to_have_text.js.coffee +9 -0
  134. data/spec_app/spec/javascripts/helpers/to_have_unhandled_rejections.coffee +0 -21
  135. data/spec_app/spec/javascripts/helpers/to_match_list.coffee +14 -0
  136. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +3 -0
  137. data/spec_app/spec/javascripts/helpers/to_match_text.js.coffee +4 -1
  138. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +1 -0
  139. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +91 -7
  140. data/spec_app/spec/javascripts/helpers/wait_until_dom_ready.js.coffee +3 -0
  141. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +23 -90
  142. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +3 -0
  143. data/spec_app/spec/javascripts/up/classes/config_spec.coffee +24 -0
  144. data/spec_app/spec/javascripts/up/classes/divertible_chain_spec.coffee +45 -0
  145. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +5 -2
  146. data/spec_app/spec/javascripts/up/classes/params_spec.coffee +557 -0
  147. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +7 -4
  148. data/spec_app/spec/javascripts/up/classes/scroll_motion_spec.js.coffee +51 -0
  149. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +3 -0
  150. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +3 -2
  151. data/spec_app/spec/javascripts/up/element_spec.coffee +897 -0
  152. data/spec_app/spec/javascripts/up/event_spec.js.coffee +496 -0
  153. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +69 -48
  154. data/spec_app/spec/javascripts/up/form_spec.js.coffee +252 -194
  155. data/spec_app/spec/javascripts/up/{dom_spec.js.coffee → fragment_spec.js.coffee} +381 -388
  156. data/spec_app/spec/javascripts/up/history_spec.js.coffee +21 -19
  157. data/spec_app/spec/javascripts/up/jquery_spec.js.coffee +4 -0
  158. data/spec_app/spec/javascripts/up/legacy_spec.js.coffee +27 -0
  159. data/spec_app/spec/javascripts/up/link_spec.js.coffee +163 -160
  160. data/spec_app/spec/javascripts/up/log_spec.js.coffee +85 -12
  161. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +141 -123
  162. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +117 -113
  163. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +60 -77
  164. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +1 -0
  165. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +85 -78
  166. data/spec_app/spec/javascripts/up/radio_spec.js.coffee +29 -22
  167. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +14 -13
  168. data/spec_app/spec/javascripts/up/spec_spec.js.coffee +9 -0
  169. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +96 -66
  170. data/spec_app/spec/javascripts/up/toast_spec.js.coffee +37 -0
  171. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +31 -47
  172. data/spec_app/spec/javascripts/up/util_spec.js.coffee +725 -562
  173. data/spec_app/spec/javascripts/up/{layout_spec.js.coffee → viewport_spec.js.coffee} +175 -149
  174. metadata +57 -19
  175. data/lib/assets/javascripts/unpoly-bootstrap3/layout-ext.coffee +0 -5
  176. data/lib/assets/javascripts/unpoly/bus.coffee.erb +0 -518
  177. data/lib/assets/javascripts/unpoly/classes/extract_step.coffee +0 -4
  178. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +0 -125
  179. data/lib/assets/javascripts/unpoly/params.coffee.erb +0 -522
  180. data/spec_app/spec/javascripts/helpers/append_fixture.js.coffee +0 -8
  181. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +0 -210
  182. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +0 -9
  183. data/spec_app/spec/javascripts/up/params_spec.coffee +0 -768
  184. data/spec_app/vendor/asset-libs/jasmine-fixture-1.3.4/jasmine-fixture.js +0 -433
  185. data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/.bower.json +0 -26
  186. data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/jasmine-jquery.js +0 -838
@@ -3,8 +3,3 @@
3
3
  ###
4
4
  window.up =
5
5
  version: <%= Unpoly::Rails::VERSION.to_json %>
6
-
7
- deprecateRenamedModule: (oldName, newName) ->
8
- Object.defineProperty? up, oldName, get: ->
9
- up.warn("Deprecated: up.#{oldName} has been renamed to up.#{newName}")
10
- up[newName]
@@ -36,27 +36,36 @@ Popups have a minimal default design:
36
36
  The easiest way to change how the popup looks is to override the
37
37
  [default CSS styles](https://github.com/unpoly/unpoly/blob/master/lib/assets/stylesheets/unpoly/popup.sass).
38
38
 
39
- The HTML of a popup element is simply this:
39
+ The HTML of a popup element looks like this:
40
40
 
41
41
  <div class="up-popup">
42
- ...
42
+ <div class="up-popup-content">
43
+ Fragment content here
44
+ </div>
43
45
  </div>
44
46
 
47
+ The popup element is appended to the [viewport](/up.viewport) of the anchor element.
45
48
 
46
- @class up.popup
49
+ @module up.popup
47
50
  ###
48
- up.popup = (($) ->
51
+ up.popup = do ->
49
52
 
50
53
  u = up.util
54
+ e = up.element
51
55
 
52
56
  ###**
53
57
  Sets default options for future popups.
54
58
 
55
59
  @property up.popup.config
56
- @param {string} [config.position='bottom-right']
57
- Defines where the popup is attached to the opening element.
60
+ @param {string} [config.position='bottom']
61
+ Defines on which side of the opening element the popup is attached.
58
62
 
59
- Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
63
+ Valid values are `'top'`, `'right'`, `'bottom'` and `'left'`.
64
+ @param {string} [config.align='left']
65
+ Defines the alignment of the popup along its side.
66
+
67
+ When the popup's `{ position }` is `'top'` or `'bottom'`, valid `{ align }` values are `'left'`, `center'` and `'right'`.
68
+ When the popup's `{ position }` is `'left'` or `'right'`, valid `{ align }` values are `top'`, `center'` and `bottom'`.
60
69
  @param {string} [config.history=false]
61
70
  Whether opening a popup will add a browser history entry.
62
71
  @param {string} [config.openAnimation='fade-in']
@@ -76,14 +85,15 @@ up.popup = (($) ->
76
85
  open even it changes the page in the background.
77
86
  @stable
78
87
  ###
79
- config = u.config
88
+ config = new up.Config
80
89
  openAnimation: 'fade-in'
81
90
  closeAnimation: 'fade-out'
82
91
  openDuration: 150
83
92
  closeDuration: 100
84
93
  openEasing: null
85
94
  closeEasing: null
86
- position: 'bottom-right'
95
+ position: 'bottom'
96
+ align: 'left'
87
97
  history: false
88
98
 
89
99
  ###**
@@ -105,70 +115,56 @@ up.popup = (($) ->
105
115
  @experimental
106
116
  ###
107
117
 
108
- state = u.config
118
+ state = new up.Config
109
119
  phase: 'closed' # can be 'opening', 'opened', 'closing' and 'closed'
110
- $anchor: null # the element to which the tooltip is anchored
111
- $popup: null # the popup container
120
+ anchor: null # the element to which the tooltip is anchored
121
+ popup: null # the .up-popup element
122
+ content: null # the .up-popup-content element
123
+ tether: null # the up.Tether instance controlling the popup's position
112
124
  position: null # the position of the popup container element relative to its anchor
125
+ align: null
113
126
  sticky: null
114
127
  url: null
115
128
  coveredUrl: null
116
129
  coveredTitle: null
117
130
 
118
- chain = new u.DivertibleChain()
131
+ chain = new up.DivertibleChain()
119
132
 
120
133
  reset = ->
121
- state.$popup?.remove()
134
+ state.tether?.destroy()
122
135
  state.reset()
123
136
  chain.reset()
124
137
  config.reset()
125
138
 
126
- align = ->
127
- style = {}
128
-
129
- popupBox = u.measure(state.$popup)
130
-
131
- if u.isFixed(state.$anchor)
132
- linkBox = state.$anchor.get(0).getBoundingClientRect()
133
- style.position = 'fixed'
134
- else
135
- linkBox = u.measure(state.$anchor)
136
-
137
- switch state.position
138
- when 'bottom-right' # anchored to bottom-right of link, opens towards bottom-left
139
- style.top = linkBox.top + linkBox.height
140
- style.left = linkBox.left + linkBox.width - popupBox.width
141
- when 'bottom-left' # anchored to bottom-left of link, opens towards bottom-right
142
- style.top = linkBox.top + linkBox.height
143
- style.left = linkBox.left
144
- when 'top-right' # anchored to top-right of link, opens to top-left
145
- style.top = linkBox.top - popupBox.height
146
- style.left = linkBox.left + linkBox.width - popupBox.width
147
- when 'top-left' # anchored to top-left of link, opens to top-right
148
- style.top = linkBox.top - popupBox.height
149
- style.left = linkBox.left
150
- else
151
- up.fail("Unknown position option '%s'", state.position)
152
-
153
- state.$popup.attr('up-position', state.position)
154
-
155
- u.writeInlineStyle(state.$popup, style)
156
-
157
139
  discardHistory = ->
158
140
  state.coveredTitle = null
159
141
  state.coveredUrl = null
160
142
 
161
- createHiddenFrame = (target) ->
162
- $popup = u.$createElementFromSelector('.up-popup')
143
+ createHiddenFrame = (targetSelector) ->
144
+ state.tether = new up.Tether(u.only(state, 'anchor', 'position', 'align'))
145
+ state.popup = e.affix(state.tether.root, '.up-popup', 'up-position': state.position, 'up-align': state.align)
146
+ state.content = e.affix(state.popup, '.up-popup-content')
147
+
163
148
  # Create an empty element that will match the
164
149
  # selector that is being replaced.
165
- u.$createPlaceholder(target, $popup)
166
- $popup.hide()
167
- $popup.appendTo(document.body)
168
- state.$popup = $popup
150
+ up.fragment.createPlaceholder(targetSelector, state.content)
151
+ e.hide(state.popup)
169
152
 
170
153
  unveilFrame = ->
171
- state.$popup.show()
154
+ e.show(state.popup)
155
+
156
+ ###**
157
+ Forces the popup to update its position relative to its anchor element.
158
+
159
+ Unpoly automatically keep popups aligned when
160
+ the document is resized or scrolled. Complex layout changes may make
161
+ it necessary to call this function.
162
+
163
+ @function up.popup.sync
164
+ @experimental
165
+ ###
166
+ syncPosition = ->
167
+ state.tether?.sync()
172
168
 
173
169
  ###**
174
170
  Returns whether popup modal is currently open.
@@ -196,10 +192,15 @@ up.popup = (($) ->
196
192
  Will be ignored if `options.html` is given.
197
193
  @param {string} [options.target]
198
194
  A CSS selector that will be extracted from the response and placed into the popup.
199
- @param {string} [options.position='bottom-right']
200
- Defines where the popup is attached to the opening element.
195
+ @param {string} [options.position='bottom']
196
+ Defines on which side of the opening element the popup is attached.
197
+
198
+ Valid values are `'top'`, `'right'`, `'bottom'` and `'left'`.
199
+ @param {string} [options.align='left']
200
+ Defines the alignment of the popup along its side.
201
201
 
202
- Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
202
+ When the popup's `{ position }` is `'top'` or `'bottom'`, valid `{ align }` values are `'left'`, `center'` and `'right'`.
203
+ When the popup's `{ position }` is `'left'` or `'right'`, valid `{ align }` values are `top'`, `center'` and `bottom'`.
203
204
  @param {string} [options.html]
204
205
  A string of HTML from which to extract the popup contents. No network request will be made.
205
206
  @param {string} [options.confirm]
@@ -228,39 +229,40 @@ up.popup = (($) ->
228
229
  chain.asap closeNow, (-> attachNow(elementOrSelector, options))
229
230
 
230
231
  attachNow = (elementOrSelector, options) ->
231
- $anchor = $(elementOrSelector)
232
- $anchor.length or up.fail('Cannot attach popup to non-existing element %o', elementOrSelector)
232
+ anchor = e.get(elementOrSelector)
233
233
 
234
- options = u.options(options)
235
- url = u.option(u.pluckKey(options, 'url'), $anchor.attr('up-href'), $anchor.attr('href'))
236
- html = u.option(u.pluckKey(options, 'html'))
234
+ options ?= {}
235
+ url = u.pluckKey(options, 'url') ? anchor.getAttribute('up-href') ? anchor.getAttribute('href')
236
+ html = u.pluckKey(options, 'html')
237
237
  url or html or up.fail('up.popup.attach() requires either an { url } or { html } option')
238
- target = u.option(u.pluckKey(options, 'target'), $anchor.attr('up-popup')) or up.fail('No target selector given for [up-popup]')
239
- position = u.option(options.position, $anchor.attr('up-position'), config.position)
240
- options.animation = u.option(options.animation, $anchor.attr('up-animation'), config.openAnimation)
241
- options.sticky = u.option(options.sticky, u.castedAttr($anchor, 'up-sticky'), config.sticky)
242
- options.history = if up.browser.canPushState() then u.option(options.history, u.castedAttr($anchor, 'up-history'), config.history) else false
243
- options.confirm = u.option(options.confirm, $anchor.attr('up-confirm'))
244
- options.method = up.link.followMethod($anchor, options)
238
+ target = u.pluckKey(options, 'target') ? anchor.getAttribute('up-popup') or up.fail('No target selector given for [up-popup]')
239
+ position = options.position ? anchor.getAttribute('up-position') ? config.position
240
+ align = options.align ? anchor.getAttribute('up-align') ? config.align
241
+ options.animation ?= anchor.getAttribute('up-animation') ? config.openAnimation
242
+ options.sticky ?= e.booleanAttr(anchor, 'up-sticky') ? config.sticky
243
+ options.history = if up.browser.canPushState() then (options.history ? e.booleanOrStringAttr(anchor, 'up-history') ? config.history) else false
244
+ options.confirm ?= anchor.getAttribute('up-confirm')
245
+ options.method = up.link.followMethod(anchor, options)
245
246
  options.layer = 'popup'
246
- options.failTarget = u.option(options.failTarget, $anchor.attr('up-fail-target'))
247
- options.failLayer = u.option(options.failLayer, $anchor.attr('up-fail-layer'), 'auto')
247
+ options.failTarget ?= anchor.getAttribute('up-fail-target')
248
+ options.failLayer ?= anchor.getAttribute('up-fail-layer')
248
249
 
249
250
  # This will prevent up.replace() from looking for fallbacks, since
250
251
  # it knows the target will always exist.
251
252
  options.provideTarget = -> createHiddenFrame(target)
252
253
 
253
- animateOptions = up.motion.animateOptions(options, $anchor, duration: config.openDuration, easing: config.openEasing)
254
+ animateOptions = up.motion.animateOptions(options, anchor, duration: config.openDuration, easing: config.openEasing)
254
255
  extractOptions = u.merge(options, animation: false)
255
256
 
256
257
  if options.preload && url
257
258
  return up.replace(target, url, options)
258
259
 
259
260
  up.browser.whenConfirmed(options).then ->
260
- up.bus.whenEmitted('up:popup:open', url: url, message: 'Opening popup').then ->
261
+ up.event.whenEmitted('up:popup:open', url: url, anchor: anchor, log: 'Opening popup').then ->
261
262
  state.phase = 'opening'
262
- state.$anchor = $anchor
263
+ state.anchor = anchor
263
264
  state.position = position
265
+ state.align = align
264
266
  if options.history
265
267
  state.coveredUrl = up.browser.url()
266
268
  state.coveredTitle = document.title
@@ -270,18 +272,20 @@ up.popup = (($) ->
270
272
  else
271
273
  promise = up.replace(target, url, extractOptions)
272
274
  promise = promise.then ->
273
- align()
274
275
  unveilFrame()
275
- up.animate(state.$popup, options.animation, animateOptions)
276
+ syncPosition()
277
+ up.animate(state.popup, options.animation, animateOptions)
276
278
  promise = promise.then ->
277
279
  state.phase = 'opened'
278
- up.emit('up:popup:opened', message: 'Popup opened')#
280
+ up.emit(state.popup, 'up:popup:opened', anchor: state.anchor, log: 'Popup opened')#
279
281
  promise
280
282
 
281
283
  ###**
282
284
  This event is [emitted](/up.emit) when a popup is starting to open.
283
285
 
284
286
  @event up:popup:open
287
+ @param {Element} event.anchor
288
+ The element to which the popup will be attached.
285
289
  @param event.preventDefault()
286
290
  Event listeners may call this method to prevent the popup from opening.
287
291
  @stable
@@ -291,6 +295,8 @@ up.popup = (($) ->
291
295
  This event is [emitted](/up.emit) when a popup has finished opening.
292
296
 
293
297
  @event up:popup:opened
298
+ @param {Element} event.anchor
299
+ The element to which the popup was attached.
294
300
  @stable
295
301
  ###
296
302
 
@@ -324,37 +330,42 @@ up.popup = (($) ->
324
330
  animateOptions = up.motion.animateOptions(options, duration: config.closeDuration, easing: config.closeEasing)
325
331
  u.assign(options, animateOptions)
326
332
 
327
- up.bus.whenEmitted('up:popup:close', message: 'Closing popup', $element: state.$popup).then ->
333
+ up.event.whenEmitted('up:popup:close', anchor: state.anchor, log: 'Closing popup').then ->
328
334
  state.phase = 'closing'
329
335
  state.url = null
330
336
  state.coveredUrl = null
331
337
  state.coveredTitle = null
332
338
 
333
- up.destroy(state.$popup, options).then ->
339
+ up.destroy(state.popup, options).then ->
334
340
  state.phase = 'closed'
335
- state.$popup = null
336
- state.$anchor = null
341
+ state.tether.destroy()
342
+ state.tether = null
343
+ state.popup = null
344
+ state.content = null
345
+ state.anchor = null
337
346
  state.sticky = null
338
- up.emit('up:popup:closed', message: 'Popup closed')
347
+ up.emit('up:popup:closed', anchor: state.anchor, log: 'Popup closed')
339
348
 
340
- preloadNow = ($link, options) ->
349
+ preloadNow = (link, options) ->
341
350
  options = u.options(options)
342
351
  options.preload = true
343
352
  # Use attachNow() and not attachAsap() so (1) we don't close a currently open popup
344
353
  # and (2) our pending AJAX request does not prevent other popups from opening
345
- attachNow($link, options)
354
+ attachNow(link, options)
346
355
 
347
- toggleAsap = ($link, options) ->
348
- if u.hasClass($link, 'up-current')
356
+ toggleAsap = (link, options) ->
357
+ if link.classList.contains('up-current')
349
358
  closeAsap()
350
359
  else
351
- attachAsap($link, options)
360
+ attachAsap(link, options)
352
361
 
353
362
  ###**
354
363
  This event is [emitted](/up.emit) when a popup dialog
355
364
  is starting to [close](/up.popup.close).
356
365
 
357
366
  @event up:popup:close
367
+ @param {Element} event.anchor
368
+ The element to which the popup is attached.
358
369
  @param event.preventDefault()
359
370
  Event listeners may call this method to prevent the popup from closing.
360
371
  @stable
@@ -365,6 +376,8 @@ up.popup = (($) ->
365
376
  is done [closing](/up.popup.close).
366
377
 
367
378
  @event up:popup:closed
379
+ @param {Element} event.anchor
380
+ The element to which the popup was attached.
368
381
  @stable
369
382
  ###
370
383
 
@@ -384,8 +397,8 @@ up.popup = (($) ->
384
397
  @stable
385
398
  ###
386
399
  contains = (elementOrSelector) ->
387
- $element = $(elementOrSelector)
388
- $element.closest('.up-popup').length > 0
400
+ element = e.get(elementOrSelector)
401
+ !!e.closest(element, '.up-popup')
389
402
 
390
403
  ###**
391
404
  Opens this link's destination of in a popup overlay:
@@ -402,10 +415,15 @@ up.popup = (($) ->
402
415
  @param {string} up-popup
403
416
  The CSS selector that will be extracted from the response and
404
417
  displayed in a popup overlay.
405
- @param [up-position]
406
- Defines where the popup is attached to the opening element.
418
+ @param {string} [up-position]
419
+ Defines on which side of the opening element the popup is attached.
407
420
 
408
- Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
421
+ Valid values are `'top'`, `'right'`, `'bottom'` and `'left'`.
422
+ @param {string} [up-align]
423
+ Defines the alignment of the popup along its side.
424
+
425
+ When the popup's `{ position }` is `'top'` or `'bottom'`, valid `{ align }` values are `'left'`, `center'` and `'right'`.
426
+ When the popup's `{ position }` is `'left'` or `'right'`, valid `{ align }` values are `top'`, `center'` and `bottom'`.
409
427
  @param {string} [up-confirm]
410
428
  A message that will be displayed in a cancelable confirmation dialog
411
429
  before the popup is opened.
@@ -424,29 +442,29 @@ up.popup = (($) ->
424
442
  ###
425
443
  up.link.addFollowVariant '[up-popup]',
426
444
  # Don't just pass the `toggleAsap` function reference so we can stub it in tests
427
- follow: ($link, options) -> toggleAsap($link, options)
428
- preload: ($link, options) -> preloadNow($link, options)
445
+ follow: (link, options) -> toggleAsap(link, options)
446
+ preload: (link, options) -> preloadNow(link, options)
429
447
 
430
448
  # We close the popup when someone clicks on the document.
431
449
  # We also need to listen to up:action:consumed in case an [up-instant] link
432
450
  # was followed on mousedown.
433
451
  up.on 'click up:action:consumed', (event) ->
434
- $target = $(event.target)
452
+ target = event.target
435
453
  # Don't close when the user clicked on a popup opener.
436
- unless $target.closest('.up-popup, [up-popup]').length
454
+ unless e.closest(target, '.up-popup, [up-popup]')
437
455
  u.muteRejection closeAsap()
438
456
  # Do not halt the event chain here. The user is allowed to directly activate
439
457
  # a link in the background, even with a (now closing) popup open.
440
458
 
441
- up.on 'up:fragment:inserted', (event, $fragment) ->
442
- if contains($fragment)
443
- if newSource = $fragment.attr('up-source')
459
+ up.on 'up:fragment:inserted', (event, fragment) ->
460
+ if contains(fragment)
461
+ if newSource = fragment.getAttribute('up-source')
444
462
  state.url = newSource
445
463
  else if event.origin && contains(event.origin)
446
464
  u.muteRejection autoclose()
447
465
 
448
466
  # Close the pop-up overlay when the user presses ESC.
449
- up.bus.onEscape ->
467
+ up.event.onEscape ->
450
468
  u.muteRejection closeAsap()
451
469
 
452
470
  ###**
@@ -471,12 +489,12 @@ up.popup = (($) ->
471
489
  @selector .up-popup [up-close]
472
490
  @stable
473
491
  ###
474
- up.on 'click', '.up-popup [up-close]', (event, $element) ->
492
+ up.on 'click', '.up-popup [up-close]', (event) ->
475
493
  u.muteRejection closeAsap()
476
494
  # Only prevent the default when we actually closed a popup.
477
495
  # This way we can have buttons that close a popup when within a popup,
478
496
  # but link to a destination if not.
479
- up.bus.consumeAction(event)
497
+ up.event.consumeAction(event)
480
498
 
481
499
  # When the user uses the back button we will usually restore <body> or a base container.
482
500
  # We close any open modal because it probably won't match the restored state.
@@ -494,5 +512,4 @@ up.popup = (($) ->
494
512
  config: config
495
513
  contains: contains
496
514
  isOpen: isOpen
497
-
498
- )(jQuery)
515
+ sync: syncPosition
@@ -172,11 +172,12 @@ The **simplest implementation** is to set this cookie for every request that is
172
172
  an existing cookie should be deleted.
173
173
 
174
174
 
175
- @class up.protocol
175
+ @module up.protocol
176
176
  ###
177
- up.protocol = (($) ->
177
+ up.protocol = do ->
178
178
 
179
179
  u = up.util
180
+ e = up.element
180
181
 
181
182
  ###**
182
183
  @function up.protocol.locationFromXhr
@@ -215,7 +216,7 @@ up.protocol = (($) ->
215
216
  # Remove the method cookie as soon as possible.
216
217
  # Don't wait until the first call to initialRequestMethod(),
217
218
  # which might be much later.
218
- up.bus.on('up:framework:booted', initialRequestMethod)
219
+ up.on('up:framework:booted', initialRequestMethod)
219
220
 
220
221
  ###**
221
222
  Configures strings used in the optional [server protocol](/up.protocol).
@@ -237,7 +238,7 @@ up.protocol = (($) ->
237
238
  The name of the HTTP header that will include the
238
239
  [CSRF token](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Synchronizer_token_pattern)
239
240
  for AJAX requests.
240
- @param {String|Function} [config.csrfParam]
241
+ @param {string|Function(): string} [config.csrfParam]
241
242
  The `name` of the hidden `<input>` used for sending a
242
243
  [CSRF token](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Synchronizer_token_pattern) when
243
244
  submitting a default, non-AJAX form. For AJAX request the token is sent as an HTTP header instead.
@@ -249,7 +250,7 @@ up.protocol = (($) ->
249
250
 
250
251
  <meta name="csrf-param" content="authenticity_token" />
251
252
 
252
- @param {String|Function} [config.csrfToken]
253
+ @param {string|Function(): string} [config.csrfToken]
253
254
  The [CSRF token](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Synchronizer_token_pattern)
254
255
  to send for unsafe requests. The token will be sent as either a HTTP header (for AJAX requests)
255
256
  or hidden form `<input>` (for default, non-AJAX form submissions).
@@ -263,7 +264,7 @@ up.protocol = (($) ->
263
264
 
264
265
  @experimental
265
266
  ###
266
- config = u.config
267
+ config = new up.Config
267
268
  targetHeader: 'X-Up-Target'
268
269
  failTargetHeader: 'X-Up-Fail-Target'
269
270
  locationHeader: 'X-Up-Location'
@@ -272,8 +273,8 @@ up.protocol = (($) ->
272
273
  methodHeader: 'X-Up-Method'
273
274
  methodCookie: '_up_method'
274
275
  methodParam: '_method'
275
- csrfParam: -> $('meta[name="csrf-param"]').attr('content')
276
- csrfToken: -> $('meta[name="csrf-token"]').attr('content')
276
+ csrfParam: -> e.metaContent('csrf-param')
277
+ csrfToken: -> e.metaContent('csrf-token')
277
278
  csrfHeader: 'X-CSRF-Token'
278
279
 
279
280
  csrfParam = ->
@@ -282,14 +283,11 @@ up.protocol = (($) ->
282
283
  csrfToken = ->
283
284
  u.evalOption(config.csrfToken)
284
285
 
285
- ## Unfortunately we cannot offer reset via event without introducing cycles
286
- ## in the asset load order
287
- #
288
- # up.on 'up:framework:reset', reset
289
-
290
286
  reset = ->
291
287
  config.reset()
292
288
 
289
+ up.on 'up:framework:reset', reset
290
+
293
291
  config: config
294
292
  reset: reset
295
293
  locationFromXhr: locationFromXhr
@@ -298,5 +296,3 @@ up.protocol = (($) ->
298
296
  csrfParam :csrfParam
299
297
  csrfToken: csrfToken
300
298
  initialRequestMethod: initialRequestMethod
301
-
302
- )(jQuery)