upjs-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a95bff232406d1e7ca5ca6919fad7af7e7b110ac
|
4
|
+
data.tar.gz: d30a63428b8ec0d20e302e27d53c96c5e540cf80
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0fe59a20209d977d270cef3a1277a7d0a288c8102d68290d0d0bd0654b9c7a8444a9681a81b028d3cf180486e9ac803558ebc7e6b04b3435735fcf56c9573dda
|
7
|
+
data.tar.gz: 9c51d41fb1cbbc28adf735ae1c72e6036f83a7ff65cdbd9b7d1edc678465c21dab68b9eb085222bd24eaad8e8acbcd2b6a77e12583f31532c13140ffc5284435
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Henning Koch
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Up + Rails bindings (WIP)
|
2
|
+
|
3
|
+
Up.js is a solution for Rails apps that need fast-responding UI
|
4
|
+
but don't want to pay the Javascript MVC complexity tax.
|
5
|
+
|
6
|
+
## Design manifesto
|
7
|
+
|
8
|
+
### Client-side code is a complexity driver
|
9
|
+
|
10
|
+
### Server-side code should stay the same
|
11
|
+
- We like the simplicity of classic Rails development
|
12
|
+
- It should not require extra controller actions to update a page part via AJAX
|
13
|
+
|
14
|
+
### Batteries included
|
15
|
+
- We will ship a basic implementation for the most established UI patterns like navigation bars, infinite scrolling, drop-down menus, modals
|
16
|
+
- We will split this out into a plugin architecture eventually, but not now
|
17
|
+
|
18
|
+
### Ruby on Rails first
|
19
|
+
- We will leverage the assumptions that Rails is underneath
|
20
|
+
- Other frameworks once we’re happy with Rails
|
21
|
+
|
22
|
+
### Not for ambitious UIs
|
23
|
+
- We don’t want to compromise ease of use for simple patterns by providing a million hooks and options
|
24
|
+
- Limits in configurability
|
25
|
+
- You can always roll your own code
|
26
|
+
- Probably the wrong choice if you want to create something very ambitious
|
27
|
+
|
28
|
+
### (Sort of) Plays nice with existing JS code
|
29
|
+
- If you're ready to go into our event binding
|
30
|
+
|
31
|
+
### URLs are important
|
32
|
+
- Every page has an URL
|
33
|
+
- Works nice with Google
|
34
|
+
Works with browsers that don’t speak Up.js (e. g. IE9 doesn’t speak pushState)
|
35
|
+
|
36
|
+
### Be small
|
37
|
+
|
38
|
+
### Few dependencies
|
39
|
+
- jQuery
|
40
|
+
|
41
|
+
### Convention over configuration
|
42
|
+
|
43
|
+
### Interface: Both UJS and programmatic
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/doc-server
ADDED
data/bin/install-ydoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
animate($element)
|
2
|
+
|
3
|
+
gar nix, ghosting macht NUR transition
|
4
|
+
|
5
|
+
($old und $new muss anders gehandelt werden)
|
6
|
+
|
7
|
+
transition($old, $new)
|
8
|
+
|
9
|
+
1. $old.css(display: none)
|
10
|
+
|
11
|
+
2. $new in den DOM setzen
|
12
|
+
|
13
|
+
3. $new ausmessen
|
14
|
+
|
15
|
+
4. ghost von $new erstellen
|
16
|
+
DOM kopieren
|
17
|
+
mit position: fixed
|
18
|
+
mit ausmessen des originals
|
19
|
+
left, top setzen
|
20
|
+
right, bottom löschen
|
21
|
+
width, height setzen
|
22
|
+
margin löschen
|
23
|
+
|
24
|
+
5. $new.css(display: none)
|
25
|
+
|
26
|
+
6. $old.css(display: previousDisplay)
|
27
|
+
|
28
|
+
7. ghost von $old erstellen
|
29
|
+
|
30
|
+
8. $old.css(opacity: 0) // damit es den platz wegnimmt
|
31
|
+
|
32
|
+
9. transition($oldGhost, $newGhost)
|
33
|
+
|
34
|
+
10. $old löschen
|
35
|
+
$oldGhost löschen
|
36
|
+
$newGhost löschen
|
37
|
+
|
38
|
+
11. $new.css(display: previousDisplay)
|
39
|
+
|
40
|
+
|
41
|
+
was löst das, was nicht?
|
42
|
+
========================
|
43
|
+
|
44
|
+
- die teile müssen nicht mehr absolut gestellt werden
|
45
|
+
- old muss immer noch absolut gehen :(
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
1. Ich kopiere $old zu $ghost
|
54
|
+
|
55
|
+
2. Ich setze die opacity von $old auf 0
|
56
|
+
|
57
|
+
3. Ich setze $ghost auf die Größe von $old
|
58
|
+
mit position: fixed
|
59
|
+
mit ausmessen des originals
|
60
|
+
left, top setzen
|
61
|
+
right, bottom löschen
|
62
|
+
width, height setzen
|
63
|
+
margin löschen
|
64
|
+
|
65
|
+
???????????
|
66
|
+
|
67
|
+
4. ich gebe $ghost an die animation
|
68
|
+
|
69
|
+
5. nach der animation
|
70
|
+
setze ich $opacity von $element auf 1
|
71
|
+
lösche ich $ghost
|
72
|
+
|
data/design/design.txt
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Gedanke 1
|
2
|
+
---------
|
3
|
+
- Immer die ganze Seite rendern ist langsam
|
4
|
+
- Optimierung nicht möglich: Layout überspringen wenn up-Request
|
5
|
+
- Optimierung nicht möglich: Manche Bereiche nicht rendern wenn up-Request
|
6
|
+
- Optimierung nicht möglich: Manche Bereiche nur für bestimmte X-Up-Selectors rendern
|
7
|
+
- Das alles nur, um beim Back-Button sofort das HTML anzeigen zu können?
|
8
|
+
- Sogar Turbolinks macht das nur Opt-In
|
9
|
+
- Ich kann genauso gut bei popstate den aktuellen State replacen
|
10
|
+
- Später: Opportunistisch cachen. Wann immer ich einen vollen Page-Load (mit/ohne Layout) sehe => cachen
|
11
|
+
|
12
|
+
Gedanke 2
|
13
|
+
---------
|
14
|
+
- Das mit dem up.app und up.page checkt kein Mensch
|
15
|
+
- In der Praxis wird es so sein, dass wichtige page-Handler nicht mehr in der Seite stehen
|
16
|
+
- Ich brauche auch noch Code, um <script>-Tags auszuführen
|
17
|
+
- Die Unterscheidung mache ich ja nur, um installierte Callbacks wieder wegzuräumen
|
18
|
+
- Was ich eigentlich bräuchte, wäre ein Weg, installierte Callbacks geregelt lozuwerden
|
19
|
+
|
20
|
+
z. B.
|
21
|
+
|
22
|
+
up.awaken('.note_form', function($element) {
|
23
|
+
|
24
|
+
var timer = setTimeout(...)
|
25
|
+
|
26
|
+
function uninstall() {
|
27
|
+
clearInterval(timer);
|
28
|
+
}
|
29
|
+
|
30
|
+
return uninstall;
|
31
|
+
|
32
|
+
});
|
33
|
+
|
34
|
+
- Vor dem Ersetzen eines Elements kann ich dann die uninstall-Funktion aufrufen.
|
@@ -0,0 +1,48 @@
|
|
1
|
+
GUT:
|
2
|
+
|
3
|
+
<a href="/terms" up-modal=".main" up-modal-style="right">
|
4
|
+
|
5
|
+
<a href="/terms" up-on-click="up.modal.open(this, '.main', { style: 'right' })">
|
6
|
+
|
7
|
+
<a href="/terms" onclick="up.modal.open(this, '.main', { style: 'right' }); return false">
|
8
|
+
|
9
|
+
|
10
|
+
WEIRD:
|
11
|
+
|
12
|
+
<a href="/terms" up-target=".main" up-presentation="modal/right">
|
13
|
+
|
14
|
+
<a href="/terms" up-target=".main" up-modal up-modal-presentation="right">
|
15
|
+
|
16
|
+
<a href="/terms" up-target="@modal(.main)" up-presentation="modal/right">
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
<a href="/sites" up-popup=".main" up-popup-anchor="bottom" up-history="push">
|
21
|
+
|
22
|
+
|
23
|
+
<!-- cards/edit.html.erb -->
|
24
|
+
|
25
|
+
<%= form_for @card do |form| %>
|
26
|
+
|
27
|
+
= form.label :title
|
28
|
+
= form.text_field :title
|
29
|
+
|
30
|
+
<div class="modifiers">
|
31
|
+
....
|
32
|
+
<%= link_to 'Edit modifiers', edit_card_meta_path(@card), 'up-modal' => '.meta_form' %>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
|
38
|
+
<!-- cards/meta/edit.html.erb -->
|
39
|
+
|
40
|
+
<%= form_for @card, html: { class: 'meta_form', 'up-target' => '.modifiers' } do |form| %>
|
41
|
+
|
42
|
+
...
|
43
|
+
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
<a href="close" up-close-modal>X</a>
|
data/design/draft.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
up.browser = (->
|
2
|
+
|
3
|
+
# safari = false
|
4
|
+
#
|
5
|
+
# detect = ->
|
6
|
+
# agent = navigator.userAgent
|
7
|
+
# agentHas = (substring) -> agent.indexOf(substring) >= 1
|
8
|
+
# safari = agentHas('Safari') && !agentHas('Chrome')
|
9
|
+
#
|
10
|
+
# transitionEndEvent = ->
|
11
|
+
# if safari
|
12
|
+
# 'webkitTransitionEnd'
|
13
|
+
# else
|
14
|
+
# 'transitionend'
|
15
|
+
#
|
16
|
+
# detect()
|
17
|
+
#
|
18
|
+
# transitionEndEvent: transitionEndEvent
|
19
|
+
|
20
|
+
)()
|
@@ -0,0 +1,54 @@
|
|
1
|
+
###*
|
2
|
+
Event bus for up-related events:
|
3
|
+
|
4
|
+
- `app:ready`
|
5
|
+
- `fragment:ready` with arguments `($fragment)`
|
6
|
+
- `fragment:destroy` with arguments `($fragment)`
|
7
|
+
|
8
|
+
TODO: This might eventually be rolled into regular document events.
|
9
|
+
|
10
|
+
@class up.bus
|
11
|
+
###
|
12
|
+
up.bus = (->
|
13
|
+
|
14
|
+
callbacksByEvent = {}
|
15
|
+
|
16
|
+
callbacksFor = (event) ->
|
17
|
+
callbacksByEvent[event] ||= []
|
18
|
+
|
19
|
+
###*
|
20
|
+
Registers an event handler to be called when the given
|
21
|
+
event is triggered.
|
22
|
+
|
23
|
+
@method up.bus.listen
|
24
|
+
@param {String} eventName
|
25
|
+
The event name to match.
|
26
|
+
@param {Function} handler
|
27
|
+
The event handler to be called with the event arguments.
|
28
|
+
###
|
29
|
+
# We cannot call this function "on" because Coffeescript
|
30
|
+
# https://makandracards.com/makandra/29733-english-words-that-you-cannot-use-in-coffeescript
|
31
|
+
listen = (eventName, handler) ->
|
32
|
+
callbacksFor(eventName).push(handler)
|
33
|
+
|
34
|
+
###*
|
35
|
+
Triggers an event.
|
36
|
+
|
37
|
+
@method up.bus.emit
|
38
|
+
@param {String} eventName
|
39
|
+
The name of the event.
|
40
|
+
@param {Anything...} args
|
41
|
+
The arguments that describe the event.
|
42
|
+
###
|
43
|
+
emit = (eventName, args...) ->
|
44
|
+
console.log("bus emitting", eventName, args)
|
45
|
+
callbacks = callbacksFor(eventName)
|
46
|
+
up.util.each(callbacks, (callback) ->
|
47
|
+
callback(args...)
|
48
|
+
)
|
49
|
+
|
50
|
+
return (
|
51
|
+
on: listen
|
52
|
+
emit: emit
|
53
|
+
)
|
54
|
+
)()
|
@@ -0,0 +1,160 @@
|
|
1
|
+
###*
|
2
|
+
Page flow.
|
3
|
+
|
4
|
+
@class up.flow
|
5
|
+
###
|
6
|
+
up.flow = (->
|
7
|
+
|
8
|
+
setSource = (element, sourceUrl) ->
|
9
|
+
$element = $(element)
|
10
|
+
sourceUrl = up.util.normalizeUrl(sourceUrl) if up.util.isPresent(sourceUrl)
|
11
|
+
$element.attr("up-source", sourceUrl)
|
12
|
+
|
13
|
+
source = (element) ->
|
14
|
+
$element = $(element).closest("[up-source]")
|
15
|
+
$element.attr("up-source") || location.href
|
16
|
+
|
17
|
+
###*
|
18
|
+
Replaces elements on the current page with corresponding elements
|
19
|
+
from a new page fetched from the server.
|
20
|
+
|
21
|
+
The current and new elements must have the same CSS selector.
|
22
|
+
|
23
|
+
@method up.replace
|
24
|
+
@param {String|Element|jQuery} selectorOrElement
|
25
|
+
The CSS selector to update. You can also pass a DOM element or jQuery element
|
26
|
+
here, in which case a selector will be inferred from the element's class and ID.
|
27
|
+
@param {String} url
|
28
|
+
The URL to fetch from the server.
|
29
|
+
@param {String} [options.history.url=url]
|
30
|
+
An alternative URL to use for the browser's location bar and history.
|
31
|
+
@param {String} [options.history.method='push']
|
32
|
+
@param {String} [options.transition]
|
33
|
+
@param {String|Boolean} [options.source]
|
34
|
+
###
|
35
|
+
replace = (selectorOrElement, url, options) ->
|
36
|
+
|
37
|
+
selector = if up.util.presence(selectorOrElement)
|
38
|
+
selectorOrElement
|
39
|
+
else
|
40
|
+
up.util.createSelectorFromElement($(selectorOrElement))
|
41
|
+
|
42
|
+
options = up.util.options(options, history: { url: url })
|
43
|
+
|
44
|
+
if up.util.isMissing(options.source) || options.source == true
|
45
|
+
options.source = url
|
46
|
+
|
47
|
+
up.util.get(url, selector: selector)
|
48
|
+
.done (html) -> implant(selector, html, options)
|
49
|
+
.fail(up.util.error)
|
50
|
+
|
51
|
+
###*
|
52
|
+
@method up.flow.implant
|
53
|
+
@protected
|
54
|
+
@param {String} selector
|
55
|
+
@param {String} html
|
56
|
+
@param {String} [options.source]
|
57
|
+
@param {String} [options.history.url]
|
58
|
+
@param {String} [options.history.method='push']
|
59
|
+
@param {String} [options.transition]
|
60
|
+
###
|
61
|
+
implant = (selector, html, options) ->
|
62
|
+
|
63
|
+
options = up.util.options(options, history: { method: 'push' })
|
64
|
+
# jQuery cannot construct transient elements that contain <html> or <body> tags,
|
65
|
+
# so we're using the native browser API to grep through the HTML
|
66
|
+
htmlElement = up.util.createElementFromHtml(html)
|
67
|
+
|
68
|
+
for step in implantSteps(selector, options)
|
69
|
+
$old = $(step.selector)
|
70
|
+
if fragment = htmlElement.querySelector(step.selector)
|
71
|
+
$new = $(fragment)
|
72
|
+
swapElements $old, $new, step.transition, ->
|
73
|
+
options.insert?($new)
|
74
|
+
title = htmlElement.querySelector("title")?.textContent # todo: extract title from header
|
75
|
+
if options.history.url
|
76
|
+
# alert(options.history)
|
77
|
+
# alert(options.history.url)
|
78
|
+
document.title = title if title
|
79
|
+
up.history[options.history.method](options.history.url)
|
80
|
+
# Remember where the element came from so we can make
|
81
|
+
# smaller page loads in the future (does this even make sense?).
|
82
|
+
setSource($new, options.source || history.url)
|
83
|
+
autofocus($new)
|
84
|
+
|
85
|
+
else
|
86
|
+
up.util.error("Could not find selector (#{step.selector}) in response (#{html})")
|
87
|
+
|
88
|
+
swapElements = ($old, $new, transitionName, afterInsert) ->
|
89
|
+
if up.util.isGiven(transitionName)
|
90
|
+
if $old.is('body') && transitionName != 'none'
|
91
|
+
up.util.error('Cannot apply transitions to body-elements', transitionName)
|
92
|
+
$new.insertAfter($old)
|
93
|
+
# Make sure that any element enhancements happen BEFORE we morph
|
94
|
+
# through the transition.
|
95
|
+
afterInsert()
|
96
|
+
up.bus.emit('fragment:ready', $new) # this should happen before the transition, so transitions see .up-current classes
|
97
|
+
up.morph($old, $new, transitionName).then -> destroy($old)
|
98
|
+
else
|
99
|
+
up.bus.emit('fragment:destroy', old)
|
100
|
+
$old.replaceWith($new)
|
101
|
+
up.bus.emit('fragment:ready', $new)
|
102
|
+
afterInsert()
|
103
|
+
|
104
|
+
|
105
|
+
implantSteps = (selector, options) ->
|
106
|
+
transitionString = options.transition || options.animation || 'none'
|
107
|
+
comma = /\ *,\ */
|
108
|
+
disjunction = selector.split(comma)
|
109
|
+
transitions = transitionString.split(comma) if up.util.isPresent(transitionString)
|
110
|
+
for selectorAtom, i in disjunction
|
111
|
+
transition = transitions[i] || up.util.last(transitions)
|
112
|
+
selector: selectorAtom
|
113
|
+
transition: transition
|
114
|
+
|
115
|
+
autofocus = ($element) ->
|
116
|
+
$control = $element.find('[autofocus]:last')
|
117
|
+
if $control.length && $control.get(0) != document.activeElement
|
118
|
+
$control.focus()
|
119
|
+
|
120
|
+
###*
|
121
|
+
Destroys the given element or selector.
|
122
|
+
Takes care that all destructors, if any, are called.
|
123
|
+
|
124
|
+
@method up.destroy
|
125
|
+
@param {String|Element|jQuery} selectorOrElement
|
126
|
+
###
|
127
|
+
destroy = (selectorOrElement, options) ->
|
128
|
+
$element = $(selectorOrElement)
|
129
|
+
options = up.util.options(options, animation: 'none')
|
130
|
+
$element.addClass('up-destroying')
|
131
|
+
up.bus.emit('fragment:destroy', $element)
|
132
|
+
up.motion.animate($element, options.animation).then ->
|
133
|
+
$element.remove()
|
134
|
+
|
135
|
+
###*
|
136
|
+
Replaces the given selector or element with a fresh copy
|
137
|
+
fetched from the server.
|
138
|
+
|
139
|
+
@method up.reload
|
140
|
+
@param {String|Element|jQuery} selectorOrElement
|
141
|
+
###
|
142
|
+
reload = (selectorOrElement) ->
|
143
|
+
sourceUrl = source(selectorOrElement)
|
144
|
+
replace(selectorOrElement, sourceUrl)
|
145
|
+
|
146
|
+
|
147
|
+
up.bus.on('app:ready', ->
|
148
|
+
setSource(document.body, location.href)
|
149
|
+
)
|
150
|
+
|
151
|
+
replace: replace
|
152
|
+
reload: reload
|
153
|
+
destroy: destroy
|
154
|
+
implant: implant
|
155
|
+
|
156
|
+
)()
|
157
|
+
|
158
|
+
up.replace = up.flow.replace
|
159
|
+
up.reload = up.flow.reload
|
160
|
+
up.destroy = up.flow.destroy
|
@@ -0,0 +1,162 @@
|
|
1
|
+
###*
|
2
|
+
Form handling
|
3
|
+
@class up.form
|
4
|
+
###
|
5
|
+
up.form = (->
|
6
|
+
|
7
|
+
###*
|
8
|
+
Submits a form using the Up.js flow.
|
9
|
+
|
10
|
+
@method up.submit
|
11
|
+
@param {Element|jQuery|String} formOrSelector
|
12
|
+
A reference or selector for the form to submit.
|
13
|
+
@param {String} [options.target]
|
14
|
+
@param {String} [options.failTarget]
|
15
|
+
@param {Boolean} [options.history=true]
|
16
|
+
Successful form submissions will add a history entry and change the browser's
|
17
|
+
location bar if the form either uses the `GET` method or the response redirected
|
18
|
+
to another page (this requires the `upjs-rails` gem).
|
19
|
+
If want to prevent history changes in any case, set this to `false`.
|
20
|
+
@param {String} [options.transition]
|
21
|
+
@param {String} [options.failTransition]
|
22
|
+
@return {Promise}
|
23
|
+
A promise for the AJAX response
|
24
|
+
@example
|
25
|
+
up.submit('form')
|
26
|
+
@example
|
27
|
+
<form method="POST" action="/users" up-target=".main">
|
28
|
+
...
|
29
|
+
</form>
|
30
|
+
|
31
|
+
###
|
32
|
+
submit = (formOrSelector, options) ->
|
33
|
+
|
34
|
+
options = up.util.options(options)
|
35
|
+
$form = $(formOrSelector)
|
36
|
+
successSelector = options.target || $form.attr('up-target') || 'body'
|
37
|
+
failureSelector = options.failTarget || $form.attr('up-fail-target') || up.util.createSelectorFromElement($form)
|
38
|
+
pushHistory = options.history != false && $form.attr('up-history') != 'false'
|
39
|
+
successTransition = options.transition || $form.attr('up-transition')
|
40
|
+
failureTransition = options.failTransition || $form.attr('up-fail-transition')
|
41
|
+
$form.addClass('up-active')
|
42
|
+
|
43
|
+
request = {
|
44
|
+
url: $form.attr('action') || location.href
|
45
|
+
type: $form.attr('method')?.toUpperCase() || 'POST',
|
46
|
+
data: $form.serialize(),
|
47
|
+
selector: successSelector
|
48
|
+
}
|
49
|
+
|
50
|
+
successUrl = (xhr) ->
|
51
|
+
if pushHistory
|
52
|
+
if redirectLocation = xhr.getResponseHeader('X-Up-Previous-Redirect-Location')
|
53
|
+
redirectLocation
|
54
|
+
else if request.type == 'GET'
|
55
|
+
request.url + '?' + request.data
|
56
|
+
else
|
57
|
+
null
|
58
|
+
|
59
|
+
up.util.ajax(request)
|
60
|
+
.always ->
|
61
|
+
$form.removeClass('up-active')
|
62
|
+
.done (html, textStatus, xhr) ->
|
63
|
+
up.flow.implant(successSelector, html,
|
64
|
+
history: { url: successUrl(xhr) },
|
65
|
+
transition: successTransition
|
66
|
+
)
|
67
|
+
.fail (xhr, textStatus, errorThrown) ->
|
68
|
+
html = xhr.responseText
|
69
|
+
up.flow.implant(failureSelector, html,
|
70
|
+
transition: failureTransition
|
71
|
+
)
|
72
|
+
|
73
|
+
###*
|
74
|
+
Observes an input field by periodic polling its value.
|
75
|
+
Executes code when the value changes.
|
76
|
+
|
77
|
+
This is useful for observing text fields while the user is typing,
|
78
|
+
since browsers will only fire a `change` event once the user
|
79
|
+
blurs the text field.
|
80
|
+
|
81
|
+
@method up.observe
|
82
|
+
@param {Element|jQuery|String} fieldOrSelector
|
83
|
+
@param {Function|String} options.change
|
84
|
+
The callback to execute when the field's value changes.
|
85
|
+
If given as a function, it must take two arguments (`value`, `$field`).
|
86
|
+
If given as a string, it will be evaled as Javascript code in a context where
|
87
|
+
(`value`, `$field`) are set.
|
88
|
+
@param {Number} [options.frequency=500]
|
89
|
+
@example
|
90
|
+
up.observe('form', { change: function(value, $form) {
|
91
|
+
up.submit($form)
|
92
|
+
} });
|
93
|
+
@example
|
94
|
+
<form method="GET" action="/search">
|
95
|
+
<input type="query" up-observe="up.form.submit(this)">
|
96
|
+
</form>
|
97
|
+
###
|
98
|
+
observe = (fieldOrSelector, options) ->
|
99
|
+
|
100
|
+
$field = $(fieldOrSelector)
|
101
|
+
options = up.util.options(options, frequency: 500)
|
102
|
+
knownValue = null
|
103
|
+
timer = null
|
104
|
+
callback = null
|
105
|
+
if codeOnChange = $field.attr('up-observe')
|
106
|
+
callback = (value, $field) ->
|
107
|
+
eval(codeOnChange)
|
108
|
+
else if options.change
|
109
|
+
callback = options.change
|
110
|
+
else
|
111
|
+
up.util.error('observe: No change callback given')
|
112
|
+
|
113
|
+
check = ->
|
114
|
+
value = $field.val()
|
115
|
+
skipCallback = _.isNull(knownValue) # don't run the callback for the check during initialization
|
116
|
+
if knownValue != value
|
117
|
+
knownValue = value
|
118
|
+
callback.apply($field.get(0), [value, $field]) unless skipCallback
|
119
|
+
|
120
|
+
resetTimer = ->
|
121
|
+
if timer
|
122
|
+
clearTimer()
|
123
|
+
startTimer()
|
124
|
+
|
125
|
+
clearTimer = ->
|
126
|
+
clearInterval(timer)
|
127
|
+
timer = null
|
128
|
+
|
129
|
+
startTimer = ->
|
130
|
+
timer = setInterval(check, options.frequency)
|
131
|
+
|
132
|
+
# reset counter after user interaction
|
133
|
+
$field.bind "keyup click mousemove", resetTimer # mousemove is for selects
|
134
|
+
|
135
|
+
check()
|
136
|
+
startTimer()
|
137
|
+
|
138
|
+
# return destructor
|
139
|
+
return clearTimer
|
140
|
+
|
141
|
+
up.on 'submit', 'form[up-target]', (event, $form) ->
|
142
|
+
event.preventDefault()
|
143
|
+
submit($form)
|
144
|
+
|
145
|
+
up.awaken '[up-observe]', ($field) ->
|
146
|
+
return observe($field)
|
147
|
+
|
148
|
+
# up.awaken '[up-autosubmit]', ($field) ->
|
149
|
+
# return observe($field, change: ->
|
150
|
+
# $form = $field.closest('form')
|
151
|
+
# $field.addClass('up-active')
|
152
|
+
# up.submit($form).always ->
|
153
|
+
# $field.removeClass('up-active')
|
154
|
+
# )
|
155
|
+
|
156
|
+
submit: submit
|
157
|
+
observe: observe
|
158
|
+
|
159
|
+
)()
|
160
|
+
|
161
|
+
up.submit = up.form.submit
|
162
|
+
up.observe = up.form.observe
|