upjs-rails 0.10.5 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/dist/up-bootstrap.js +2 -1
- data/dist/up-bootstrap.min.js +1 -1
- data/dist/up.js +213 -73
- data/dist/up.min.js +2 -2
- data/lib/assets/javascripts/up/browser.js.coffee +33 -5
- data/lib/assets/javascripts/up/flow.js.coffee +14 -17
- data/lib/assets/javascripts/up/history.js.coffee +0 -1
- data/lib/assets/javascripts/up/layout.js.coffee +34 -5
- data/lib/assets/javascripts/up/modal.js.coffee +26 -8
- data/lib/assets/javascripts/up/motion.js.coffee +74 -30
- data/lib/assets/javascripts/up/popup.js.coffee +5 -1
- data/lib/assets/javascripts/up/proxy.js.coffee +2 -0
- data/lib/assets/javascripts/up/util.js.coffee +47 -6
- data/lib/assets/javascripts/up-bootstrap/layout-ext.js.coffee +1 -0
- data/lib/upjs/rails/{current_location.rb → request_echo_headers.rb} +4 -4
- data/lib/upjs/rails/{request.rb → request_ext.rb} +1 -1
- data/lib/upjs/rails/request_method_cookie.rb +28 -0
- data/lib/upjs/rails/version.rb +1 -1
- data/lib/upjs-rails.rb +3 -2
- data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +3 -2
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +0 -3
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +0 -3
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +27 -1
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +68 -22
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +15 -1
- metadata +6 -4
@@ -155,21 +155,49 @@ up.motion = (->
|
|
155
155
|
|
156
156
|
GHOSTING_PROMISE_KEY = 'up-ghosting-promise'
|
157
157
|
|
158
|
-
withGhosts = ($old, $new, block) ->
|
159
|
-
|
160
|
-
|
158
|
+
withGhosts = ($old, $new, options, block) ->
|
159
|
+
|
160
|
+
oldCopy = undefined
|
161
|
+
newCopy = undefined
|
162
|
+
oldScrollTop = undefined
|
163
|
+
newScrollTop = undefined
|
164
|
+
|
165
|
+
$viewport = up.layout.viewportOf($old)
|
161
166
|
|
162
167
|
u.temporaryCss $new, display: 'none', ->
|
163
|
-
|
168
|
+
# Within this block, $new is hidden but $old is visible
|
169
|
+
oldCopy = prependCopy($old, $viewport)
|
164
170
|
oldCopy.$ghost.addClass('up-destroying')
|
165
171
|
oldCopy.$bounds.addClass('up-destroying')
|
172
|
+
# Remember the previous scroll position in case we will reveal $new below.
|
173
|
+
oldScrollTop = $viewport.scrollTop()
|
174
|
+
# $viewport.scrollTop(oldScrollTop + 1)
|
175
|
+
|
166
176
|
u.temporaryCss $old, display: 'none', ->
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
177
|
+
# Within this block, $old is hidden but $new is visible
|
178
|
+
up.reveal($new, viewport: $viewport) if options.reveal
|
179
|
+
newCopy = prependCopy($new, $viewport)
|
180
|
+
newScrollTop = $viewport.scrollTop()
|
181
|
+
|
182
|
+
# Since we have scrolled the viewport (containing both $old and $new),
|
183
|
+
# we must shift the old copy so it looks like it it is still sitting
|
184
|
+
# in the same position.
|
185
|
+
oldCopy.moveTop(newScrollTop - oldScrollTop)
|
186
|
+
|
187
|
+
# Hide $old since we no longer need it.
|
188
|
+
$old.hide()
|
189
|
+
|
190
|
+
# We will let $new take up space in the element flow, but hide it.
|
191
|
+
# The user will only see the two animated ghosts until the transition
|
192
|
+
# is over.
|
193
|
+
showNew = u.temporaryCss($new, visibility: 'hidden')
|
194
|
+
|
172
195
|
promise = block(oldCopy.$ghost, newCopy.$ghost)
|
196
|
+
|
197
|
+
# Make a way to look at $old and $new and see if an animation is
|
198
|
+
# already in progress. If someone attempted a new animation on the
|
199
|
+
# same elements, the stored promises would be resolved by the second
|
200
|
+
# animation call, making the transition jump to the last frame instantly.
|
173
201
|
$old.data(GHOSTING_PROMISE_KEY, promise)
|
174
202
|
$new.data(GHOSTING_PROMISE_KEY, promise)
|
175
203
|
|
@@ -179,9 +207,6 @@ up.motion = (->
|
|
179
207
|
oldCopy.$bounds.remove()
|
180
208
|
newCopy.$bounds.remove()
|
181
209
|
# Now that the transition is over we show $new again.
|
182
|
-
# Since we expect $old to be removed in a heartbeat,
|
183
|
-
# $new should take up space
|
184
|
-
$old.css(display: 'none')
|
185
210
|
showNew()
|
186
211
|
|
187
212
|
promise
|
@@ -207,11 +232,11 @@ up.motion = (->
|
|
207
232
|
u.debug('Canceling existing ghosting on %o', $element)
|
208
233
|
existingGhosting.resolve?()
|
209
234
|
|
210
|
-
assertIsDeferred = (object,
|
235
|
+
assertIsDeferred = (object, source) ->
|
211
236
|
if u.isDeferred(object)
|
212
237
|
object
|
213
238
|
else
|
214
|
-
u.error("Did not return a promise with .then and .resolve methods: %o",
|
239
|
+
u.error("Did not return a promise with .then and .resolve methods: %o", source)
|
215
240
|
|
216
241
|
###*
|
217
242
|
Performs an animated transition between two elements.
|
@@ -252,25 +277,31 @@ up.motion = (->
|
|
252
277
|
The timing function that controls the transition's acceleration.
|
253
278
|
See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
|
254
279
|
for a list of pre-defined timing functions.
|
280
|
+
@param {Boolean} [options.reveal=false]
|
281
|
+
Whether to reveal the new element by scrolling its parent viewport.
|
255
282
|
@return {Promise}
|
256
283
|
A promise for the transition's end.
|
257
284
|
###
|
258
285
|
morph = (source, target, transitionOrName, options) ->
|
259
286
|
if up.browser.canCssAnimation()
|
260
|
-
|
287
|
+
parsedOptions = u.only(options, 'reveal')
|
288
|
+
parsedOptions = u.extend(parsedOptions, animateOptions(options))
|
261
289
|
$old = $(source)
|
262
290
|
$new = $(target)
|
291
|
+
|
263
292
|
finish($old)
|
264
293
|
finish($new)
|
265
|
-
|
266
|
-
|
267
|
-
none()
|
268
|
-
else if transition = u.presence(transitionOrName, u.isFunction) || transitions[transitionOrName]
|
269
|
-
withGhosts $old, $new, ($oldGhost, $newGhost) ->
|
270
|
-
assertIsDeferred(transition($oldGhost, $newGhost, options), transitionOrName)
|
271
|
-
else if animation = animations[transitionOrName]
|
294
|
+
|
295
|
+
if transitionOrName == 'none' || transitionOrName == false || animation = animations[transitionOrName]
|
272
296
|
$old.hide()
|
273
|
-
|
297
|
+
# Since we can not longer rely on withGhosts to process options.reveal
|
298
|
+
# in this branch, we need to do it ourselves.
|
299
|
+
up.reveal($new) if options.reveal
|
300
|
+
animate($new, animation || 'none', options)
|
301
|
+
else if transition = u.presence(transitionOrName, u.isFunction) || transitions[transitionOrName]
|
302
|
+
withGhosts $old, $new, parsedOptions, ($oldGhost, $newGhost) ->
|
303
|
+
transitionPromise = transition($oldGhost, $newGhost, parsedOptions)
|
304
|
+
assertIsDeferred(transitionPromise, transitionOrName)
|
274
305
|
else if u.isString(transitionOrName) && transitionOrName.indexOf('/') >= 0
|
275
306
|
parts = transitionOrName.split('/')
|
276
307
|
transition = ($old, $new, options) ->
|
@@ -278,7 +309,7 @@ up.motion = (->
|
|
278
309
|
animate($old, parts[0], options),
|
279
310
|
animate($new, parts[1], options)
|
280
311
|
)
|
281
|
-
morph($old, $new, transition,
|
312
|
+
morph($old, $new, transition, parsedOptions)
|
282
313
|
else
|
283
314
|
u.error("Unknown transition %o", transitionOrName)
|
284
315
|
else
|
@@ -289,7 +320,7 @@ up.motion = (->
|
|
289
320
|
###*
|
290
321
|
@private
|
291
322
|
###
|
292
|
-
|
323
|
+
prependCopy = ($element, $viewport) ->
|
293
324
|
elementDims = u.measure($element, relative: true, inner: true)
|
294
325
|
|
295
326
|
$ghost = $element.clone()
|
@@ -313,16 +344,29 @@ up.motion = (->
|
|
313
344
|
$bounds.css(position: 'absolute')
|
314
345
|
$bounds.css(elementDims)
|
315
346
|
|
347
|
+
top = elementDims.top
|
348
|
+
|
349
|
+
moveTop = (diff) ->
|
350
|
+
if diff != 0
|
351
|
+
top += diff
|
352
|
+
$bounds.css(top: top)
|
353
|
+
|
316
354
|
$ghost.appendTo($bounds)
|
317
355
|
$bounds.insertBefore($element)
|
318
356
|
|
319
|
-
#
|
320
|
-
#
|
321
|
-
|
322
|
-
$
|
357
|
+
# In theory, $ghost should now sit over $element perfectly.
|
358
|
+
# However, $element might collapse its margin against a previous sibling
|
359
|
+
# element, and $ghost does not have the same sibling.
|
360
|
+
# So we manually correct $ghost's top position so it aligns with $element.
|
361
|
+
moveTop($element.offset().top - $ghost.offset().top)
|
362
|
+
|
363
|
+
$fixedElements = up.layout.fixedChildren($ghost)
|
364
|
+
for fixedElement in $fixedElements
|
365
|
+
u.fixedToAbsolute(fixedElement, $viewport)
|
323
366
|
|
324
367
|
$ghost: $ghost
|
325
368
|
$bounds: $bounds
|
369
|
+
moveTop: moveTop
|
326
370
|
|
327
371
|
###*
|
328
372
|
Defines a named transition.
|
@@ -543,7 +587,7 @@ up.motion = (->
|
|
543
587
|
defaults: config.update
|
544
588
|
none: none
|
545
589
|
when: resolvableWhen
|
546
|
-
|
590
|
+
prependCopy: prependCopy
|
547
591
|
|
548
592
|
)()
|
549
593
|
|
@@ -254,8 +254,12 @@ up.popup = (->
|
|
254
254
|
@ujs
|
255
255
|
###
|
256
256
|
up.on('click', '[up-close]', (event, $element) ->
|
257
|
-
if $element.closest('.up-popup')
|
257
|
+
if $element.closest('.up-popup').length
|
258
258
|
close()
|
259
|
+
# Only prevent the default when we actually closed a popup.
|
260
|
+
# This way we can have buttons that close a popup when within a popup,
|
261
|
+
# but link to a destination if not.
|
262
|
+
event.preventDefault()
|
259
263
|
)
|
260
264
|
|
261
265
|
# The framework is reset between tests
|
@@ -189,6 +189,8 @@ up.proxy = (->
|
|
189
189
|
else
|
190
190
|
promise = load(request)
|
191
191
|
set(request, promise)
|
192
|
+
# Don't cache failed requests
|
193
|
+
promise.fail -> remove(request)
|
192
194
|
|
193
195
|
if pending && !options.preload
|
194
196
|
# This will actually make `pendingCount` higher than the actual
|
@@ -529,24 +529,38 @@ up.util = (->
|
|
529
529
|
if existingAnimation = $(this).data(ANIMATION_PROMISE_KEY)
|
530
530
|
existingAnimation.resolve()
|
531
531
|
|
532
|
-
measure = ($element,
|
533
|
-
|
534
|
-
|
532
|
+
measure = ($element, opts) ->
|
533
|
+
opts = options(opts, relative: false, inner: false, full: false)
|
534
|
+
|
535
|
+
if opts.relative
|
536
|
+
if opts.relative == true
|
537
|
+
coordinates = $element.position()
|
538
|
+
else
|
539
|
+
$context = $(opts.relative)
|
540
|
+
elementCoords = $element.offset()
|
541
|
+
if $context.is(document)
|
542
|
+
# The document is always at the origin
|
543
|
+
coordinates = elementCoords
|
544
|
+
else
|
545
|
+
contextCoords = $context.offset()
|
546
|
+
coordinates =
|
547
|
+
left: elementCoords.left - contextCoords.left
|
548
|
+
top: elementCoords.top - contextCoords.top
|
535
549
|
else
|
536
|
-
$element.offset()
|
550
|
+
coordinates = $element.offset()
|
537
551
|
|
538
552
|
box =
|
539
553
|
left: coordinates.left
|
540
554
|
top: coordinates.top
|
541
555
|
|
542
|
-
if
|
556
|
+
if opts.inner
|
543
557
|
box.width = $element.width()
|
544
558
|
box.height = $element.height()
|
545
559
|
else
|
546
560
|
box.width = $element.outerWidth()
|
547
561
|
box.height = $element.outerHeight()
|
548
562
|
|
549
|
-
if
|
563
|
+
if opts.full
|
550
564
|
viewport = clientSize()
|
551
565
|
box.right = viewport.width - (box.left + box.width)
|
552
566
|
box.bottom = viewport.height - (box.top + box.height)
|
@@ -838,6 +852,33 @@ up.util = (->
|
|
838
852
|
parent.insertBefore(wrappedNode, wrapper)
|
839
853
|
parent.removeChild(wrapper)
|
840
854
|
|
855
|
+
offsetParent = ($element) ->
|
856
|
+
$match = undefined
|
857
|
+
while ($element = $element.parent()) && $element.length
|
858
|
+
position = $element.css('position')
|
859
|
+
console.log("Iteration element is %o with position %o", $element, position)
|
860
|
+
if position == 'absolute' || position == 'relative' || $element.is('body')
|
861
|
+
$match = $element
|
862
|
+
break
|
863
|
+
$match
|
864
|
+
|
865
|
+
fixedToAbsolute = (element, $viewport) ->
|
866
|
+
$element = $(element)
|
867
|
+
$futureOffsetParent = offsetParent($element)
|
868
|
+
# To get a fixed elements distance from the edge of the screen,
|
869
|
+
# use position(), not offset(). offset() would include the current
|
870
|
+
# scrollTop of the viewport.
|
871
|
+
elementCoords = $element.position()
|
872
|
+
futureParentCoords = $futureOffsetParent.offset()
|
873
|
+
$element.css
|
874
|
+
position: 'absolute'
|
875
|
+
left: elementCoords.left - futureParentCoords.left
|
876
|
+
top: elementCoords.top - futureParentCoords.top + $viewport.scrollTop()
|
877
|
+
right: ''
|
878
|
+
bottom: ''
|
879
|
+
|
880
|
+
offsetParent: offsetParent
|
881
|
+
fixedToAbsolute: fixedToAbsolute
|
841
882
|
presentAttr: presentAttr
|
842
883
|
createElement: createElement
|
843
884
|
normalizeUrl: normalizeUrl
|
@@ -3,3 +3,4 @@ defaults = up.layout.defaults()
|
|
3
3
|
up.layout.defaults
|
4
4
|
fixedTop: defaults.fixedTop.concat(['.navbar-fixed-top'])
|
5
5
|
fixedBottom: defaults.fixedBottom.concat(['.navbar-fixed-bottom'])
|
6
|
+
anchoredRight: defaults.anchoredRight.concat(['.navbar-fixed-top', '.navbar-fixed-bottom', '.footer'])
|
@@ -1,19 +1,19 @@
|
|
1
1
|
module Upjs
|
2
2
|
module Rails
|
3
|
-
module
|
3
|
+
module RequestEchoHeaders
|
4
4
|
|
5
5
|
def self.included(base)
|
6
|
-
base.before_filter :
|
6
|
+
base.before_filter :set_up_request_echo_headers
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
def
|
11
|
+
def set_up_request_echo_headers
|
12
12
|
headers['X-Up-Location'] = request.original_url
|
13
13
|
headers['X-Up-Method'] = request.method
|
14
14
|
end
|
15
15
|
|
16
|
-
ActionController::Base.include
|
16
|
+
ActionController::Base.send(:include, self)
|
17
17
|
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# See
|
2
|
+
# https://github.com/rails/turbolinks/search?q=request_method&ref=cmdform
|
3
|
+
# https://github.com/rails/turbolinks/blob/83d4b3d2c52a681f07900c28adb28bc8da604733/README.md#initialization
|
4
|
+
module Upjs
|
5
|
+
module Rails
|
6
|
+
module RequestMethod
|
7
|
+
|
8
|
+
COOKIE_NAME = '_up_request_method'
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.before_filter :set_up_request_method_cookie
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def set_up_request_method_cookie
|
17
|
+
if request.get?
|
18
|
+
cookies.delete(COOKIE_NAME)
|
19
|
+
else
|
20
|
+
cookies[COOKIE_NAME] = request.request_method
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActionController::Base.send(:include, self)
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/upjs/rails/version.rb
CHANGED
data/lib/upjs-rails.rb
CHANGED
@@ -124,11 +124,12 @@ describe 'up.flow', ->
|
|
124
124
|
@revealedHTML = $revealedElement.get(0).outerHTML
|
125
125
|
u.resolvedPromise()
|
126
126
|
|
127
|
-
it 'reveals
|
127
|
+
it 'reveals a new element before it is being replaced', (done) ->
|
128
128
|
@request = up.replace('.middle', '/path', reveal: true)
|
129
129
|
@respond()
|
130
130
|
@request.then =>
|
131
|
-
expect(up.reveal).toHaveBeenCalledWith(@oldMiddle)
|
131
|
+
expect(up.reveal).not.toHaveBeenCalledWith(@oldMiddle)
|
132
|
+
expect(@revealedHTML).toContain('new-middle')
|
132
133
|
done()
|
133
134
|
|
134
135
|
it 'reveals a new element that is being appended', (done) ->
|
@@ -16,11 +16,8 @@ describe 'up.history', ->
|
|
16
16
|
|
17
17
|
it 'sets an [up-href] attribute to the previous URL and sets the up-restore-scroll attribute to "true"', ->
|
18
18
|
up.history.push('/one')
|
19
|
-
console.log("url is now %o, previous was %o", up.history.url(), up.history.previousUrl())
|
20
19
|
up.history.push('/two')
|
21
|
-
console.log("url is now %o, previous was %o", up.history.url(), up.history.previousUrl())
|
22
20
|
$element = up.ready(affix('a[href="/three"][up-back]').text('text'))
|
23
|
-
console.log("url is now %o, previous was %o", up.history.url(), up.history.previousUrl())
|
24
21
|
expect($element.attr('href')).toEndWith('/three')
|
25
22
|
expect($element.attr('up-href')).toEndWith('/one')
|
26
23
|
expect($element.attr('up-restore-scroll')).toBe('')
|
@@ -17,8 +17,6 @@ describe 'up.layout', ->
|
|
17
17
|
beforeEach ->
|
18
18
|
$body = $('body')
|
19
19
|
|
20
|
-
@restoreMargin = u.temporaryCss($body, 'margin-top': 0)
|
21
|
-
|
22
20
|
@$elements = []
|
23
21
|
@$container = $('<div class="container">').prependTo($body)
|
24
22
|
|
@@ -31,7 +29,6 @@ describe 'up.layout', ->
|
|
31
29
|
|
32
30
|
afterEach ->
|
33
31
|
@$container.remove()
|
34
|
-
@restoreMargin()
|
35
32
|
|
36
33
|
it 'reveals the given element', ->
|
37
34
|
up.reveal(@$elements[0])
|
@@ -8,6 +8,8 @@ describe 'up.modal', ->
|
|
8
8
|
|
9
9
|
describe 'up.modal.open', ->
|
10
10
|
|
11
|
+
assumedScrollbarWidth = 15
|
12
|
+
|
11
13
|
it "loads the given link's destination in a dialog window", (done) ->
|
12
14
|
$link = affix('a[href="/path/to"][up-modal=".middle"]').text('link')
|
13
15
|
promise = up.modal.open($link)
|
@@ -49,7 +51,7 @@ describe 'up.modal', ->
|
|
49
51
|
expect($modal).toExist()
|
50
52
|
expect($modal.css('overflow-y')).toEqual('scroll')
|
51
53
|
expect($body.css('overflow-y')).toEqual('hidden')
|
52
|
-
expect(parseInt($body.css('padding-right'))).toBeAround(
|
54
|
+
expect(parseInt($body.css('padding-right'))).toBeAround(assumedScrollbarWidth, 10)
|
53
55
|
|
54
56
|
up.modal.close().then ->
|
55
57
|
expect($body.css('overflow-y')).toEqual('scroll')
|
@@ -57,6 +59,30 @@ describe 'up.modal', ->
|
|
57
59
|
|
58
60
|
done()
|
59
61
|
|
62
|
+
it 'pushes right-anchored elements away from the edge of the screen in order to prevent jumping', (done) ->
|
63
|
+
|
64
|
+
$anchoredElement = affix('div[up-anchored=right]').css
|
65
|
+
position: 'absolute'
|
66
|
+
top: '0'
|
67
|
+
right: '30px'
|
68
|
+
|
69
|
+
promise = up.modal.open(url: '/foo', target: '.container')
|
70
|
+
|
71
|
+
@lastRequest().respondWith
|
72
|
+
status: 200
|
73
|
+
contentType: 'text/html'
|
74
|
+
responseText:
|
75
|
+
"""
|
76
|
+
<div class="container">text</div>
|
77
|
+
"""
|
78
|
+
|
79
|
+
promise.then ->
|
80
|
+
expect(parseInt($anchoredElement.css('right'))).toBeAround(30 + assumedScrollbarWidth, 10)
|
81
|
+
|
82
|
+
up.modal.close().then ->
|
83
|
+
expect(parseInt($anchoredElement.css('right'))).toBeAround(30 , 10)
|
84
|
+
done()
|
85
|
+
|
60
86
|
describe 'up.modal.close', ->
|
61
87
|
|
62
88
|
it 'should have tests'
|
@@ -37,7 +37,7 @@ describe 'up.motion', ->
|
|
37
37
|
|
38
38
|
if up.browser.canCssAnimation()
|
39
39
|
|
40
|
-
it 'transitions between two element
|
40
|
+
it 'transitions between two element by animating two copies while keeping the originals in the background', (done) ->
|
41
41
|
|
42
42
|
$old = affix('.old').text('old content').css(
|
43
43
|
position: 'absolute'
|
@@ -53,7 +53,7 @@ describe 'up.motion', ->
|
|
53
53
|
width: '22px',
|
54
54
|
height: '23px'
|
55
55
|
)
|
56
|
-
up.morph($old, $new, 'cross-fade', duration:
|
56
|
+
up.morph($old, $new, 'cross-fade', duration: 200, easing: 'linear')
|
57
57
|
|
58
58
|
# The actual animation will be performed on Ghosts since
|
59
59
|
# two element usually cannot exist in the DOM at the same time
|
@@ -76,14 +76,12 @@ describe 'up.motion', ->
|
|
76
76
|
|
77
77
|
# The actual elements are hidden, but $old will take up its original
|
78
78
|
# space until the animation completes.
|
79
|
-
expect($old.css(
|
79
|
+
expect($old.css('display')).toEqual('none')
|
80
|
+
|
81
|
+
expect($new.css(['display', 'visibility'])).toEqual(
|
80
82
|
display: 'block',
|
81
83
|
visibility: 'hidden'
|
82
84
|
)
|
83
|
-
expect($new.css(['display', 'visibility'])).toEqual(
|
84
|
-
display: 'none',
|
85
|
-
visibility: 'visible'
|
86
|
-
)
|
87
85
|
|
88
86
|
# Ghosts will hover over $old and $new using absolute positioning,
|
89
87
|
# matching the coordinates of the original elements.
|
@@ -108,15 +106,15 @@ describe 'up.motion', ->
|
|
108
106
|
expect(opacity($newGhost)).toBeAround(0.0, 0.25)
|
109
107
|
expect(opacity($oldGhost)).toBeAround(1.0, 0.25)
|
110
108
|
|
111
|
-
@setTimer
|
109
|
+
@setTimer 80, ->
|
112
110
|
expect(opacity($newGhost)).toBeAround(0.4, 0.25)
|
113
111
|
expect(opacity($oldGhost)).toBeAround(0.6, 0.25)
|
114
112
|
|
115
|
-
@setTimer
|
113
|
+
@setTimer 140, ->
|
116
114
|
expect(opacity($newGhost)).toBeAround(0.7, 0.25)
|
117
115
|
expect(opacity($oldGhost)).toBeAround(0.3, 0.25)
|
118
116
|
|
119
|
-
@setTimer
|
117
|
+
@setTimer 250, ->
|
120
118
|
# Once our two ghosts have rendered their visual effect,
|
121
119
|
# we remove them from the DOM.
|
122
120
|
expect($newGhost).not.toBeInDOM()
|
@@ -124,10 +122,7 @@ describe 'up.motion', ->
|
|
124
122
|
|
125
123
|
# The old element is still in the DOM, but hidden.
|
126
124
|
# Morphing does *not* remove the target element.
|
127
|
-
expect($old.css(
|
128
|
-
display: 'none',
|
129
|
-
visibility: 'hidden'
|
130
|
-
)
|
125
|
+
expect($old.css('display')).toEqual('none')
|
131
126
|
expect($new.css(['display', 'visibility'])).toEqual(
|
132
127
|
display: 'block',
|
133
128
|
visibility: 'visible'
|
@@ -150,6 +145,39 @@ describe 'up.motion', ->
|
|
150
145
|
# Check that it's a different ghosts
|
151
146
|
expect($ghost2).not.toEqual($ghost1)
|
152
147
|
|
148
|
+
describe 'with { reveal: true } option', ->
|
149
|
+
|
150
|
+
it 'reveals the new element while making the old element within the same viewport appear as if it would keep its scroll position', ->
|
151
|
+
$container = affix('.container[up-viewport]').css
|
152
|
+
'width': '200px'
|
153
|
+
'height': '200px'
|
154
|
+
'overflow-y': 'scroll'
|
155
|
+
'position': 'fixed'
|
156
|
+
'left': 0,
|
157
|
+
'top': 0
|
158
|
+
$old = affix('.old').appendTo($container).css(height: '600px')
|
159
|
+
$container.scrollTop(300)
|
160
|
+
|
161
|
+
$new = affix('.new').insertBefore($old).css(height: '600px')
|
162
|
+
|
163
|
+
up.morph($old, $new, 'cross-fade', duration: 50, reveal: true)
|
164
|
+
|
165
|
+
$oldGhost = $('.old.up-ghost')
|
166
|
+
$newGhost = $('.new.up-ghost')
|
167
|
+
|
168
|
+
# Container is scrolled up due to { reveal: true } option.
|
169
|
+
# Since $old and $new are sitting in the same viewport with a
|
170
|
+
# single shares scrollbar This will make the ghost for $old jump.
|
171
|
+
expect($container.scrollTop()).toEqual(0)
|
172
|
+
|
173
|
+
# See that the ghost for $new is aligned with the top edge
|
174
|
+
# of the viewport.
|
175
|
+
expect($newGhost.offset().top).toEqual(0)
|
176
|
+
|
177
|
+
# The ghost for $old is shifted upwards to make it looks like it
|
178
|
+
# was at the scroll position before we revealed $new.
|
179
|
+
expect($oldGhost.offset().top).toEqual(-300)
|
180
|
+
|
153
181
|
else
|
154
182
|
|
155
183
|
it "doesn't animate and hides the first element instead", ->
|
@@ -171,14 +199,14 @@ describe 'up.motion', ->
|
|
171
199
|
|
172
200
|
it 'should have tests'
|
173
201
|
|
174
|
-
describe 'up.motion.
|
202
|
+
describe 'up.motion.prependCopy', ->
|
175
203
|
|
176
204
|
afterEach ->
|
177
205
|
$('.up-bounds, .up-ghost, .fixture').remove()
|
178
206
|
|
179
207
|
it 'clones the given element into a .up-ghost-bounds container and inserts it as a sibling before the element', ->
|
180
208
|
$element = affix('.element').text('element text')
|
181
|
-
up.motion.
|
209
|
+
up.motion.prependCopy($element)
|
182
210
|
$bounds = $element.prev()
|
183
211
|
expect($bounds).toExist()
|
184
212
|
expect($bounds).toHaveClass('up-bounds')
|
@@ -190,13 +218,13 @@ describe 'up.motion', ->
|
|
190
218
|
it 'removes <script> tags from the cloned element', ->
|
191
219
|
$element = affix('.element')
|
192
220
|
$('<script></script>').appendTo($element)
|
193
|
-
up.motion.
|
221
|
+
up.motion.prependCopy($element)
|
194
222
|
$ghost = $('.up-ghost')
|
195
223
|
expect($ghost.find('script')).not.toExist()
|
196
224
|
|
197
225
|
it 'absolutely positions the ghost over the given element', ->
|
198
226
|
$element = affix('.element')
|
199
|
-
up.motion.
|
227
|
+
up.motion.prependCopy($element)
|
200
228
|
$ghost = $('.up-ghost')
|
201
229
|
expect($ghost.offset()).toEqual($element.offset())
|
202
230
|
expect($ghost.width()).toEqual($element.width())
|
@@ -204,25 +232,43 @@ describe 'up.motion', ->
|
|
204
232
|
|
205
233
|
it 'accurately positions the ghost over an element with margins', ->
|
206
234
|
$element = affix('.element').css(margin: '40px')
|
207
|
-
up.motion.
|
235
|
+
up.motion.prependCopy($element)
|
208
236
|
$ghost = $('.up-ghost')
|
209
237
|
expect($ghost.offset()).toEqual($element.offset())
|
210
238
|
|
211
239
|
it "doesn't change the position of a child whose margins no longer collapse", ->
|
212
240
|
$element = affix('.element')
|
213
241
|
$child = $('<div class="child"></div>').css(margin: '40px').appendTo($element)
|
214
|
-
up.motion.
|
242
|
+
up.motion.prependCopy($element)
|
215
243
|
$clonedChild = $('.up-ghost .child')
|
216
244
|
expect($clonedChild.offset()).toEqual($child.offset())
|
217
245
|
|
218
246
|
it 'correctly positions the ghost over an element within a scrolled body', ->
|
219
|
-
$body = $('body')
|
247
|
+
$body = $('body')
|
220
248
|
$element1 = $('<div class="fixture"></div>').css(height: '75px').prependTo($body)
|
221
249
|
$element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
|
222
250
|
$body.scrollTop(17)
|
223
|
-
{ $bounds, $ghost } = up.motion.
|
251
|
+
{ $bounds, $ghost } = up.motion.prependCopy($element2)
|
224
252
|
expect($bounds.css('position')).toBe('absolute')
|
225
253
|
expect($bounds.css('top')).toEqual('75px')
|
226
254
|
expect($ghost.css('position')).toBe('static')
|
227
255
|
|
228
256
|
it 'correctly positions the ghost over an element within a viewport with overflow-y: scroll'
|
257
|
+
|
258
|
+
it 'converts fixed elements within the copies to absolutely positioning', ->
|
259
|
+
$element = affix('.element').css
|
260
|
+
position: 'absolute'
|
261
|
+
top: '50px'
|
262
|
+
left: '50px'
|
263
|
+
$fixedChild = $('<div class="fixed-child" up-fixed></div>').css
|
264
|
+
position: 'fixed'
|
265
|
+
left: '77px'
|
266
|
+
top: '77px'
|
267
|
+
$fixedChild.appendTo($element)
|
268
|
+
up.motion.prependCopy($element, $('body'))
|
269
|
+
$fixedChildGhost = $('.up-ghost .fixed-child')
|
270
|
+
expect($fixedChildGhost.css(['position', 'left', 'top'])).toEqual
|
271
|
+
position: 'absolute',
|
272
|
+
left: '27px',
|
273
|
+
top: '27px'
|
274
|
+
|