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
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
|