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.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-version +2 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +47 -0
  6. data/Rakefile +1 -0
  7. data/bin/doc-server +3 -0
  8. data/bin/install-ydoc +4 -0
  9. data/design/animation-ghosting.txt +72 -0
  10. data/design/design.txt +34 -0
  11. data/design/draft.html.erb +48 -0
  12. data/design/draft.rb +9 -0
  13. data/lib/assets/javascripts/up/browser.js.coffee +20 -0
  14. data/lib/assets/javascripts/up/bus.js.coffee +54 -0
  15. data/lib/assets/javascripts/up/flow.js.coffee +160 -0
  16. data/lib/assets/javascripts/up/form.js.coffee +162 -0
  17. data/lib/assets/javascripts/up/history.js.coffee +33 -0
  18. data/lib/assets/javascripts/up/index.js +13 -0
  19. data/lib/assets/javascripts/up/link.js.coffee +95 -0
  20. data/lib/assets/javascripts/up/magic.js.coffee +79 -0
  21. data/lib/assets/javascripts/up/modal.js.coffee +51 -0
  22. data/lib/assets/javascripts/up/module.js.coffee +4 -0
  23. data/lib/assets/javascripts/up/motion.js.coffee +276 -0
  24. data/lib/assets/javascripts/up/navigation.js.coffee +63 -0
  25. data/lib/assets/javascripts/up/popup.js.coffee +143 -0
  26. data/lib/assets/javascripts/up/util.js.coffee +287 -0
  27. data/lib/assets/stylesheets/up/follow.css.sass +2 -0
  28. data/lib/assets/stylesheets/up/index.css +3 -0
  29. data/lib/assets/stylesheets/up/modal.css.sass +54 -0
  30. data/lib/assets/stylesheets/up/popup.css.sass +9 -0
  31. data/lib/upjs/rails/engine.rb +6 -0
  32. data/lib/upjs/rails/redirection.rb +26 -0
  33. data/lib/upjs/rails/request.rb +13 -0
  34. data/lib/upjs/rails/version.rb +5 -0
  35. data/lib/upjs-rails.rb +7 -0
  36. data/spec_app/.firefox-version +1 -0
  37. data/spec_app/.gitignore +17 -0
  38. data/spec_app/Gemfile +24 -0
  39. data/spec_app/Gemfile.lock +239 -0
  40. data/spec_app/README.rdoc +28 -0
  41. data/spec_app/Rakefile +6 -0
  42. data/spec_app/app/assets/images/.keep +0 -0
  43. data/spec_app/app/assets/javascripts/application.js +16 -0
  44. data/spec_app/app/assets/stylesheets/application.css +15 -0
  45. data/spec_app/app/assets/stylesheets/blocks/card.css.sass +11 -0
  46. data/spec_app/app/assets/stylesheets/blocks/controls.css.sass +7 -0
  47. data/spec_app/app/assets/stylesheets/blocks/field_with_errors.css.sass +5 -0
  48. data/spec_app/app/assets/stylesheets/blocks/menu.css.sass +13 -0
  49. data/spec_app/app/assets/stylesheets/blocks/panel.css.sass +8 -0
  50. data/spec_app/app/controllers/application_controller.rb +14 -0
  51. data/spec_app/app/controllers/cards_controller.rb +51 -0
  52. data/spec_app/app/controllers/concerns/.keep +0 -0
  53. data/spec_app/app/controllers/pages_controller.rb +6 -0
  54. data/spec_app/app/helpers/application_helper.rb +2 -0
  55. data/spec_app/app/mailers/.keep +0 -0
  56. data/spec_app/app/models/card.rb +6 -0
  57. data/spec_app/app/models/concerns/.keep +0 -0
  58. data/spec_app/app/models/tests.rb +27 -0
  59. data/spec_app/app/views/cards/_side.html.haml +4 -0
  60. data/spec_app/app/views/cards/index.html.haml +10 -0
  61. data/spec_app/app/views/cards/new.html.haml +15 -0
  62. data/spec_app/app/views/cards/show.html.haml +11 -0
  63. data/spec_app/app/views/layouts/application.html.erb +12 -0
  64. data/spec_app/app/views/pages/home.html.haml +5 -0
  65. data/spec_app/bin/bundle +3 -0
  66. data/spec_app/bin/rails +8 -0
  67. data/spec_app/bin/rake +8 -0
  68. data/spec_app/bin/setup +29 -0
  69. data/spec_app/bin/spring +18 -0
  70. data/spec_app/config/application.rb +26 -0
  71. data/spec_app/config/boot.rb +3 -0
  72. data/spec_app/config/cucumber.yml +8 -0
  73. data/spec_app/config/database.yml +25 -0
  74. data/spec_app/config/environment.rb +5 -0
  75. data/spec_app/config/environments/development.rb +41 -0
  76. data/spec_app/config/environments/production.rb +79 -0
  77. data/spec_app/config/environments/test.rb +42 -0
  78. data/spec_app/config/initializers/assets.rb +11 -0
  79. data/spec_app/config/initializers/backtrace_silencers.rb +7 -0
  80. data/spec_app/config/initializers/cookies_serializer.rb +3 -0
  81. data/spec_app/config/initializers/filter_parameter_logging.rb +4 -0
  82. data/spec_app/config/initializers/inflections.rb +16 -0
  83. data/spec_app/config/initializers/mime_types.rb +4 -0
  84. data/spec_app/config/initializers/session_store.rb +3 -0
  85. data/spec_app/config/initializers/wrap_parameters.rb +14 -0
  86. data/spec_app/config/locales/en.yml +23 -0
  87. data/spec_app/config/routes.rb +6 -0
  88. data/spec_app/config/secrets.yml +22 -0
  89. data/spec_app/config.ru +4 -0
  90. data/spec_app/db/migrate/20141225125143_create_card.rb +9 -0
  91. data/spec_app/db/schema.rb +23 -0
  92. data/spec_app/db/seeds.rb +7 -0
  93. data/spec_app/features/history.feature +30 -0
  94. data/spec_app/features/navigation.feature +9 -0
  95. data/spec_app/features/step_definitions/factory_steps.rb +5 -0
  96. data/spec_app/features/step_definitions/navigation_steps.rb +29 -0
  97. data/spec_app/features/step_definitions/utility_steps.rb +15 -0
  98. data/spec_app/features/support/env.rb +73 -0
  99. data/spec_app/features/support/find_by_anything.rb +19 -0
  100. data/spec_app/features/support/paths.rb +51 -0
  101. data/spec_app/lib/assets/.keep +0 -0
  102. data/spec_app/lib/tasks/.keep +0 -0
  103. data/spec_app/lib/tasks/cucumber.rake +65 -0
  104. data/spec_app/log/.keep +0 -0
  105. data/spec_app/public/404.html +67 -0
  106. data/spec_app/public/422.html +67 -0
  107. data/spec_app/public/500.html +66 -0
  108. data/spec_app/public/favicon.ico +0 -0
  109. data/spec_app/public/robots.txt +5 -0
  110. data/spec_app/script/cucumber +10 -0
  111. data/spec_app/test/controllers/.keep +0 -0
  112. data/spec_app/test/fixtures/.keep +0 -0
  113. data/spec_app/test/helpers/.keep +0 -0
  114. data/spec_app/test/integration/.keep +0 -0
  115. data/spec_app/test/mailers/.keep +0 -0
  116. data/spec_app/test/models/.keep +0 -0
  117. data/spec_app/test/test_helper.rb +10 -0
  118. data/spec_app/vendor/assets/javascripts/.keep +0 -0
  119. data/spec_app/vendor/assets/stylesheets/.keep +0 -0
  120. data/upjs-rails.gemspec +24 -0
  121. 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ 2.1.2
2
+
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
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ yuidoc -c doc/yuidoc.json --server 3001
data/bin/install-ydoc ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+
3
+ npm install -g yuidocjs
4
+
@@ -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,9 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+ <a href="/terms" up-modal=".main">
@@ -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