upjs-rails 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/bin/doc-server +3 -0
- data/bin/install-ydoc +4 -0
- data/design/animation-ghosting.txt +72 -0
- data/design/design.txt +34 -0
- data/design/draft.html.erb +48 -0
- data/design/draft.rb +9 -0
- data/lib/assets/javascripts/up/browser.js.coffee +20 -0
- data/lib/assets/javascripts/up/bus.js.coffee +54 -0
- data/lib/assets/javascripts/up/flow.js.coffee +160 -0
- data/lib/assets/javascripts/up/form.js.coffee +162 -0
- data/lib/assets/javascripts/up/history.js.coffee +33 -0
- data/lib/assets/javascripts/up/index.js +13 -0
- data/lib/assets/javascripts/up/link.js.coffee +95 -0
- data/lib/assets/javascripts/up/magic.js.coffee +79 -0
- data/lib/assets/javascripts/up/modal.js.coffee +51 -0
- data/lib/assets/javascripts/up/module.js.coffee +4 -0
- data/lib/assets/javascripts/up/motion.js.coffee +276 -0
- data/lib/assets/javascripts/up/navigation.js.coffee +63 -0
- data/lib/assets/javascripts/up/popup.js.coffee +143 -0
- data/lib/assets/javascripts/up/util.js.coffee +287 -0
- data/lib/assets/stylesheets/up/follow.css.sass +2 -0
- data/lib/assets/stylesheets/up/index.css +3 -0
- data/lib/assets/stylesheets/up/modal.css.sass +54 -0
- data/lib/assets/stylesheets/up/popup.css.sass +9 -0
- data/lib/upjs/rails/engine.rb +6 -0
- data/lib/upjs/rails/redirection.rb +26 -0
- data/lib/upjs/rails/request.rb +13 -0
- data/lib/upjs/rails/version.rb +5 -0
- data/lib/upjs-rails.rb +7 -0
- data/spec_app/.firefox-version +1 -0
- data/spec_app/.gitignore +17 -0
- data/spec_app/Gemfile +24 -0
- data/spec_app/Gemfile.lock +239 -0
- data/spec_app/README.rdoc +28 -0
- data/spec_app/Rakefile +6 -0
- data/spec_app/app/assets/images/.keep +0 -0
- data/spec_app/app/assets/javascripts/application.js +16 -0
- data/spec_app/app/assets/stylesheets/application.css +15 -0
- data/spec_app/app/assets/stylesheets/blocks/card.css.sass +11 -0
- data/spec_app/app/assets/stylesheets/blocks/controls.css.sass +7 -0
- data/spec_app/app/assets/stylesheets/blocks/field_with_errors.css.sass +5 -0
- data/spec_app/app/assets/stylesheets/blocks/menu.css.sass +13 -0
- data/spec_app/app/assets/stylesheets/blocks/panel.css.sass +8 -0
- data/spec_app/app/controllers/application_controller.rb +14 -0
- data/spec_app/app/controllers/cards_controller.rb +51 -0
- data/spec_app/app/controllers/concerns/.keep +0 -0
- data/spec_app/app/controllers/pages_controller.rb +6 -0
- data/spec_app/app/helpers/application_helper.rb +2 -0
- data/spec_app/app/mailers/.keep +0 -0
- data/spec_app/app/models/card.rb +6 -0
- data/spec_app/app/models/concerns/.keep +0 -0
- data/spec_app/app/models/tests.rb +27 -0
- data/spec_app/app/views/cards/_side.html.haml +4 -0
- data/spec_app/app/views/cards/index.html.haml +10 -0
- data/spec_app/app/views/cards/new.html.haml +15 -0
- data/spec_app/app/views/cards/show.html.haml +11 -0
- data/spec_app/app/views/layouts/application.html.erb +12 -0
- data/spec_app/app/views/pages/home.html.haml +5 -0
- data/spec_app/bin/bundle +3 -0
- data/spec_app/bin/rails +8 -0
- data/spec_app/bin/rake +8 -0
- data/spec_app/bin/setup +29 -0
- data/spec_app/bin/spring +18 -0
- data/spec_app/config/application.rb +26 -0
- data/spec_app/config/boot.rb +3 -0
- data/spec_app/config/cucumber.yml +8 -0
- data/spec_app/config/database.yml +25 -0
- data/spec_app/config/environment.rb +5 -0
- data/spec_app/config/environments/development.rb +41 -0
- data/spec_app/config/environments/production.rb +79 -0
- data/spec_app/config/environments/test.rb +42 -0
- data/spec_app/config/initializers/assets.rb +11 -0
- data/spec_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec_app/config/initializers/inflections.rb +16 -0
- data/spec_app/config/initializers/mime_types.rb +4 -0
- data/spec_app/config/initializers/session_store.rb +3 -0
- data/spec_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec_app/config/locales/en.yml +23 -0
- data/spec_app/config/routes.rb +6 -0
- data/spec_app/config/secrets.yml +22 -0
- data/spec_app/config.ru +4 -0
- data/spec_app/db/migrate/20141225125143_create_card.rb +9 -0
- data/spec_app/db/schema.rb +23 -0
- data/spec_app/db/seeds.rb +7 -0
- data/spec_app/features/history.feature +30 -0
- data/spec_app/features/navigation.feature +9 -0
- data/spec_app/features/step_definitions/factory_steps.rb +5 -0
- data/spec_app/features/step_definitions/navigation_steps.rb +29 -0
- data/spec_app/features/step_definitions/utility_steps.rb +15 -0
- data/spec_app/features/support/env.rb +73 -0
- data/spec_app/features/support/find_by_anything.rb +19 -0
- data/spec_app/features/support/paths.rb +51 -0
- data/spec_app/lib/assets/.keep +0 -0
- data/spec_app/lib/tasks/.keep +0 -0
- data/spec_app/lib/tasks/cucumber.rake +65 -0
- data/spec_app/log/.keep +0 -0
- data/spec_app/public/404.html +67 -0
- data/spec_app/public/422.html +67 -0
- data/spec_app/public/500.html +66 -0
- data/spec_app/public/favicon.ico +0 -0
- data/spec_app/public/robots.txt +5 -0
- data/spec_app/script/cucumber +10 -0
- data/spec_app/test/controllers/.keep +0 -0
- data/spec_app/test/fixtures/.keep +0 -0
- data/spec_app/test/helpers/.keep +0 -0
- data/spec_app/test/integration/.keep +0 -0
- data/spec_app/test/mailers/.keep +0 -0
- data/spec_app/test/models/.keep +0 -0
- data/spec_app/test/test_helper.rb +10 -0
- data/spec_app/vendor/assets/javascripts/.keep +0 -0
- data/spec_app/vendor/assets/stylesheets/.keep +0 -0
- data/upjs-rails.gemspec +24 -0
- metadata +207 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
up.history = (->
|
|
2
|
+
|
|
3
|
+
replace = (url) ->
|
|
4
|
+
manipulate "replace", url
|
|
5
|
+
|
|
6
|
+
push = (url) ->
|
|
7
|
+
manipulate "push", url
|
|
8
|
+
|
|
9
|
+
manipulate = (method, url) ->
|
|
10
|
+
method += "State" # resulting in either pushState or replaceState
|
|
11
|
+
window.history[method]({ fromUp: true }, '', url)
|
|
12
|
+
|
|
13
|
+
pop = (event) ->
|
|
14
|
+
state = event.originalEvent.state
|
|
15
|
+
console.log "popping state", state
|
|
16
|
+
console.log "current href", currentUrl()
|
|
17
|
+
if state?.fromUp
|
|
18
|
+
up.visit currentUrl(), history: { method: 'replace' }
|
|
19
|
+
else
|
|
20
|
+
console.log "null state"
|
|
21
|
+
|
|
22
|
+
currentUrl = ->
|
|
23
|
+
location.href
|
|
24
|
+
|
|
25
|
+
setTimeout (->
|
|
26
|
+
$(window).on "popstate", pop
|
|
27
|
+
replace(currentUrl())
|
|
28
|
+
), 200
|
|
29
|
+
|
|
30
|
+
push: push
|
|
31
|
+
replace: replace
|
|
32
|
+
|
|
33
|
+
)()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//= require up/module
|
|
2
|
+
//= require up/util
|
|
3
|
+
//= require up/browser
|
|
4
|
+
//= require up/bus
|
|
5
|
+
//= require up/flow
|
|
6
|
+
//= require up/magic
|
|
7
|
+
//= require up/history
|
|
8
|
+
//= require up/motion
|
|
9
|
+
//= require up/link
|
|
10
|
+
//= require up/form
|
|
11
|
+
//= require up/popup
|
|
12
|
+
//= require up/modal
|
|
13
|
+
//= require up/navigation
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
###*
|
|
2
|
+
Links.
|
|
3
|
+
|
|
4
|
+
@class up.link
|
|
5
|
+
###
|
|
6
|
+
|
|
7
|
+
up.link = (->
|
|
8
|
+
|
|
9
|
+
###*
|
|
10
|
+
Visits the given URL without a full page load.
|
|
11
|
+
This is done by fetching `url` through an AJAX request
|
|
12
|
+
and replaceing the current `<body>` tag with the response's `<body>` tag..
|
|
13
|
+
|
|
14
|
+
@method up.visit
|
|
15
|
+
@param {String} url
|
|
16
|
+
The URL to visit.
|
|
17
|
+
@param {Object} options
|
|
18
|
+
See options for {{#crossLink "up.flow/up.replace"}}{{/crossLink}}.
|
|
19
|
+
@example
|
|
20
|
+
<a href="/users" up-follow>User list</a>
|
|
21
|
+
###
|
|
22
|
+
visit = (url, options) ->
|
|
23
|
+
console.log("up.visit", url)
|
|
24
|
+
# options = up.util.options(options, )
|
|
25
|
+
up.replace('body', url, options)
|
|
26
|
+
|
|
27
|
+
###*
|
|
28
|
+
Follows the given link and replaces a selector in the current page
|
|
29
|
+
with corresponding elements from a new page fetched from the server.
|
|
30
|
+
|
|
31
|
+
@method up.follow
|
|
32
|
+
@param {Element|jQuery|String} link
|
|
33
|
+
An element or selector which resolves to an `<a>` tag
|
|
34
|
+
or any element that is marked up with an `up-follow` attribute.
|
|
35
|
+
@param {String} [options.target]
|
|
36
|
+
The selector to replace.
|
|
37
|
+
Defaults to the `up-target` attribute on `link`,
|
|
38
|
+
or to `body` if such an attribute does not exist.
|
|
39
|
+
@param {Function|String} [options.transition]
|
|
40
|
+
A transition function or name.
|
|
41
|
+
@example
|
|
42
|
+
<a href="/users" up-target=".main">User list</a>
|
|
43
|
+
###
|
|
44
|
+
follow = (link, options) ->
|
|
45
|
+
$link = $(link)
|
|
46
|
+
options = up.util.options(options)
|
|
47
|
+
url = up.util.presentAttr($link, 'href', 'up-follow')
|
|
48
|
+
selector = options.target || $link.attr("up-target") || 'body'
|
|
49
|
+
options.transition ||= $link.attr('up-transition')
|
|
50
|
+
up.replace(selector, url, options)
|
|
51
|
+
|
|
52
|
+
resolve = (element) ->
|
|
53
|
+
$element = $(element)
|
|
54
|
+
if $element.is('a') || up.util.presentAttr($element, 'up-follow')
|
|
55
|
+
$element
|
|
56
|
+
else
|
|
57
|
+
$element.find('a:first')
|
|
58
|
+
|
|
59
|
+
resolveUrl = (element) ->
|
|
60
|
+
if link = resolve(element)
|
|
61
|
+
up.util.presentAttr(link, 'href', 'up-follow')
|
|
62
|
+
|
|
63
|
+
markActive = (element) ->
|
|
64
|
+
markUnactive()
|
|
65
|
+
$element = $(element)
|
|
66
|
+
$clickArea = $element.ancestors('up-follow')
|
|
67
|
+
$clickArea = $element unless $clickArea.length
|
|
68
|
+
$clickArea.addClass('up-active')
|
|
69
|
+
|
|
70
|
+
markUnactive = ->
|
|
71
|
+
$('[up-active]').removeClass('up-active')
|
|
72
|
+
|
|
73
|
+
up.on 'click', 'a[up-target]', (event, $link) ->
|
|
74
|
+
event.preventDefault()
|
|
75
|
+
follow($link)
|
|
76
|
+
|
|
77
|
+
up.on 'click', '[up-follow]', (event, $element) ->
|
|
78
|
+
|
|
79
|
+
childLinkClicked = ->
|
|
80
|
+
$target = $(event.target)
|
|
81
|
+
$target.closest('a').length && $element.has($target).length
|
|
82
|
+
|
|
83
|
+
unless childLinkClicked()
|
|
84
|
+
event.preventDefault()
|
|
85
|
+
follow(resolve($element))
|
|
86
|
+
|
|
87
|
+
visit: visit
|
|
88
|
+
follow: follow
|
|
89
|
+
resolve: resolve
|
|
90
|
+
resolveUrl: resolveUrl
|
|
91
|
+
|
|
92
|
+
)()
|
|
93
|
+
|
|
94
|
+
up.visit = up.link.visit
|
|
95
|
+
up.follow = up.link.follow
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
###*
|
|
2
|
+
Event handling.
|
|
3
|
+
|
|
4
|
+
@class up.magic
|
|
5
|
+
###
|
|
6
|
+
up.magic = (->
|
|
7
|
+
|
|
8
|
+
AWAKENED_CLASS = 'up-awakened'
|
|
9
|
+
DESTROYER_KEY = 'up-destroyer'
|
|
10
|
+
|
|
11
|
+
###*
|
|
12
|
+
Binds an event handler to the document,
|
|
13
|
+
which will be executed whenever the given event
|
|
14
|
+
is triggered on the given selector.
|
|
15
|
+
|
|
16
|
+
@method up.on
|
|
17
|
+
@param {String} events
|
|
18
|
+
A space-separated list of event names to bind.
|
|
19
|
+
@param {String} selector
|
|
20
|
+
The selector an on which the event must be triggered.
|
|
21
|
+
@param {Function} behavior
|
|
22
|
+
The handler that should be called.
|
|
23
|
+
###
|
|
24
|
+
live = (events, selector, behavior) ->
|
|
25
|
+
$(document).on events, selector, (event) ->
|
|
26
|
+
behavior.apply(this, [event, $(this)])
|
|
27
|
+
|
|
28
|
+
awakeners = []
|
|
29
|
+
|
|
30
|
+
###*
|
|
31
|
+
Registers a function to be called whenever an element with
|
|
32
|
+
the given selector is inserted into the DOM through Up.js.
|
|
33
|
+
|
|
34
|
+
@method up.awaken
|
|
35
|
+
@param {String} selector
|
|
36
|
+
The selector to match.
|
|
37
|
+
@param {Function} awakener
|
|
38
|
+
The function to call when a matching element is inserted.
|
|
39
|
+
The function takes the new element as the first argument (as a jQuery object).
|
|
40
|
+
It may return another function that destroys the awakened
|
|
41
|
+
object when it is removed from the DOM, by clearing global state such as
|
|
42
|
+
time-outs and event handlers bound to the document.
|
|
43
|
+
###
|
|
44
|
+
awaken = (selector, awakener) ->
|
|
45
|
+
awakeners.push
|
|
46
|
+
selector: selector
|
|
47
|
+
behavior: awakener
|
|
48
|
+
|
|
49
|
+
$deepFilter = ($element, selector) ->
|
|
50
|
+
$element.find(selector).addBack(selector)
|
|
51
|
+
|
|
52
|
+
compile = ($fragment) ->
|
|
53
|
+
for awakener in awakeners
|
|
54
|
+
$deepFilter($fragment, awakener.selector).each ->
|
|
55
|
+
$element = $(this)
|
|
56
|
+
destroyer = awakener.behavior.apply(this, [$element])
|
|
57
|
+
if up.util.isFunction(destroyer)
|
|
58
|
+
$element.prop(AWAKENED_CLASS, true)
|
|
59
|
+
$element.data(DESTROYER_KEY, destroyer)
|
|
60
|
+
|
|
61
|
+
destroy = ($fragment) ->
|
|
62
|
+
$deepFilter($fragment, "[#{AWAKENED_CLASS}]").each ->
|
|
63
|
+
$element = $(this)
|
|
64
|
+
destroyer = $element.data(DESTROYER_KEY)
|
|
65
|
+
destroyer()
|
|
66
|
+
|
|
67
|
+
up.bus.on 'app:ready', (-> up.bus.emit 'fragment:ready', $(document.body))
|
|
68
|
+
up.bus.on 'fragment:ready', compile
|
|
69
|
+
up.bus.on 'fragment:destroy', destroy
|
|
70
|
+
$(document).on 'ready', -> up.bus.emit('app:ready')
|
|
71
|
+
|
|
72
|
+
awaken: awaken
|
|
73
|
+
on: live
|
|
74
|
+
|
|
75
|
+
)()
|
|
76
|
+
|
|
77
|
+
up.awaken = up.magic.awaken
|
|
78
|
+
up.on = up.magic.on
|
|
79
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
###*
|
|
2
|
+
Modal dialogs (WIP).
|
|
3
|
+
|
|
4
|
+
@class up.modal
|
|
5
|
+
###
|
|
6
|
+
up.modal = (->
|
|
7
|
+
|
|
8
|
+
# configuration =
|
|
9
|
+
# width: 600
|
|
10
|
+
# height: 400
|
|
11
|
+
# template:
|
|
12
|
+
# """
|
|
13
|
+
# <div class="up-modal">
|
|
14
|
+
# <div class="up-modal-overlay"></div>
|
|
15
|
+
# <div class="up-modal-dialog">
|
|
16
|
+
# <a href="#" class="up-modal-close">
|
|
17
|
+
# <span class="up-modal-close-label">Close</span>
|
|
18
|
+
# <span class="up-modal-close-key">ESC</span>
|
|
19
|
+
# </a>
|
|
20
|
+
# <div class="up-modal-content"></div>
|
|
21
|
+
# </div>
|
|
22
|
+
# </div>
|
|
23
|
+
# """
|
|
24
|
+
#
|
|
25
|
+
# defaults = (defaults) ->
|
|
26
|
+
# up.util.extend(configuration, defaults)
|
|
27
|
+
#
|
|
28
|
+
# open = (link, options) ->
|
|
29
|
+
# $link = $(link)
|
|
30
|
+
# url = $link.attr("href")
|
|
31
|
+
# selector = $link.attr("up-modal") || $link.attr("up-target") || 'body'
|
|
32
|
+
# options.source = true
|
|
33
|
+
# replace(selector, url, options)
|
|
34
|
+
# options = up.util.options(options, configuration)
|
|
35
|
+
# createElements()
|
|
36
|
+
# update(options)
|
|
37
|
+
#
|
|
38
|
+
# close = ->
|
|
39
|
+
# if $container
|
|
40
|
+
# $container.remove()
|
|
41
|
+
# $container = null
|
|
42
|
+
|
|
43
|
+
source = ->
|
|
44
|
+
$('.up-modal').find('[up-source]').attr('up-source')
|
|
45
|
+
|
|
46
|
+
# defaults: defaults
|
|
47
|
+
# open: open
|
|
48
|
+
# close: close
|
|
49
|
+
source: source
|
|
50
|
+
|
|
51
|
+
)()
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
###*
|
|
2
|
+
Animation and transition effects.
|
|
3
|
+
|
|
4
|
+
@class up.motion
|
|
5
|
+
###
|
|
6
|
+
up.motion = (->
|
|
7
|
+
|
|
8
|
+
defaultOptions =
|
|
9
|
+
duration: 300
|
|
10
|
+
easing: 'ease'
|
|
11
|
+
|
|
12
|
+
animations = {}
|
|
13
|
+
transitions = {}
|
|
14
|
+
|
|
15
|
+
cssAnimate = up.util.cssAnimate
|
|
16
|
+
|
|
17
|
+
###*
|
|
18
|
+
Animates an element.
|
|
19
|
+
|
|
20
|
+
The following animations are pre-registered:
|
|
21
|
+
|
|
22
|
+
- `fade-in`
|
|
23
|
+
- `fade-out`
|
|
24
|
+
- `move-to-top`
|
|
25
|
+
- `move-from-top`
|
|
26
|
+
- `move-to-bottom`
|
|
27
|
+
- `move-from-bottom`
|
|
28
|
+
- `move-to-left`
|
|
29
|
+
- `move-from-left`
|
|
30
|
+
- `move-to-right`
|
|
31
|
+
- `move-from-right`
|
|
32
|
+
- `none`
|
|
33
|
+
|
|
34
|
+
@method up.animate
|
|
35
|
+
@param {Element|jQuery|String} elementOrSelector
|
|
36
|
+
@param {String|Function} animationOrName
|
|
37
|
+
@param {Number} [options.duration]
|
|
38
|
+
@param {String} [options.easing]
|
|
39
|
+
@return {Promise}
|
|
40
|
+
A promise for the animation's end.
|
|
41
|
+
###
|
|
42
|
+
animate = (elementOrSelector, animationOrName, options) ->
|
|
43
|
+
$element = $(elementOrSelector)
|
|
44
|
+
console.log("animating with", animationOrName)
|
|
45
|
+
options = up.util.options(options, defaultOptions)
|
|
46
|
+
anim = if up.util.isFunction(animationOrName)
|
|
47
|
+
animationOrName
|
|
48
|
+
else
|
|
49
|
+
animations[animationOrName] or up.util.error("Unknown animation", animationName)
|
|
50
|
+
assertIsPromise(
|
|
51
|
+
anim($element, options),
|
|
52
|
+
["Animation did not return a Promise", animationOrName]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
withGhosts = ($old, $new, block) ->
|
|
56
|
+
$oldGhost = null
|
|
57
|
+
$newGhost = null
|
|
58
|
+
up.util.temporaryCss $new, display: 'none', ->
|
|
59
|
+
$oldGhost = up.util.prependGhost($old)
|
|
60
|
+
up.util.temporaryCss $old, display: 'none', ->
|
|
61
|
+
$newGhost = up.util.prependGhost($new)
|
|
62
|
+
# $old should take up space in the page flow until the transition ends
|
|
63
|
+
$old.css(visibility: 'hidden')
|
|
64
|
+
newCssMemo = up.util.temporaryCss($new, display: 'none')
|
|
65
|
+
promise = block($oldGhost, $newGhost)
|
|
66
|
+
promise.then ->
|
|
67
|
+
$oldGhost.remove()
|
|
68
|
+
$newGhost.remove()
|
|
69
|
+
# Now that the transition is over we show $new again.
|
|
70
|
+
# Since we expect $old to be removed in a heartblink,
|
|
71
|
+
# $new should take up space
|
|
72
|
+
$old.css(display: 'none')
|
|
73
|
+
newCssMemo()
|
|
74
|
+
|
|
75
|
+
assertIsPromise = (object, messageParts) ->
|
|
76
|
+
up.util.isPromise(object) or up.util.error(messageParts...)
|
|
77
|
+
object
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
###*
|
|
83
|
+
Performs a transition between two elements.
|
|
84
|
+
|
|
85
|
+
The following transitions are pre-registered:
|
|
86
|
+
|
|
87
|
+
- `cross-fade`
|
|
88
|
+
- `move-top`
|
|
89
|
+
- `move-bottom`
|
|
90
|
+
- `move-left`
|
|
91
|
+
- `move-right`
|
|
92
|
+
- `none`
|
|
93
|
+
|
|
94
|
+
You can also compose a transition from two animation names
|
|
95
|
+
separated by a slash character (`/`):
|
|
96
|
+
|
|
97
|
+
- `move-to-bottom/fade-in`
|
|
98
|
+
- `move-to-left/move-from-top`
|
|
99
|
+
|
|
100
|
+
@method up.morph
|
|
101
|
+
@param {Element|jQuery|String} source
|
|
102
|
+
@param {Element|jQuery|String} target
|
|
103
|
+
@param {Function|String} transitionOrName
|
|
104
|
+
@param {Number} [options.duration]
|
|
105
|
+
@param {String} [options.easing]
|
|
106
|
+
@return {Promise}
|
|
107
|
+
A promise for the transition's end.
|
|
108
|
+
###
|
|
109
|
+
morph = (source, target, transitionOrName, options) ->
|
|
110
|
+
options = up.util.options(defaultOptions)
|
|
111
|
+
$old = $(source)
|
|
112
|
+
$new = $(target)
|
|
113
|
+
transition = up.util.presence(transitionOrName, up.util.isFunction) || transitions[transitionOrName]
|
|
114
|
+
if transition
|
|
115
|
+
withGhosts $old, $new, ($oldGhost, $newGhost) ->
|
|
116
|
+
assertIsPromise(
|
|
117
|
+
transition($oldGhost, $newGhost, options),
|
|
118
|
+
["Transition did not return a promise", transitionOrName]
|
|
119
|
+
)
|
|
120
|
+
else if animation = animations[transitionOrName]
|
|
121
|
+
$old.hide()
|
|
122
|
+
animate($new, animation, options)
|
|
123
|
+
else if up.util.isString(transitionOrName) && transitionOrName.indexOf('/') >= 0
|
|
124
|
+
parts = transitionOrName.split('/')
|
|
125
|
+
transition = ($old, $new, options) ->
|
|
126
|
+
$.when(
|
|
127
|
+
animate($old, parts[0], options),
|
|
128
|
+
animate($new, parts[1], options)
|
|
129
|
+
)
|
|
130
|
+
morph($old, $new, transition, options)
|
|
131
|
+
else
|
|
132
|
+
up.util.error("Unknown transition: #{transitionOrName}")
|
|
133
|
+
|
|
134
|
+
###*
|
|
135
|
+
Defines a named transition.
|
|
136
|
+
|
|
137
|
+
@method up.transition
|
|
138
|
+
@param {String} name
|
|
139
|
+
@param {Function} transition
|
|
140
|
+
###
|
|
141
|
+
transition = (name, transition) ->
|
|
142
|
+
transitions[name] = transition
|
|
143
|
+
|
|
144
|
+
###*
|
|
145
|
+
Defines a named animation.
|
|
146
|
+
|
|
147
|
+
@method up.animation
|
|
148
|
+
@param {String} name
|
|
149
|
+
@param {Function} animation
|
|
150
|
+
###
|
|
151
|
+
animation = (name, animation) ->
|
|
152
|
+
animations[name] = animation
|
|
153
|
+
|
|
154
|
+
###*
|
|
155
|
+
Returns a no-op animation or transition which has no visual effects
|
|
156
|
+
and completes instantly.
|
|
157
|
+
|
|
158
|
+
@method up.motion.none
|
|
159
|
+
@return {Promise}
|
|
160
|
+
A resolved promise
|
|
161
|
+
###
|
|
162
|
+
none = ->
|
|
163
|
+
deferred = $.Deferred()
|
|
164
|
+
deferred.resolve()
|
|
165
|
+
deferred.promise()
|
|
166
|
+
|
|
167
|
+
animation('none', none)
|
|
168
|
+
|
|
169
|
+
animation('fade-in', ($ghost, options) ->
|
|
170
|
+
$ghost.css(opacity: 0)
|
|
171
|
+
cssAnimate($ghost, { opacity: 1 }, options)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
animation('fade-out', ($ghost, options) ->
|
|
175
|
+
$ghost.css(opacity: 1)
|
|
176
|
+
cssAnimate($ghost, { opacity: 0 }, options)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
animation('move-to-top', ($ghost, options) ->
|
|
180
|
+
$ghost.css('margin-top': '0%')
|
|
181
|
+
cssAnimate($ghost, { 'margin-top': '-100%' }, options)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
animation('move-from-top', ($ghost, options) ->
|
|
185
|
+
$ghost.css('margin-top': '-100%')
|
|
186
|
+
cssAnimate($ghost, { 'margin-top': '0%' }, options)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
animation('move-to-bottom', ($ghost, options) ->
|
|
190
|
+
$ghost.css('margin-top': '0%')
|
|
191
|
+
cssAnimate($ghost, { 'margin-top': '100%' }, options)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
animation('move-from-bottom', ($ghost, options) ->
|
|
195
|
+
$ghost.css('margin-top': '100%')
|
|
196
|
+
cssAnimate($ghost, { 'margin-top': '0%' }, options)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
animation('move-to-left', ($ghost, options) ->
|
|
200
|
+
$ghost.css('margin-left': '0%')
|
|
201
|
+
cssAnimate($ghost, { 'margin-left': '-100%' }, options)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
animation('move-from-left', ($ghost, options) ->
|
|
205
|
+
$ghost.css('margin-left': '-100%')
|
|
206
|
+
cssAnimate($ghost, { 'margin-left': '0%' }, options)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
animation('move-to-right', ($ghost, options) ->
|
|
210
|
+
$ghost.css('margin-left': '0%')
|
|
211
|
+
cssAnimate($ghost, { 'margin-left': '100%' }, options)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
animation('move-from-right', ($ghost, options) ->
|
|
215
|
+
$ghost.css('margin-left': '100%')
|
|
216
|
+
cssAnimate($ghost, { 'margin-left': '0%' }, options)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
animation('roll-down', ($ghost, options) ->
|
|
220
|
+
fullHeight = $ghost.height()
|
|
221
|
+
styleMemo = up.util.temporaryCss($ghost,
|
|
222
|
+
height: '0px'
|
|
223
|
+
overflow: 'hidden'
|
|
224
|
+
)
|
|
225
|
+
cssAnimate($ghost, { height: "#{fullHeight}px" }, options).then(styleMemo)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
transition('none', none)
|
|
229
|
+
|
|
230
|
+
transition('move-left', ($old, $new, options) ->
|
|
231
|
+
$.when(
|
|
232
|
+
animate($old, 'move-to-left', options),
|
|
233
|
+
animate($new, 'move-from-right', options)
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
transition('move-right', ($old, $new, options) ->
|
|
238
|
+
$.when(
|
|
239
|
+
animate($old, 'move-to-right', options),
|
|
240
|
+
animate($new, 'move-from-left', options)
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
transition('move-top', ($old, $new, options) ->
|
|
245
|
+
$.when(
|
|
246
|
+
animate($old, 'move-to-top', options),
|
|
247
|
+
animate($new, 'move-from-bottom', options)
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
transition('move-bottom', ($old, $new, options) ->
|
|
252
|
+
$.when(
|
|
253
|
+
animate($old, 'move-to-bottom', options),
|
|
254
|
+
animate($new, 'move-from-top', options)
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
transition('cross-fade', ($old, $new, options) ->
|
|
259
|
+
$.when(
|
|
260
|
+
animate($old, 'fade-out', options),
|
|
261
|
+
animate($new, 'fade-in', options)
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
morph: morph
|
|
266
|
+
animate: animate
|
|
267
|
+
transition: transition
|
|
268
|
+
animation: animation
|
|
269
|
+
none: none
|
|
270
|
+
|
|
271
|
+
)()
|
|
272
|
+
|
|
273
|
+
up.transition = up.motion.transition
|
|
274
|
+
up.animation = up.motion.animation
|
|
275
|
+
up.morph = up.motion.morph
|
|
276
|
+
up.animate = up.motion.animate
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
###*
|
|
2
|
+
This module marks up link elements with classes indicating that
|
|
3
|
+
they are currently loading (class `up-active`) or linking
|
|
4
|
+
to the current location (class `up-current`).
|
|
5
|
+
|
|
6
|
+
The current location is either:
|
|
7
|
+
|
|
8
|
+
- the URL displayed in the browser window's location bar
|
|
9
|
+
- the source URL of a currently opened {{#crossLink "up.modal"}}modal dialog{{/crossLink}}
|
|
10
|
+
- the source URL of a currently opened {{#crossLink "up.popup"}}popup overlay{{/crossLink}}
|
|
11
|
+
|
|
12
|
+
@class up.navigation
|
|
13
|
+
###
|
|
14
|
+
up.navigation = (->
|
|
15
|
+
|
|
16
|
+
CLASS_ACTIVE = 'up-active'
|
|
17
|
+
CLASS_CURRENT = 'up-current'
|
|
18
|
+
SELECTOR_SECTION = 'a[href], a[up-target], [up-follow], [up-modal], [up-popup]'
|
|
19
|
+
SELECTOR_ACTIVE = ".#{CLASS_ACTIVE}"
|
|
20
|
+
|
|
21
|
+
locationChanged = ->
|
|
22
|
+
windowLocation = up.util.normalizeUrl(location.href, search: false)
|
|
23
|
+
modalLocation = up.modal.source()
|
|
24
|
+
popupLocation = up.popup.source()
|
|
25
|
+
|
|
26
|
+
up.util.each $(SELECTOR_SECTION), (section) ->
|
|
27
|
+
$section = $(section)
|
|
28
|
+
# if $section is marked up with up-follow,
|
|
29
|
+
# the actual link might be a child element.
|
|
30
|
+
url = up.link.resolveUrl($section)
|
|
31
|
+
url = up.util.normalizeUrl(url, search: false)
|
|
32
|
+
if url == windowLocation || url == modalLocation || url == popupLocation
|
|
33
|
+
$section.addClass(CLASS_CURRENT)
|
|
34
|
+
else
|
|
35
|
+
$section.removeClass(CLASS_CURRENT)
|
|
36
|
+
|
|
37
|
+
sectionClicked = ($section) ->
|
|
38
|
+
unmarkActive()
|
|
39
|
+
$section = enlargeClickArea($section)
|
|
40
|
+
$section.addClass(CLASS_ACTIVE)
|
|
41
|
+
|
|
42
|
+
enlargeClickArea = ($section) ->
|
|
43
|
+
up.util.presence($section.parents(SELECTOR_SECTION)) || $section
|
|
44
|
+
|
|
45
|
+
unmarkActive = ->
|
|
46
|
+
$(SELECTOR_ACTIVE).removeClass(CLASS_ACTIVE)
|
|
47
|
+
|
|
48
|
+
up.on 'click', SELECTOR_SECTION, (event, $section) ->
|
|
49
|
+
sectionClicked($section)
|
|
50
|
+
|
|
51
|
+
# If a new fragment is inserted, it's likely to be the result
|
|
52
|
+
# to the active action. So we can remove the active marker.
|
|
53
|
+
up.bus.on 'fragment:ready', ->
|
|
54
|
+
unmarkActive()
|
|
55
|
+
locationChanged()
|
|
56
|
+
|
|
57
|
+
# If the destroyed fragment is a modal or popup container
|
|
58
|
+
# this changes which URLs we consider currents.
|
|
59
|
+
up.bus.on 'fragment:destroy', ($fragment) ->
|
|
60
|
+
if $fragment.is('.up-modal, .up-popup')
|
|
61
|
+
locationChanged()
|
|
62
|
+
|
|
63
|
+
)()
|