unpoly-rails 0.26.2 → 0.27.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -1
- data/dist/unpoly.js +704 -446
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly/browser.js.coffee +18 -9
- data/lib/assets/javascripts/unpoly/bus.js.coffee +28 -1
- data/lib/assets/javascripts/unpoly/flow.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/history.js.coffee +54 -22
- data/lib/assets/javascripts/unpoly/link.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/log.js.coffee +19 -12
- data/lib/assets/javascripts/unpoly/modal.js.coffee +119 -124
- data/lib/assets/javascripts/unpoly/motion.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/navigation.js.coffee +2 -6
- data/lib/assets/javascripts/unpoly/popup.js.coffee +136 -126
- data/lib/assets/javascripts/unpoly/proxy.js.coffee +0 -2
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/tooltip.js.coffee +101 -46
- data/lib/assets/javascripts/unpoly/util.js.coffee +76 -7
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/assets/stylesheets/integration_test.sass +4 -0
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +5 -0
- data/spec_app/app/views/css_test/modal.erb +3 -0
- data/spec_app/app/views/css_test/modal_contents.erb +5 -0
- data/spec_app/app/views/css_test/popup.erb +11 -11
- data/spec_app/app/views/css_test/tooltip.erb +12 -5
- data/spec_app/app/views/pages/start.erb +4 -0
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +2 -3
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +97 -88
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +100 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +18 -16
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +102 -97
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +89 -75
- data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +17 -5
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +89 -70
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +23 -0
- metadata +4 -2
@@ -39,12 +39,8 @@ up.navigation = (($) ->
|
|
39
39
|
SELECTOR_SECTION = 'a, [up-href]'
|
40
40
|
|
41
41
|
normalizeUrl = (url) ->
|
42
|
-
if u.isPresent(url)
|
43
|
-
|
44
|
-
search: false
|
45
|
-
stripTrailingSlash: true
|
46
|
-
)
|
47
|
-
|
42
|
+
u.normalizeUrl(url) if u.isPresent(url)
|
43
|
+
|
48
44
|
sectionUrls = ($section) ->
|
49
45
|
urls = []
|
50
46
|
for attr in ['href', 'up-href', 'up-alias']
|
@@ -46,27 +46,6 @@ up.popup = (($) ->
|
|
46
46
|
|
47
47
|
u = up.util
|
48
48
|
|
49
|
-
###*
|
50
|
-
Returns the source URL for the fragment displayed
|
51
|
-
in the current popup, or `undefined` if no popup is open.
|
52
|
-
|
53
|
-
@function up.popup.url
|
54
|
-
@return {String}
|
55
|
-
the source URL
|
56
|
-
@stable
|
57
|
-
###
|
58
|
-
currentUrl = undefined
|
59
|
-
|
60
|
-
###*
|
61
|
-
Returns the URL of the page or modal behind the popup.
|
62
|
-
|
63
|
-
@function up.popup.coveredUrl
|
64
|
-
@return {String}
|
65
|
-
@experimental
|
66
|
-
###
|
67
|
-
coveredUrl = ->
|
68
|
-
$('.up-popup').attr('up-covered-url')
|
69
|
-
|
70
49
|
###*
|
71
50
|
Sets default options for future popups.
|
72
51
|
|
@@ -97,68 +76,86 @@ up.popup = (($) ->
|
|
97
76
|
config = u.config
|
98
77
|
openAnimation: 'fade-in'
|
99
78
|
closeAnimation: 'fade-out'
|
100
|
-
openDuration:
|
101
|
-
closeDuration:
|
79
|
+
openDuration: 150
|
80
|
+
closeDuration: 100
|
102
81
|
openEasing: null
|
103
82
|
closeEasing: null
|
104
83
|
position: 'bottom-right'
|
105
84
|
history: false
|
106
85
|
|
86
|
+
###*
|
87
|
+
Returns the source URL for the fragment displayed
|
88
|
+
in the current popup, or `undefined` if no popup is open.
|
89
|
+
|
90
|
+
@function up.popup.url
|
91
|
+
@return {String}
|
92
|
+
the source URL
|
93
|
+
@stable
|
94
|
+
###
|
95
|
+
|
96
|
+
###*
|
97
|
+
Returns the URL of the page or modal behind the popup.
|
98
|
+
|
99
|
+
@function up.popup.coveredUrl
|
100
|
+
@return {String}
|
101
|
+
@experimental
|
102
|
+
###
|
103
|
+
|
104
|
+
state = u.config
|
105
|
+
phase: 'closed' # can be 'opening', 'opened', 'closing' and 'closed'
|
106
|
+
$anchor: null # the element to which the tooltip is anchored
|
107
|
+
$popup: null # the popup container
|
108
|
+
position: null # the position of the popup container element relative to its anchor
|
109
|
+
sticky: null
|
110
|
+
url: null
|
111
|
+
coveredUrl: null
|
112
|
+
coveredTitle: null
|
113
|
+
|
114
|
+
chain = new u.DivertibleChain()
|
115
|
+
|
107
116
|
reset = ->
|
108
|
-
|
117
|
+
state.$popup?.remove()
|
118
|
+
state.reset()
|
119
|
+
chain.reset()
|
109
120
|
config.reset()
|
110
121
|
|
111
|
-
|
122
|
+
align = ->
|
112
123
|
css = {}
|
113
124
|
|
114
|
-
|
115
|
-
popupBox = u.measure($popup)
|
125
|
+
popupBox = u.measure(state.$popup)
|
116
126
|
|
117
|
-
if u.isFixed(
|
118
|
-
linkBox =
|
127
|
+
if u.isFixed(state.$anchor)
|
128
|
+
linkBox = state.$anchor.get(0).getBoundingClientRect()
|
119
129
|
css['position'] = 'fixed'
|
120
130
|
else
|
121
|
-
linkBox = u.measure(
|
131
|
+
linkBox = u.measure(state.$anchor)
|
122
132
|
|
123
|
-
switch position
|
124
|
-
when
|
133
|
+
switch state.position
|
134
|
+
when 'bottom-right' # anchored to bottom-right of link, opens towards bottom-left
|
125
135
|
css['top'] = linkBox.top + linkBox.height
|
126
136
|
css['left'] = linkBox.left + linkBox.width - popupBox.width
|
127
|
-
when
|
137
|
+
when 'bottom-left' # anchored to bottom-left of link, opens towards bottom-right
|
128
138
|
css['top'] = linkBox.top + linkBox.height
|
129
139
|
css['left'] = linkBox.left
|
130
|
-
when
|
140
|
+
when 'top-right' # anchored to top-right of link, opens to top-left
|
131
141
|
css['top'] = linkBox.top - popupBox.height
|
132
142
|
css['left'] = linkBox.left + linkBox.width - popupBox.width
|
133
|
-
when
|
143
|
+
when 'top-left' # anchored to top-left of link, opens to top-right
|
134
144
|
css['top'] = linkBox.top - popupBox.height
|
135
145
|
css['left'] = linkBox.left
|
136
146
|
else
|
137
|
-
u.error("Unknown position option '%s'", position)
|
147
|
+
u.error("Unknown position option '%s'", state.position)
|
138
148
|
|
139
|
-
|
140
|
-
|
149
|
+
state.$popup.attr('up-position', state.position)
|
150
|
+
state.$popup.css(css)
|
141
151
|
|
142
|
-
|
143
|
-
$popup =
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
if isOpen()
|
150
|
-
promise = promise.then -> close()
|
151
|
-
promise = promise.then ->
|
152
|
-
$popup = u.$createElementFromSelector('.up-popup')
|
153
|
-
$popup.attr('up-sticky', '') if options.sticky
|
154
|
-
$popup.attr('up-covered-url', up.browser.url())
|
155
|
-
$popup.attr('up-covered-title', document.title)
|
156
|
-
# Create an empty element that will match the
|
157
|
-
# selector that is being replaced.
|
158
|
-
u.$createPlaceholder(target, $popup)
|
159
|
-
$popup.appendTo(document.body)
|
160
|
-
$popup
|
161
|
-
return promise
|
152
|
+
createFrame = (target) ->
|
153
|
+
$popup = u.$createElementFromSelector('.up-popup')
|
154
|
+
# Create an empty element that will match the
|
155
|
+
# selector that is being replaced.
|
156
|
+
u.$createPlaceholder(target, $popup)
|
157
|
+
$popup.appendTo(document.body)
|
158
|
+
state.$popup = $popup
|
162
159
|
|
163
160
|
###*
|
164
161
|
Returns whether popup modal is currently open.
|
@@ -167,13 +164,13 @@ up.popup = (($) ->
|
|
167
164
|
@stable
|
168
165
|
###
|
169
166
|
isOpen = ->
|
170
|
-
|
167
|
+
state.phase == 'opened' || state.phase == 'opening'
|
171
168
|
|
172
169
|
###*
|
173
170
|
Attaches a popup overlay to the given element or selector.
|
174
171
|
|
175
172
|
Emits events [`up:popup:open`](/up:popup:open) and [`up:popup:opened`](/up:popup:opened).
|
176
|
-
|
173
|
+
|
177
174
|
@function up.popup.attach
|
178
175
|
@param {Element|jQuery|String} elementOrSelector
|
179
176
|
@param {String} [options.url]
|
@@ -205,41 +202,51 @@ up.popup = (($) ->
|
|
205
202
|
the opening animation has completed.
|
206
203
|
@stable
|
207
204
|
###
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
205
|
+
attachAsap = (elementOrSelector, options) ->
|
206
|
+
curriedAttachNow = -> attachNow(elementOrSelector, options)
|
207
|
+
if isOpen()
|
208
|
+
chain.asap(closeNow, curriedAttachNow)
|
209
|
+
else
|
210
|
+
chain.asap(curriedAttachNow)
|
211
|
+
chain.promise()
|
212
|
+
|
213
|
+
attachNow = (elementOrSelector, options) ->
|
214
|
+
$anchor = $(elementOrSelector)
|
215
|
+
$anchor.length or u.error('Cannot attach popup to non-existing element %o', elementOrSelector)
|
216
|
+
|
212
217
|
options = u.options(options)
|
213
|
-
url = u.option(u.pluckKey(options, 'url'), $
|
218
|
+
url = u.option(u.pluckKey(options, 'url'), $anchor.attr('up-href'), $anchor.attr('href'))
|
214
219
|
html = u.option(u.pluckKey(options, 'html'))
|
215
|
-
target = u.option(u.pluckKey(options, 'target'), $
|
216
|
-
|
217
|
-
options.animation = u.option(options.animation, $
|
218
|
-
options.sticky = u.option(options.sticky, u.castedAttr($
|
219
|
-
options.history = if up.browser.canPushState() then u.option(options.history, u.castedAttr($
|
220
|
-
options.confirm = u.option(options.confirm, $
|
221
|
-
options.method = up.link.followMethod($
|
222
|
-
animateOptions = up.motion.animateOptions(options, $
|
223
|
-
|
224
|
-
up.browser.
|
225
|
-
|
226
|
-
|
220
|
+
target = u.option(u.pluckKey(options, 'target'), $anchor.attr('up-popup'), 'body')
|
221
|
+
position = u.option(options.position, $anchor.attr('up-position'), config.position)
|
222
|
+
options.animation = u.option(options.animation, $anchor.attr('up-animation'), config.openAnimation)
|
223
|
+
options.sticky = u.option(options.sticky, u.castedAttr($anchor, 'up-sticky'), config.sticky)
|
224
|
+
options.history = if up.browser.canPushState() then u.option(options.history, u.castedAttr($anchor, 'up-history'), config.history) else false
|
225
|
+
options.confirm = u.option(options.confirm, $anchor.attr('up-confirm'))
|
226
|
+
options.method = up.link.followMethod($anchor, options)
|
227
|
+
animateOptions = up.motion.animateOptions(options, $anchor, duration: config.openDuration, easing: config.openEasing)
|
228
|
+
|
229
|
+
up.browser.whenConfirmed(options).then ->
|
230
|
+
up.bus.whenEmitted('up:popup:open', url: url, message: 'Opening popup').then ->
|
231
|
+
state.phase = 'opening'
|
232
|
+
state.$anchor = $anchor
|
233
|
+
state.position = position
|
234
|
+
state.coveredUrl = up.browser.url()
|
235
|
+
state.coveredTitle = document.title
|
236
|
+
state.sticky = options.sticky
|
237
|
+
options.beforeSwap = -> createFrame(target)
|
227
238
|
extractOptions = u.merge(options, animation: false)
|
228
239
|
if html
|
229
240
|
promise = up.extract(target, html, extractOptions)
|
230
241
|
else
|
231
242
|
promise = up.replace(target, url, extractOptions)
|
232
243
|
promise = promise.then ->
|
233
|
-
|
244
|
+
align()
|
245
|
+
up.animate(state.$popup, options.animation, animateOptions)
|
234
246
|
promise = promise.then ->
|
235
|
-
|
236
|
-
|
237
|
-
up.emit('up:popup:opened', message: 'Popup opened')
|
247
|
+
state.phase = 'opened'
|
248
|
+
up.emit('up:popup:opened', message: 'Popup opened')#
|
238
249
|
promise
|
239
|
-
else
|
240
|
-
# Although someone prevented the destruction, keep a uniform API for
|
241
|
-
# callers by returning a promise that will never be resolved.
|
242
|
-
u.unresolvablePromise()
|
243
250
|
|
244
251
|
###*
|
245
252
|
This event is [emitted](/up.emit) when a popup is starting to open.
|
@@ -256,7 +263,7 @@ up.popup = (($) ->
|
|
256
263
|
@event up:popup:opened
|
257
264
|
@stable
|
258
265
|
###
|
259
|
-
|
266
|
+
|
260
267
|
###*
|
261
268
|
Closes a currently opened popup overlay.
|
262
269
|
|
@@ -272,27 +279,35 @@ up.popup = (($) ->
|
|
272
279
|
animation has finished.
|
273
280
|
@stable
|
274
281
|
###
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
282
|
+
closeAsap = (options) ->
|
283
|
+
if isOpen()
|
284
|
+
chain.asap -> closeNow(options)
|
285
|
+
chain.promise()
|
286
|
+
|
287
|
+
closeNow = (options) ->
|
288
|
+
unless isOpen() # this can happen when a request fails and the chain proceeds to the next task
|
289
|
+
return u.resolvedPromise()
|
290
|
+
|
291
|
+
options = u.options(options,
|
292
|
+
animation: config.closeAnimation
|
293
|
+
url: state.coveredUrl,
|
294
|
+
title: state.coveredTitle
|
295
|
+
)
|
296
|
+
animateOptions = up.motion.animateOptions(options, duration: config.closeDuration, easing: config.closeEasing)
|
297
|
+
u.extend(options, animateOptions)
|
298
|
+
|
299
|
+
up.bus.whenEmitted('up:popup:close', message: 'Closing popup', $element: state.$popup).then ->
|
300
|
+
state.phase = 'closing'
|
301
|
+
state.url = null
|
302
|
+
state.coveredUrl = null
|
303
|
+
state.coveredTitle = null
|
304
|
+
|
305
|
+
up.destroy(state.$popup, options).then ->
|
306
|
+
state.phase = 'closed'
|
307
|
+
state.$popup = null
|
308
|
+
state.$anchor = null
|
309
|
+
state.sticky = null
|
310
|
+
up.emit('up:popup:closed', message: 'Popup closed')
|
296
311
|
|
297
312
|
###*
|
298
313
|
This event is [emitted](/up.emit) when a popup dialog
|
@@ -313,9 +328,7 @@ up.popup = (($) ->
|
|
313
328
|
###
|
314
329
|
|
315
330
|
autoclose = ->
|
316
|
-
unless
|
317
|
-
discardHistory()
|
318
|
-
close()
|
331
|
+
closeAsap() unless state.sticky
|
319
332
|
|
320
333
|
###*
|
321
334
|
Returns whether the given element or selector is contained
|
@@ -363,29 +376,29 @@ up.popup = (($) ->
|
|
363
376
|
###
|
364
377
|
up.link.onAction('[up-popup]', ($link) ->
|
365
378
|
if $link.is('.up-current')
|
366
|
-
|
379
|
+
closeAsap()
|
367
380
|
else
|
368
|
-
|
381
|
+
attachAsap($link)
|
369
382
|
)
|
370
383
|
|
371
384
|
# Close the popup when someone clicks outside the popup
|
372
385
|
# (but not on a popup opener).
|
373
|
-
up.on('
|
386
|
+
up.on('mousedown', 'body', (event, $body) ->
|
374
387
|
$target = $(event.target)
|
375
|
-
unless $target.closest('.up-popup
|
376
|
-
|
388
|
+
unless $target.closest('.up-popup, [up-popup]').length
|
389
|
+
closeAsap()
|
377
390
|
)
|
378
391
|
|
379
392
|
up.on('up:fragment:inserted', (event, $fragment) ->
|
380
393
|
if contains($fragment)
|
381
394
|
if newSource = $fragment.attr('up-source')
|
382
|
-
|
395
|
+
state.url = newSource
|
383
396
|
else if contains(event.origin)
|
384
397
|
autoclose()
|
385
398
|
)
|
386
399
|
|
387
400
|
# Close the pop-up overlay when the user presses ESC.
|
388
|
-
up.bus.onEscape(
|
401
|
+
up.bus.onEscape(closeAsap)
|
389
402
|
|
390
403
|
###*
|
391
404
|
When an element with this attribute is clicked,
|
@@ -402,8 +415,8 @@ up.popup = (($) ->
|
|
402
415
|
@stable
|
403
416
|
###
|
404
417
|
up.on('click', '[up-close]', (event, $element) ->
|
405
|
-
if $element
|
406
|
-
|
418
|
+
if contains($element)
|
419
|
+
closeAsap()
|
407
420
|
# Only prevent the default when we actually closed a popup.
|
408
421
|
# This way we can have buttons that close a popup when within a popup,
|
409
422
|
# but link to a destination if not.
|
@@ -414,15 +427,12 @@ up.popup = (($) ->
|
|
414
427
|
up.on 'up:framework:reset', reset
|
415
428
|
|
416
429
|
knife: eval(Knife?.point)
|
417
|
-
attach:
|
418
|
-
close:
|
419
|
-
url: ->
|
420
|
-
coveredUrl: coveredUrl
|
430
|
+
attach: attachAsap
|
431
|
+
close: closeAsap
|
432
|
+
url: -> state.url
|
433
|
+
coveredUrl: -> state.coveredUrl
|
421
434
|
config: config
|
422
|
-
defaults: -> u.error('up.popup.defaults(...) no longer exists. Set values on he up.popup.config property instead.')
|
423
435
|
contains: contains
|
424
|
-
open: -> up.error('up.popup.open no longer exists. Please use up.popup.attach instead.')
|
425
|
-
source: -> up.error('up.popup.source no longer exists. Please use up.popup.url instead.')
|
426
436
|
isOpen: isOpen
|
427
437
|
|
428
438
|
)(jQuery)
|
@@ -50,7 +50,7 @@ up.syntax = (($) ->
|
|
50
50
|
// your code here
|
51
51
|
});
|
52
52
|
|
53
|
-
The functions will be called on elements
|
53
|
+
The functions will be called on elements matching `.action` when
|
54
54
|
the page loads, or whenever a matching fragment is [updated through Unpoly](/up.replace)
|
55
55
|
later.
|
56
56
|
|
@@ -44,46 +44,68 @@ up.tooltip = (($) ->
|
|
44
44
|
The animation used to open a tooltip.
|
45
45
|
@param {String} [config.closeAnimation='fade-out']
|
46
46
|
The animation used to close a tooltip.
|
47
|
+
@param {Number} [config.openDuration]
|
48
|
+
The duration of the open animation (in milliseconds).
|
49
|
+
@param {Number} [config.closeDuration]
|
50
|
+
The duration of the close animation (in milliseconds).
|
51
|
+
@param {String} [config.openEasing]
|
52
|
+
The timing function controlling the acceleration of the opening animation.
|
53
|
+
@param {String} [config.closeEasing]
|
54
|
+
The timing function controlling the acceleration of the closing animation.
|
47
55
|
@stable
|
48
56
|
###
|
49
57
|
config = u.config
|
50
58
|
position: 'top'
|
51
59
|
openAnimation: 'fade-in'
|
52
60
|
closeAnimation: 'fade-out'
|
61
|
+
openDuration: 100
|
62
|
+
closeDuration: 50
|
63
|
+
openEasing: null
|
64
|
+
closeEasing: null
|
65
|
+
|
66
|
+
state = u.config
|
67
|
+
phase: 'closed' # can be 'opening', 'opened', 'closing' and 'closed'
|
68
|
+
$anchor: null # the element to which the tooltip is anchored
|
69
|
+
$tooltip: null # the tooltiop element
|
70
|
+
position: null # the position of the tooltip element relative to its anchor
|
71
|
+
|
72
|
+
chain = new u.DivertibleChain()
|
53
73
|
|
54
74
|
reset = ->
|
55
75
|
# Destroy the tooltip container regardless whether it's currently in a closing animation
|
56
|
-
|
76
|
+
state.$tooltip?.remove()
|
77
|
+
state.reset()
|
78
|
+
chain.reset()
|
57
79
|
config.reset()
|
58
80
|
|
59
|
-
|
81
|
+
align = ->
|
60
82
|
css = {}
|
61
|
-
tooltipBox = u.measure(
|
83
|
+
tooltipBox = u.measure(state.$tooltip)
|
62
84
|
|
63
|
-
if u.isFixed(
|
64
|
-
linkBox =
|
85
|
+
if u.isFixed(state.$anchor)
|
86
|
+
linkBox = state.$anchor.get(0).getBoundingClientRect()
|
65
87
|
css['position'] = 'fixed'
|
66
88
|
else
|
67
|
-
linkBox = u.measure(
|
89
|
+
linkBox = u.measure(state.$anchor)
|
68
90
|
|
69
|
-
switch position
|
70
|
-
when
|
91
|
+
switch state.position
|
92
|
+
when 'top'
|
71
93
|
css['top'] = linkBox.top - tooltipBox.height
|
72
94
|
css['left'] = linkBox.left + 0.5 * (linkBox.width - tooltipBox.width)
|
73
|
-
when
|
95
|
+
when 'left'
|
74
96
|
css['top'] = linkBox.top + 0.5 * (linkBox.height - tooltipBox.height)
|
75
97
|
css['left'] = linkBox.left - tooltipBox.width
|
76
|
-
when
|
98
|
+
when 'right'
|
77
99
|
css['top'] = linkBox.top + 0.5 * (linkBox.height - tooltipBox.height)
|
78
100
|
css['left'] = linkBox.left + linkBox.width
|
79
|
-
when
|
101
|
+
when 'bottom'
|
80
102
|
css['top'] = linkBox.top + linkBox.height
|
81
103
|
css['left'] = linkBox.left + 0.5 * (linkBox.width - tooltipBox.width)
|
82
104
|
else
|
83
|
-
u.error("Unknown position option '%s'", position)
|
105
|
+
u.error("Unknown position option '%s'", state.position)
|
84
106
|
|
85
|
-
|
86
|
-
|
107
|
+
state.$tooltip.attr('up-position', state.position)
|
108
|
+
state.$tooltip.css(css)
|
87
109
|
|
88
110
|
createElement = (options) ->
|
89
111
|
$element = u.$createElementFromSelector('.up-tooltip')
|
@@ -92,7 +114,7 @@ up.tooltip = (($) ->
|
|
92
114
|
else
|
93
115
|
$element.html(options.html)
|
94
116
|
$element.appendTo(document.body)
|
95
|
-
$element
|
117
|
+
state.$tooltip = $element
|
96
118
|
|
97
119
|
###*
|
98
120
|
Opens a tooltip over the given element.
|
@@ -100,7 +122,7 @@ up.tooltip = (($) ->
|
|
100
122
|
up.tooltip.attach('.help', {
|
101
123
|
html: 'Enter multiple words or phrases'
|
102
124
|
});
|
103
|
-
|
125
|
+
|
104
126
|
@function up.tooltip.attach
|
105
127
|
@param {Element|jQuery|String} elementOrSelector
|
106
128
|
@param {String} [options.html]
|
@@ -114,33 +136,68 @@ up.tooltip = (($) ->
|
|
114
136
|
A promise that will be resolved when the tooltip's opening animation has finished.
|
115
137
|
@stable
|
116
138
|
###
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
139
|
+
attachAsap = (elementOrSelector, options = {}) ->
|
140
|
+
curriedAttachNow = -> attachNow(elementOrSelector, options)
|
141
|
+
if isOpen()
|
142
|
+
chain.asap(closeNow, curriedAttachNow)
|
143
|
+
else
|
144
|
+
chain.asap(curriedAttachNow)
|
145
|
+
chain.promise()
|
146
|
+
|
147
|
+
attachNow = (elementOrSelector, options) ->
|
148
|
+
$anchor = $(elementOrSelector)
|
149
|
+
options = u.options(options)
|
150
|
+
html = u.option(options.html, $anchor.attr('up-tooltip-html'))
|
151
|
+
text = u.option(options.text, $anchor.attr('up-tooltip'))
|
152
|
+
position = u.option(options.position, $anchor.attr('up-position'), config.position)
|
153
|
+
animation = u.option(options.animation, u.castedAttr($anchor, 'up-animation'), config.openAnimation)
|
154
|
+
animateOptions = up.motion.animateOptions(options, $anchor, duration: config.openDuration, easing: config.openEasing)
|
155
|
+
|
156
|
+
state.phase = 'opening'
|
157
|
+
state.$anchor = $anchor
|
158
|
+
createElement(text: text, html: html)
|
159
|
+
state.position = position
|
160
|
+
align()
|
161
|
+
up.animate(state.$tooltip, animation, animateOptions).then ->
|
162
|
+
state.phase = 'opened'
|
128
163
|
|
129
164
|
###*
|
130
165
|
Closes a currently shown tooltip.
|
131
166
|
Does nothing if no tooltip is currently shown.
|
132
|
-
|
167
|
+
|
133
168
|
@function up.tooltip.close
|
134
169
|
@param {Object} options
|
135
170
|
See options for [`up.animate`](/up.animate).
|
171
|
+
@return {Promise}
|
172
|
+
A promise for the end of the closing animation.
|
136
173
|
@stable
|
137
174
|
###
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
175
|
+
closeAsap = (options) ->
|
176
|
+
if isOpen()
|
177
|
+
chain.asap -> closeNow(options)
|
178
|
+
chain.promise()
|
179
|
+
|
180
|
+
closeNow = (options) ->
|
181
|
+
unless isOpen() # this can happen when a request fails and the chain proceeds to the next task
|
182
|
+
return u.resolvedPromise()
|
183
|
+
|
184
|
+
options = u.options(options, animation: config.closeAnimation)
|
185
|
+
animateOptions = up.motion.animateOptions(options, duration: config.closeDuration, easing: config.closeEasing)
|
186
|
+
u.extend(options, animateOptions)
|
187
|
+
state.phase = 'closing'
|
188
|
+
up.destroy(state.$tooltip, options).then ->
|
189
|
+
state.phase = 'closed'
|
190
|
+
state.$tooltip = null
|
191
|
+
state.$anchor = null
|
192
|
+
|
193
|
+
###*
|
194
|
+
Returns whether a tooltip is currently showing.
|
195
|
+
|
196
|
+
@function up.tooltip.isOpen
|
197
|
+
@stable
|
198
|
+
###
|
199
|
+
isOpen = ->
|
200
|
+
state.phase == 'opening' || state.phase == 'opened'
|
144
201
|
|
145
202
|
###*
|
146
203
|
Displays a tooltip with text content when hovering the mouse over this element:
|
@@ -171,30 +228,28 @@ up.tooltip = (($) ->
|
|
171
228
|
@selector [up-tooltip-html]
|
172
229
|
@stable
|
173
230
|
###
|
174
|
-
up.compiler('[up-tooltip], [up-tooltip-html]', ($
|
231
|
+
up.compiler('[up-tooltip], [up-tooltip-html]', ($opener) ->
|
175
232
|
# Don't register these events on document since *every*
|
176
233
|
# mouse move interaction bubbles up to the document.
|
177
|
-
$
|
178
|
-
$
|
234
|
+
$opener.on('mouseenter', -> attachAsap($opener))
|
235
|
+
$opener.on('mouseleave', -> closeAsap())
|
179
236
|
)
|
180
237
|
|
181
238
|
# Close the tooltip when someone clicks anywhere.
|
182
239
|
up.on('click', 'body', (event, $body) ->
|
183
|
-
|
240
|
+
closeAsap()
|
184
241
|
)
|
185
242
|
|
186
243
|
# The framework is reset between tests, so also close
|
187
244
|
# a currently open tooltip.
|
188
|
-
up.on 'up:framework:reset',
|
245
|
+
up.on 'up:framework:reset', reset
|
189
246
|
|
190
247
|
# Close the tooltip when the user presses ESC.
|
191
|
-
up.bus.onEscape(->
|
192
|
-
|
193
|
-
# The framework is reset between tests
|
194
|
-
up.on 'up:framework:reset', reset
|
248
|
+
up.bus.onEscape(-> closeAsap())
|
195
249
|
|
196
|
-
|
197
|
-
|
198
|
-
|
250
|
+
config: config
|
251
|
+
attach: attachAsap
|
252
|
+
isOpen: isOpen
|
253
|
+
close: closeAsap
|
199
254
|
|
200
255
|
)(jQuery)
|