upjs-rails 0.4.1 → 0.4.2
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 +4 -4
- data/README.md +1 -3
- data/lib/assets/javascripts/up/flow.js.coffee +49 -36
- data/lib/assets/javascripts/up/form.js.coffee +2 -2
- data/lib/assets/javascripts/up/link.js.coffee +19 -12
- data/lib/assets/javascripts/up/magic.js.coffee +17 -1
- data/lib/assets/javascripts/up/modal.js.coffee +12 -0
- data/lib/assets/javascripts/up/motion.js.coffee +10 -9
- data/lib/assets/javascripts/up/navigation.js.coffee +11 -4
- data/lib/assets/javascripts/up/popup.js.coffee +12 -3
- data/lib/assets/javascripts/up/proxy.js.coffee +26 -12
- data/lib/assets/javascripts/up/util.js.coffee +14 -2
- data/lib/assets/javascripts/up/viewport.js.coffee +125 -0
- data/lib/assets/javascripts/up.js.coffee +1 -0
- data/lib/upjs/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/trigger.js.coffee +28 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +3 -2
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +37 -0
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +9 -1
- data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +15 -1
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +27 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea22c4f8c05c98c188200595aae81fda36affbc6
|
4
|
+
data.tar.gz: 7cccbe256055dcfefa5036d0ad83df85f32064a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 927e77f26edab9252b32c4c32a04a7a94ba5efd3f1cb8f03fd7dc2920f7f2fca09ca06cdf5b5977d508af7b81aa56f1be67c621d218b77da368303e18df6a195
|
7
|
+
data.tar.gz: a42ec712bd4a7a68b19ec5a813a8e044dc19eeec71e5a9dca989ffde7e90e4e6cecefd9bde8b44cc07d1c4b63c86482f2e8ec9a7241fafd33df5522edf485dd7
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@ To run Jasmine tests:
|
|
20
20
|
- Install Ruby 2.1.2
|
21
21
|
- `cd` into `spec_app`
|
22
22
|
- Install dependencies by running `bundle install`
|
23
|
+
- Migrate
|
23
24
|
- Start the Rails server
|
24
25
|
- Access `http://localhost:3000/specs`
|
25
26
|
|
@@ -44,6 +45,3 @@ Now make the release for manual download and bower:
|
|
44
45
|
- From the project root, type `rake assets:compile`
|
45
46
|
- This will output minified JS and CSS files to the `dist` folder
|
46
47
|
- Commit and push the generated files
|
47
|
-
|
48
|
-
|
49
|
-
|
@@ -48,6 +48,7 @@ up.flow = (->
|
|
48
48
|
If set to `false`, the history will remain unchanged.
|
49
49
|
@param {String|Boolean} [options.source=true]
|
50
50
|
@param {String} [options.transition]
|
51
|
+
@param {String} [options.scroll='body']
|
51
52
|
@param {String} [options.historyMethod='push']
|
52
53
|
###
|
53
54
|
replace = (selectorOrElement, url, options) ->
|
@@ -102,6 +103,7 @@ up.flow = (->
|
|
102
103
|
@param {String} [options.title]
|
103
104
|
@param {String} [options.source]
|
104
105
|
@param {Object} [options.transition]
|
106
|
+
@param {String} [options.scroll='body']
|
105
107
|
@param {String} [options.history]
|
106
108
|
@param {String} [options.historyMethod='push']
|
107
109
|
###
|
@@ -111,38 +113,55 @@ up.flow = (->
|
|
111
113
|
historyMethod: 'push'
|
112
114
|
)
|
113
115
|
|
114
|
-
if options.history
|
116
|
+
if u.castsToFalse(options.history)
|
115
117
|
options.history = null
|
116
|
-
|
118
|
+
|
119
|
+
if u.castsToFalse(options.scroll)
|
120
|
+
options.scroll = null
|
121
|
+
|
117
122
|
options.source = u.option(options.source, options.history)
|
118
|
-
|
123
|
+
|
124
|
+
response = parseResponse(html)
|
125
|
+
|
126
|
+
options.title ||= response.title()
|
127
|
+
|
128
|
+
for step in parseImplantSteps(selector, options)
|
129
|
+
$old = findOldFragment(step.selector)
|
130
|
+
$new = response.find(step.selector)
|
131
|
+
prepareForReplacement($old, options).then ->
|
132
|
+
swapElements($old, $new, step.pseudoClass, step.transition, options)
|
133
|
+
|
134
|
+
findOldFragment = (selector) ->
|
135
|
+
u.presence($(".up-popup " + selector)) ||
|
136
|
+
u.presence($(".up-modal " + selector)) ||
|
137
|
+
u.presence($(selector)) ||
|
138
|
+
u.error('Could not find selector %o in current body HTML', selector)
|
139
|
+
|
140
|
+
parseResponse = (html) ->
|
119
141
|
# jQuery cannot construct transient elements that contain <html> or <body> tags,
|
120
142
|
# so we're using the native browser API to grep through the HTML
|
121
143
|
htmlElement = u.createElementFromHtml(html)
|
144
|
+
title: -> htmlElement.querySelector("title")?.textContent
|
145
|
+
find: (selector) ->
|
146
|
+
if child = htmlElement.querySelector(selector)
|
147
|
+
$(child)
|
148
|
+
else
|
149
|
+
u.error("Could not find selector %o in response %o", selector, html)
|
122
150
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
151
|
+
prepareForReplacement = ($element, options) ->
|
152
|
+
# Before we select a replacement target, ensure that all transitions
|
153
|
+
# and animations have been run. Finishing a transition usually removes
|
154
|
+
# the element that is being morphed, so it will affect further selections
|
155
|
+
# using the same selector.
|
156
|
+
up.motion.finish($element)
|
157
|
+
reveal($element, options.scroll)
|
127
158
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
159
|
+
reveal = ($element, view) ->
|
160
|
+
if view
|
161
|
+
up.reveal($element, view: view)
|
162
|
+
else
|
163
|
+
u.resolvedDeferred()
|
133
164
|
|
134
|
-
$old =
|
135
|
-
# always prefer to replace content in popups or modals
|
136
|
-
u.presence($(".up-popup " + step.selector)) ||
|
137
|
-
u.presence($(".up-modal " + step.selector)) ||
|
138
|
-
u.presence($(step.selector)) ||
|
139
|
-
u.error('Could not find selector %o in current body HTML', step.selector)
|
140
|
-
if fragment = htmlElement.querySelector(step.selector)
|
141
|
-
$new = $(fragment)
|
142
|
-
swapElements $old, $new, step.pseudoClass, step.transition, options
|
143
|
-
else
|
144
|
-
u.error("Could not find selector %o in response %o", step.selector, html)
|
145
|
-
|
146
165
|
elementsInserted = ($new, options) ->
|
147
166
|
options.insert?($new)
|
148
167
|
if options.history
|
@@ -177,13 +196,16 @@ up.flow = (->
|
|
177
196
|
# Wrap the replacement as a destroy animation, so $old will
|
178
197
|
# get marked as .up-destroying right away.
|
179
198
|
destroy $old, animation: ->
|
180
|
-
|
199
|
+
# Don't insert the new element after the old element.
|
200
|
+
# For some reason this will make the browser scroll to the
|
201
|
+
# bottom of the new element.
|
202
|
+
$new.insertBefore($old)
|
181
203
|
elementsInserted($new, options)
|
182
204
|
if $old.is('body') && transition != 'none'
|
183
205
|
u.error('Cannot apply transitions to body-elements (%o)', transition)
|
184
206
|
up.morph($old, $new, transition)
|
185
207
|
|
186
|
-
|
208
|
+
parseImplantSteps = (selector, options) ->
|
187
209
|
transitionString = options.transition || options.animation || 'none'
|
188
210
|
comma = /\ *,\ */
|
189
211
|
disjunction = selector.split(comma)
|
@@ -201,16 +223,7 @@ up.flow = (->
|
|
201
223
|
$control = u.findWithSelf($element, selector)
|
202
224
|
if $control.length && $control.get(0) != document.activeElement
|
203
225
|
$control.focus()
|
204
|
-
|
205
|
-
# executeScripts = ($new) ->
|
206
|
-
# $new.find('script').each ->
|
207
|
-
# $script = $(this)
|
208
|
-
# type = $script.attr('type')
|
209
|
-
# if u.isBlank(type) || type.indexOf('text/javascript') == 0
|
210
|
-
# code = $script.text()
|
211
|
-
# console.log("Evaling javascript code", code)
|
212
|
-
# eval(code)
|
213
|
-
|
226
|
+
|
214
227
|
###*
|
215
228
|
Destroys the given element or selector.
|
216
229
|
Takes care that all destructors, if any, are called.
|
@@ -82,7 +82,7 @@ up.form = (->
|
|
82
82
|
|
83
83
|
successUrl = (xhr) ->
|
84
84
|
url = if historyOption
|
85
|
-
if historyOption
|
85
|
+
if u.castsToFalse(historyOption)
|
86
86
|
false
|
87
87
|
else if u.isString(historyOption)
|
88
88
|
historyOption
|
@@ -165,7 +165,7 @@ up.form = (->
|
|
165
165
|
check = ->
|
166
166
|
value = $field.val()
|
167
167
|
# don't run the callback for the check during initialization
|
168
|
-
skipCallback =
|
168
|
+
skipCallback = u.isNull(knownValue)
|
169
169
|
if knownValue != value
|
170
170
|
knownValue = value
|
171
171
|
unless skipCallback
|
@@ -109,6 +109,9 @@ up.link = (->
|
|
109
109
|
or to `body` if such an attribute does not exist.
|
110
110
|
@param {Function|String} [options.transition]
|
111
111
|
A transition function or name.
|
112
|
+
@param {Element|jQuery|String} scroll
|
113
|
+
An element or selector that will be scrolled to the top in
|
114
|
+
case the replaced element is not visible in the viewport.
|
112
115
|
###
|
113
116
|
follow = (link, options) ->
|
114
117
|
$link = $(link)
|
@@ -118,6 +121,7 @@ up.link = (->
|
|
118
121
|
selector = u.option(options.target, $link.attr('up-target'), 'body')
|
119
122
|
options.transition = u.option(options.transition, $link.attr('up-transition'), $link.attr('up-animation'))
|
120
123
|
options.history = u.option(options.history, $link.attr('up-history'))
|
124
|
+
options.scroll = u.option(options.history, $link.attr('up-scroll'), 'body')
|
121
125
|
|
122
126
|
up.replace(selector, url, options)
|
123
127
|
|
@@ -160,11 +164,11 @@ up.link = (->
|
|
160
164
|
follow($link)
|
161
165
|
|
162
166
|
up.on 'mousedown', 'a[up-target][up-instant]', (event, $link) ->
|
163
|
-
if event
|
167
|
+
if activeInstantLink(event, $link)
|
164
168
|
event.preventDefault()
|
165
|
-
follow($link)
|
169
|
+
up.follow($link)
|
166
170
|
|
167
|
-
|
171
|
+
###*
|
168
172
|
@method up.link.childClicked
|
169
173
|
@private
|
170
174
|
###
|
@@ -173,6 +177,9 @@ up.link = (->
|
|
173
177
|
$targetLink = $target.closest('a, [up-follow]')
|
174
178
|
$targetLink.length && $link.find($targetLink).length
|
175
179
|
|
180
|
+
activeInstantLink = (event, $link) ->
|
181
|
+
u.isUnmodifiedMouseEvent(event) && !childClicked(event, $link)
|
182
|
+
|
176
183
|
###*
|
177
184
|
If applied on a link, Follows this link via AJAX and replaces the
|
178
185
|
current `<body>` element with the response's `<body>` element
|
@@ -206,19 +213,19 @@ up.link = (->
|
|
206
213
|
@param up-instant
|
207
214
|
If set, fetches the element on `mousedown` instead of `click`.
|
208
215
|
###
|
209
|
-
up.on 'click', '[up-follow]', (event, $
|
210
|
-
unless childClicked(event, $
|
216
|
+
up.on 'click', '[up-follow]', (event, $link) ->
|
217
|
+
unless childClicked(event, $link)
|
211
218
|
event.preventDefault()
|
212
219
|
# Check if the event was already triggered by `mousedown`
|
213
|
-
unless $
|
214
|
-
follow(resolve($
|
220
|
+
unless $link.is('[up-instant]')
|
221
|
+
follow(resolve($link))
|
215
222
|
|
216
|
-
up.on 'mousedown', '[up-follow][up-instant]', (event, $
|
217
|
-
if
|
223
|
+
up.on 'mousedown', '[up-follow][up-instant]', (event, $link) ->
|
224
|
+
if activeInstantLink(event, $link)
|
218
225
|
event.preventDefault()
|
219
|
-
follow(resolve($
|
226
|
+
up.follow(resolve($link))
|
220
227
|
|
221
|
-
|
228
|
+
###*
|
222
229
|
Marks up the current link to be followed *as fast as possible*.
|
223
230
|
This is done by:
|
224
231
|
|
@@ -232,7 +239,7 @@ up.link = (->
|
|
232
239
|
|
233
240
|
Note that this is shorthand for:
|
234
241
|
|
235
|
-
|
242
|
+
<a href="/users" up-target=".main" up-instant up-preload>User list</a>
|
236
243
|
|
237
244
|
You can also apply `[up-dash]` to any element that contains a link
|
238
245
|
in order to enlarge the link's click area:
|
@@ -113,7 +113,7 @@ up.magic = (->
|
|
113
113
|
destroyer = $element.data(DESTROYER_KEY)
|
114
114
|
destroyer()
|
115
115
|
|
116
|
-
|
116
|
+
###*
|
117
117
|
Checks if the given element has an `up-data` attribute.
|
118
118
|
If yes, parses the attribute value as JSON and returns the parsed object.
|
119
119
|
|
@@ -126,6 +126,22 @@ up.magic = (->
|
|
126
126
|
@method up.magic.data
|
127
127
|
@param {String|Element|jQuery} elementOrSelector
|
128
128
|
###
|
129
|
+
|
130
|
+
###
|
131
|
+
Stores a JSON-string with the element.
|
132
|
+
|
133
|
+
If an element annotated with [`up-data`] is inserted into the DOM,
|
134
|
+
Up will parse the JSON and pass the resulting object to any matching
|
135
|
+
[`up.awaken`](/up.magic#up.magic.awaken) handlers.
|
136
|
+
|
137
|
+
Similarly, when an event is triggered on an element annotated with
|
138
|
+
[`up-data`], the parsed object will be passed to any matching
|
139
|
+
[`up.on`](/up.magic#up.on) handlers.
|
140
|
+
|
141
|
+
@ujs
|
142
|
+
@method [up-data]
|
143
|
+
@param {JSON} [up-data]
|
144
|
+
###
|
129
145
|
data = (elementOrSelector) ->
|
130
146
|
$element = $(elementOrSelector)
|
131
147
|
json = $element.attr('up-data')
|
@@ -56,6 +56,16 @@ up.modal = (->
|
|
56
56
|
else
|
57
57
|
template
|
58
58
|
|
59
|
+
rememberHistory = ->
|
60
|
+
$popup = $('.up-modal')
|
61
|
+
$popup.attr('up-previous-url', up.browser.url())
|
62
|
+
$popup.attr('up-previous-title', document.title)
|
63
|
+
|
64
|
+
discardHistory = ->
|
65
|
+
$popup = $('.up-modal')
|
66
|
+
$popup.removeAttr('up-previous-url')
|
67
|
+
$popup.removeAttr('up-previous-title')
|
68
|
+
|
59
69
|
createHiddenModal = (selector, width, height, sticky) ->
|
60
70
|
$modal = $(templateHtml())
|
61
71
|
$modal.attr('up-sticky', '') if sticky
|
@@ -68,6 +78,7 @@ up.modal = (->
|
|
68
78
|
$placeholder = u.$createElementFromSelector(selector)
|
69
79
|
$placeholder.appendTo($content)
|
70
80
|
$modal.appendTo(document.body)
|
81
|
+
rememberHistory()
|
71
82
|
$modal.hide()
|
72
83
|
$modal
|
73
84
|
|
@@ -143,6 +154,7 @@ up.modal = (->
|
|
143
154
|
|
144
155
|
autoclose = ->
|
145
156
|
unless $('.up-modal').is('[up-sticky]')
|
157
|
+
discardHistory()
|
146
158
|
close()
|
147
159
|
|
148
160
|
###*
|
@@ -22,22 +22,22 @@ We need to work on this page:
|
|
22
22
|
up.motion = (->
|
23
23
|
|
24
24
|
u = up.util
|
25
|
-
|
26
|
-
config =
|
27
|
-
duration: 300
|
28
|
-
delay: 0
|
29
|
-
easing: 'ease'
|
30
25
|
|
31
26
|
animations = {}
|
32
27
|
defaultAnimations = {}
|
33
28
|
transitions = {}
|
34
29
|
defaultTransitions = {}
|
35
30
|
|
31
|
+
config =
|
32
|
+
duration: 300
|
33
|
+
delay: 0
|
34
|
+
easing: 'ease'
|
35
|
+
|
36
36
|
###*
|
37
37
|
@method up.modal.defaults
|
38
|
-
@param {Number} options.duration
|
39
|
-
@param {Number} options.delay
|
40
|
-
@param {String} options.easing
|
38
|
+
@param {Number} [options.duration]
|
39
|
+
@param {Number} [options.delay]
|
40
|
+
@param {String} [options.easing]
|
41
41
|
###
|
42
42
|
defaults = (options) ->
|
43
43
|
u.extend(config, options)
|
@@ -117,13 +117,14 @@ up.motion = (->
|
|
117
117
|
|
118
118
|
promise
|
119
119
|
|
120
|
-
|
120
|
+
###*
|
121
121
|
Completes all animations and transitions for the given element
|
122
122
|
by jumping to the last animation frame instantly. All callbacks chained to
|
123
123
|
the original animation's promise will be called.
|
124
124
|
|
125
125
|
Does nothing if the given element is not currently animating.
|
126
126
|
|
127
|
+
@method up.motion.finish
|
127
128
|
@param {Element|jQuery|String} elementOrSelector
|
128
129
|
###
|
129
130
|
finish = (elementOrSelector) ->
|
@@ -29,7 +29,9 @@ up.navigation = (->
|
|
29
29
|
|
30
30
|
CLASS_ACTIVE = 'up-active'
|
31
31
|
CLASS_CURRENT = 'up-current'
|
32
|
-
|
32
|
+
SELECTORS_SECTION = ['a[href]', 'a[up-target]', '[up-follow]', '[up-modal]', '[up-popup]', '[up-href]']
|
33
|
+
SELECTOR_SECTION = SELECTORS_SECTION.join(', ')
|
34
|
+
SELECTOR_SECTION_INSTANT = ("#{selector}[up-instant]" for selector in SELECTORS_SECTION).join(', ')
|
33
35
|
SELECTOR_ACTIVE = ".#{CLASS_ACTIVE}"
|
34
36
|
|
35
37
|
normalizeUrl = (url) ->
|
@@ -42,7 +44,7 @@ up.navigation = (->
|
|
42
44
|
sectionUrls = ($section) ->
|
43
45
|
urls = []
|
44
46
|
if $link = up.link.resolve($section)
|
45
|
-
for attr in ['href', 'up-follow', 'up-
|
47
|
+
for attr in ['href', 'up-follow', 'up-href']
|
46
48
|
if url = u.presentAttr($link, attr)
|
47
49
|
url = normalizeUrl(url)
|
48
50
|
urls.push(url)
|
@@ -77,8 +79,13 @@ up.navigation = (->
|
|
77
79
|
$(SELECTOR_ACTIVE).removeClass(CLASS_ACTIVE)
|
78
80
|
|
79
81
|
up.on 'click', SELECTOR_SECTION, (event, $section) ->
|
80
|
-
|
81
|
-
|
82
|
+
unless $section.is('[up-instant]')
|
83
|
+
sectionClicked($section)
|
84
|
+
|
85
|
+
up.on 'mousedown', SELECTOR_SECTION_INSTANT, (event, $section) ->
|
86
|
+
if u.isUnmodifiedMouseEvent(event)
|
87
|
+
sectionClicked($section)
|
88
|
+
|
82
89
|
# When a fragment is ready it might either have brought a location change
|
83
90
|
# with it, or it might have opened a modal / popup which we consider
|
84
91
|
# to be secondary location sources (the primary being the browser's
|
@@ -84,16 +84,24 @@ up.popup = (->
|
|
84
84
|
$popup.css('top', top - errorY)
|
85
85
|
else if bottom = parseInt($popup.css('bottom'))
|
86
86
|
$popup.css('bottom', bottom + errorY)
|
87
|
-
|
87
|
+
|
88
|
+
rememberHistory = ->
|
89
|
+
$popup = $('.up-popup')
|
90
|
+
$popup.attr('up-previous-url', up.browser.url())
|
91
|
+
$popup.attr('up-previous-title', document.title)
|
92
|
+
|
93
|
+
discardHistory = ->
|
94
|
+
$popup = $('.up-popup')
|
95
|
+
$popup.removeAttr('up-previous-url')
|
96
|
+
$popup.removeAttr('up-previous-title')
|
88
97
|
|
89
98
|
createHiddenPopup = ($link, selector, sticky) ->
|
90
99
|
$popup = u.$createElementFromSelector('.up-popup')
|
91
100
|
$popup.attr('up-sticky', '') if sticky
|
92
|
-
$popup.attr('up-previous-url', up.browser.url())
|
93
|
-
$popup.attr('up-previous-title', document.title)
|
94
101
|
$placeholder = u.$createElementFromSelector(selector)
|
95
102
|
$placeholder.appendTo($popup)
|
96
103
|
$popup.appendTo(document.body)
|
104
|
+
rememberHistory()
|
97
105
|
$popup.hide()
|
98
106
|
$popup
|
99
107
|
|
@@ -204,6 +212,7 @@ up.popup = (->
|
|
204
212
|
|
205
213
|
up.bus.on('fragment:ready', ($fragment) ->
|
206
214
|
unless $fragment.closest('.up-popup').length
|
215
|
+
discardHistory()
|
207
216
|
autoclose()
|
208
217
|
)
|
209
218
|
|
@@ -3,10 +3,12 @@ Caching and preloading
|
|
3
3
|
======================
|
4
4
|
|
5
5
|
All HTTP requests go through the Up.js proxy.
|
6
|
-
It caches a limited number
|
6
|
+
It caches a [limited](/up.proxy#up.proxy.defaults) number of server responses
|
7
|
+
for a [limited](/up.proxy#up.proxy.defaults) amount of time,
|
8
|
+
making requests to these URLs return insantly.
|
7
9
|
|
8
|
-
The cache is cleared whenever the user makes a non
|
9
|
-
(like `POST`, `PUT
|
10
|
+
The cache is cleared whenever the user makes a non-`GET` request
|
11
|
+
(like `POST`, `PUT` or `DELETE`).
|
10
12
|
|
11
13
|
The proxy can also used to speed up reaction times by preloading
|
12
14
|
links when the user hovers over the click area (or puts the mouse/finger
|
@@ -18,15 +20,15 @@ response will already be cached when the user performs the click.
|
|
18
20
|
up.proxy = (->
|
19
21
|
|
20
22
|
config =
|
21
|
-
preloadDelay:
|
23
|
+
preloadDelay: 75
|
22
24
|
cacheSize: 70
|
23
25
|
cacheExpiry: 1000 * 60 * 5
|
24
26
|
|
25
27
|
###*
|
26
28
|
@method up.proxy.defaults
|
27
|
-
@param {Number} [preloadDelay]
|
28
|
-
@param {Number} [cacheSize]
|
29
|
-
@param {Number} [cacheExpiry]
|
29
|
+
@param {Number} [options.preloadDelay]
|
30
|
+
@param {Number} [options.cacheSize]
|
31
|
+
@param {Number} [options.cacheExpiry]
|
30
32
|
The number of milliseconds until a cache entry expires.
|
31
33
|
###
|
32
34
|
defaults = (options) ->
|
@@ -76,11 +78,19 @@ up.proxy = (->
|
|
76
78
|
if promise = get(oldRequest)
|
77
79
|
set(newRequest, promise)
|
78
80
|
|
79
|
-
|
81
|
+
###*
|
82
|
+
Makes a request to the given URL and caches the response.
|
83
|
+
If the response was already cached, returns the HTML instantly.
|
84
|
+
|
85
|
+
If requesting a URL that is not read-only, the response will
|
86
|
+
not be cached and the entire cache will be cleared.
|
87
|
+
Only requests with a method of `GET`, `OPTIONS` and `HEAD`
|
88
|
+
are considered to be read-only.
|
89
|
+
|
80
90
|
@method up.proxy.ajax
|
81
|
-
@param {String}
|
82
|
-
@param {String} [
|
83
|
-
@param {String} [
|
91
|
+
@param {String} request.url
|
92
|
+
@param {String} [request.method='GET']
|
93
|
+
@param {String} [request.selector]
|
84
94
|
###
|
85
95
|
ajax = (request) ->
|
86
96
|
if !isIdempotent(request)
|
@@ -168,7 +178,7 @@ up.proxy = (->
|
|
168
178
|
|
169
179
|
up.bus.on 'framework:reset', reset
|
170
180
|
|
171
|
-
|
181
|
+
###*
|
172
182
|
Links with an `up-preload` attribute will silently fetch their target
|
173
183
|
when the user hovers over the click area, or when the user puts her
|
174
184
|
mouse/finger down (before releasing). This way the
|
@@ -176,6 +186,10 @@ up.proxy = (->
|
|
176
186
|
making the interaction feel instant.
|
177
187
|
|
178
188
|
@method [up-preload]
|
189
|
+
@param [[up-delay]=50]
|
190
|
+
The number of milliseconds to wait between hovering
|
191
|
+
and preloading. Increasing this will lower the load in your server,
|
192
|
+
but will also make the interaction feel less instant.
|
179
193
|
@ujs
|
180
194
|
###
|
181
195
|
up.on 'mouseover mousedown touchstart', '[up-preload]', (event, $element) ->
|
@@ -58,7 +58,7 @@ up.util = (->
|
|
58
58
|
normalized += anchor.search unless options?.search == false
|
59
59
|
normalized
|
60
60
|
|
61
|
-
|
61
|
+
###*
|
62
62
|
@method up.util.normalizeMethod
|
63
63
|
@protected
|
64
64
|
###
|
@@ -436,13 +436,17 @@ up.util = (->
|
|
436
436
|
|
437
437
|
ANIMATION_PROMISE_KEY = 'up-animation-promise'
|
438
438
|
|
439
|
-
|
439
|
+
###*
|
440
440
|
Completes the animation for the given element by jumping
|
441
441
|
to the last frame instantly. All callbacks chained to
|
442
442
|
the original animation's promise will be called.
|
443
443
|
|
444
444
|
Does nothing if the given element is not currently animating.
|
445
445
|
|
446
|
+
Also see [`up.motion.finish`](/up.motion#up.motion.finish).
|
447
|
+
|
448
|
+
@method up.util.finishCssAnimate
|
449
|
+
@protected
|
446
450
|
@param {Element|jQuery|String} elementOrSelector
|
447
451
|
###
|
448
452
|
finishCssAnimate = (elementOrSelector) ->
|
@@ -523,6 +527,12 @@ up.util = (->
|
|
523
527
|
filtered[key] = object[key]
|
524
528
|
filtered
|
525
529
|
|
530
|
+
isUnmodifiedKeyEvent = (event) ->
|
531
|
+
not (event.metaKey or event.shiftKey or event.ctrlKey)
|
532
|
+
|
533
|
+
isUnmodifiedMouseEvent = (event) ->
|
534
|
+
event.button is 0 and isUnmodifiedKeyEvent(event)
|
535
|
+
|
526
536
|
resolvedDeferred = ->
|
527
537
|
deferred = $.Deferred()
|
528
538
|
deferred.resolve()
|
@@ -615,6 +625,8 @@ up.util = (->
|
|
615
625
|
isDeferred: isDeferred
|
616
626
|
isHash: isHash
|
617
627
|
ifGiven: ifGiven
|
628
|
+
isUnmodifiedKeyEvent: isUnmodifiedKeyEvent
|
629
|
+
isUnmodifiedMouseEvent: isUnmodifiedMouseEvent
|
618
630
|
unwrap: unwrap
|
619
631
|
nextFrame: nextFrame
|
620
632
|
measure: measure
|
@@ -0,0 +1,125 @@
|
|
1
|
+
###*
|
2
|
+
Viewport scrolling
|
3
|
+
==================
|
4
|
+
|
5
|
+
This modules contains functions to scroll the viewport and reveal contained elements.
|
6
|
+
|
7
|
+
By default Up.js will always scroll to an element before updating it.
|
8
|
+
|
9
|
+
@class up.viewport
|
10
|
+
###
|
11
|
+
up.viewport = (->
|
12
|
+
|
13
|
+
u = up.util
|
14
|
+
|
15
|
+
config =
|
16
|
+
duration: 0
|
17
|
+
view: 'body'
|
18
|
+
easing: 'swing'
|
19
|
+
|
20
|
+
###*
|
21
|
+
@method up.viewport.defaults
|
22
|
+
@param {Number} [options.duration]
|
23
|
+
@param {String} [options.easing]
|
24
|
+
@param {Number} [options.padding]
|
25
|
+
@param {String|Element|jQuery} [options.view]
|
26
|
+
###
|
27
|
+
defaults = (options) ->
|
28
|
+
u.extend(config, options)
|
29
|
+
|
30
|
+
SCROLL_PROMISE_KEY = 'up-scroll-promise'
|
31
|
+
|
32
|
+
###*
|
33
|
+
@method up.scroll
|
34
|
+
@param {String|Element|jQuery} viewOrSelector
|
35
|
+
@param {Number} scrollPos
|
36
|
+
@param {String}[options.duration]
|
37
|
+
@param {String}[options.easing]
|
38
|
+
@returns {Deferred}
|
39
|
+
@protected
|
40
|
+
###
|
41
|
+
scroll = (viewOrSelector, scrollPos, options) ->
|
42
|
+
$view = $(viewOrSelector)
|
43
|
+
options = u.options(options)
|
44
|
+
duration = u.option(options.duration, config.duration)
|
45
|
+
easing = u.option(options.easing, config.easing)
|
46
|
+
|
47
|
+
finishScrolling($view)
|
48
|
+
|
49
|
+
if duration > 0
|
50
|
+
deferred = $.Deferred()
|
51
|
+
|
52
|
+
$view.data(SCROLL_PROMISE_KEY, deferred)
|
53
|
+
deferred.then ->
|
54
|
+
$view.removeData(SCROLL_PROMISE_KEY)
|
55
|
+
$view.finish()
|
56
|
+
|
57
|
+
targetProps =
|
58
|
+
scrollTop: scrollPos
|
59
|
+
|
60
|
+
$view.animate targetProps,
|
61
|
+
duration: duration,
|
62
|
+
easing: easing,
|
63
|
+
complete: -> deferred.resolve()
|
64
|
+
|
65
|
+
deferred
|
66
|
+
else
|
67
|
+
$view.scrollTop(scrollPos)
|
68
|
+
u.resolvedDeferred()
|
69
|
+
|
70
|
+
###*
|
71
|
+
@method up.viewport.finishScrolling
|
72
|
+
@private
|
73
|
+
###
|
74
|
+
finishScrolling = (elementOrSelector) ->
|
75
|
+
$(elementOrSelector).each ->
|
76
|
+
if existingScrolling = $(this).data(SCROLL_PROMISE_KEY)
|
77
|
+
existingScrolling.resolve()
|
78
|
+
|
79
|
+
###*
|
80
|
+
@method up.reveal
|
81
|
+
@param {String|Element|jQuery} element
|
82
|
+
@param {String|Element|jQuery} [options.view]
|
83
|
+
@param {Number} [options.duration]
|
84
|
+
@param {String} [options.easing]
|
85
|
+
@param {Number} [options.padding]
|
86
|
+
@returns {Deferred}
|
87
|
+
@protected
|
88
|
+
###
|
89
|
+
reveal = (elementOrSelector, options) ->
|
90
|
+
|
91
|
+
options = u.options(options)
|
92
|
+
view = u.option(options.view, config.view)
|
93
|
+
padding = u.option(options.padding, config.padding)
|
94
|
+
|
95
|
+
$element = $(elementOrSelector)
|
96
|
+
$view = $(view)
|
97
|
+
|
98
|
+
viewHeight = $view.height()
|
99
|
+
scrollPos = $view.scrollTop()
|
100
|
+
|
101
|
+
firstVisibleRow = scrollPos
|
102
|
+
lastVisibleRow = scrollPos + viewHeight
|
103
|
+
|
104
|
+
elementTop = $element.position().top
|
105
|
+
|
106
|
+
elementTooHigh = elementTop - padding < firstVisibleRow
|
107
|
+
elementTooLow = elementTop > lastVisibleRow - padding
|
108
|
+
|
109
|
+
if elementTooHigh || elementTooLow
|
110
|
+
scrollPos = elementTop - padding
|
111
|
+
scrollPos = Math.max(scrollPos, 0)
|
112
|
+
scrollPos = Math.min(scrollPos, viewHeight - 1)
|
113
|
+
scroll($view, scrollPos, options)
|
114
|
+
else
|
115
|
+
u.resolvedDeferred()
|
116
|
+
|
117
|
+
reveal: reveal
|
118
|
+
scroll: scroll
|
119
|
+
finishScrolling: finishScrolling
|
120
|
+
defaults: defaults
|
121
|
+
|
122
|
+
)()
|
123
|
+
|
124
|
+
up.scroll = up.viewport.scroll
|
125
|
+
up.reveal = up.viewport.reveal
|
data/lib/upjs/rails/version.rb
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
@Trigger = (->
|
2
|
+
|
3
|
+
u = up.util
|
4
|
+
|
5
|
+
mousedown = ($element, options) ->
|
6
|
+
options = u.options(options, view: window, cancelable: true, bubbles: true)
|
7
|
+
event = new MouseEvent('mousedown', options)
|
8
|
+
dispatch($element, event)
|
9
|
+
|
10
|
+
mouseup = ($element, options) ->
|
11
|
+
options = u.options(options, view: window, cancelable: true, bubbles: true)
|
12
|
+
event = new MouseEvent('mouseup', options)
|
13
|
+
dispatch($element, event)
|
14
|
+
|
15
|
+
click = ($element, options) ->
|
16
|
+
options = u.options(options, view: window, cancelable: true, bubbles: true)
|
17
|
+
event = new MouseEvent('click', options)
|
18
|
+
dispatch($element, event)
|
19
|
+
|
20
|
+
dispatch = ($element, event) ->
|
21
|
+
$element.each ->
|
22
|
+
this.dispatchEvent(event)
|
23
|
+
|
24
|
+
mousedown: mousedown
|
25
|
+
mouseup: mouseup
|
26
|
+
click: click
|
27
|
+
|
28
|
+
)()
|
@@ -35,7 +35,7 @@ describe 'up.flow', ->
|
|
35
35
|
expect($('.before')).toHaveText('old-before')
|
36
36
|
expect($('.middle')).toHaveText('new-middle')
|
37
37
|
expect($('.after')).toHaveText('old-after')
|
38
|
-
done()
|
38
|
+
done()
|
39
39
|
|
40
40
|
it 'should set the browser location to the given URL', (done) ->
|
41
41
|
@request = up.replace('.middle', '/path')
|
@@ -55,4 +55,41 @@ describe 'up.link', ->
|
|
55
55
|
|
56
56
|
it 'should have tests'
|
57
57
|
|
58
|
+
describe '[up-instant]', ->
|
59
|
+
|
60
|
+
beforeEach ->
|
61
|
+
@$link = affix('a[href="/path"][up-follow][up-instant]')
|
62
|
+
spyOn(up, 'follow')
|
63
|
+
|
64
|
+
it 'follows an [up-follow] link on mousedown (instead of on click)', ->
|
65
|
+
Trigger.mousedown(@$link)
|
66
|
+
expect(up.follow.calls.mostRecent().args[0]).toEqual(@$link)
|
67
|
+
|
68
|
+
it 'follows an [up-target] link on mousedown (instead of on click)', ->
|
69
|
+
Trigger.mousedown(@$link)
|
70
|
+
expect(up.follow.calls.mostRecent().args[0]).toEqual(@$link)
|
71
|
+
|
72
|
+
it 'does nothing on mouseup', ->
|
73
|
+
Trigger.mouseup(@$link)
|
74
|
+
expect(up.follow).not.toHaveBeenCalled()
|
75
|
+
|
76
|
+
it 'does nothing on click', ->
|
77
|
+
Trigger.click(@$link)
|
78
|
+
expect(up.follow).not.toHaveBeenCalled()
|
79
|
+
|
80
|
+
it 'does nothing if the right mouse button is pressed down', ->
|
81
|
+
Trigger.mousedown(@$link, button: 2)
|
82
|
+
expect(up.follow).not.toHaveBeenCalled()
|
83
|
+
|
84
|
+
it 'does nothing if shift is pressed during mousedown', ->
|
85
|
+
Trigger.mousedown(@$link, shiftKey: true)
|
86
|
+
expect(up.follow).not.toHaveBeenCalled()
|
87
|
+
|
88
|
+
it 'does nothing if ctrl is pressed during mousedown', ->
|
89
|
+
Trigger.mousedown(@$link, ctrlKey: true)
|
90
|
+
expect(up.follow).not.toHaveBeenCalled()
|
91
|
+
|
92
|
+
it 'does nothing if meta is pressed during mousedown', ->
|
93
|
+
Trigger.mousedown(@$link, metaKey: true)
|
94
|
+
expect(up.follow).not.toHaveBeenCalled()
|
58
95
|
|
@@ -27,4 +27,12 @@ describe 'up.modal', ->
|
|
27
27
|
describe '[up-close]', ->
|
28
28
|
|
29
29
|
it 'should have tests'
|
30
|
-
|
30
|
+
|
31
|
+
describe 'when following links inside a modal', ->
|
32
|
+
|
33
|
+
it 'prefers to replace a selector within the modal'
|
34
|
+
|
35
|
+
it 'auto-closes the modal if a selector behind the modal gets replaced'
|
36
|
+
|
37
|
+
it "doesn't auto-close the modal if a selector behind the modal if the modal is sticky"
|
38
|
+
|
@@ -37,11 +37,25 @@ describe 'up.navigation', ->
|
|
37
37
|
|
38
38
|
it 'changes .up-current marks as the URL changes'
|
39
39
|
|
40
|
-
it 'marks clicked
|
40
|
+
it 'marks clicked links as .up-active until the request finishes', ->
|
41
41
|
$link = affix('a[href="/foo"][up-target=".main"]')
|
42
42
|
affix('.main')
|
43
43
|
jasmine.Ajax.install()
|
44
44
|
$link.click()
|
45
|
+
# console.log($link)
|
46
|
+
expect($link).toHaveClass('up-active')
|
47
|
+
jasmine.Ajax.requests.mostRecent().respondWith
|
48
|
+
status: 200
|
49
|
+
contentType: 'text/html'
|
50
|
+
responseText: '<div class="main">new-text</div>'
|
51
|
+
expect($link).not.toHaveClass('up-active')
|
52
|
+
expect($link).toHaveClass('up-current')
|
53
|
+
|
54
|
+
it 'marks links with [up-instant] on mousedown as .up-active until the request finishes', ->
|
55
|
+
$link = affix('a[href="/foo"][up-instant][up-target=".main"]')
|
56
|
+
affix('.main')
|
57
|
+
jasmine.Ajax.install()
|
58
|
+
Trigger.mousedown($link)
|
45
59
|
expect($link).toHaveClass('up-active')
|
46
60
|
jasmine.Ajax.requests.mostRecent().respondWith
|
47
61
|
status: 200
|
@@ -0,0 +1,27 @@
|
|
1
|
+
describe 'up.proxy', ->
|
2
|
+
|
3
|
+
describe 'Javascript functions', ->
|
4
|
+
|
5
|
+
describe 'up.proxy.preload', ->
|
6
|
+
|
7
|
+
it 'should have tests'
|
8
|
+
|
9
|
+
describe 'up.proxy.ajax', ->
|
10
|
+
|
11
|
+
it 'should have tests'
|
12
|
+
|
13
|
+
describe 'up.proxy.get', ->
|
14
|
+
|
15
|
+
it 'should have tests'
|
16
|
+
|
17
|
+
describe 'up.proxy.set', ->
|
18
|
+
|
19
|
+
it 'should have tests'
|
20
|
+
|
21
|
+
describe 'up.proxy.alias', ->
|
22
|
+
|
23
|
+
it 'should have tests'
|
24
|
+
|
25
|
+
describe 'up.proxy.clear', ->
|
26
|
+
|
27
|
+
it 'should have tests'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: upjs-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henning Koch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- lib/assets/javascripts/up/proxy.js.coffee
|
135
135
|
- lib/assets/javascripts/up/tooltip.js.coffee
|
136
136
|
- lib/assets/javascripts/up/util.js.coffee
|
137
|
+
- lib/assets/javascripts/up/viewport.js.coffee
|
137
138
|
- lib/assets/stylesheets/up.css
|
138
139
|
- lib/assets/stylesheets/up/close.css.sass
|
139
140
|
- lib/assets/stylesheets/up/error.css.sass
|
@@ -226,6 +227,8 @@ files:
|
|
226
227
|
- spec_app/spec/javascripts/helpers/index.js.coffee
|
227
228
|
- spec_app/spec/javascripts/helpers/reset_path.js.coffee
|
228
229
|
- spec_app/spec/javascripts/helpers/reset_up.js.coffee
|
230
|
+
- spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee
|
231
|
+
- spec_app/spec/javascripts/helpers/trigger.js.coffee
|
229
232
|
- spec_app/spec/javascripts/support/jasmine.yml
|
230
233
|
- spec_app/spec/javascripts/up/bus_spec.js.coffee
|
231
234
|
- spec_app/spec/javascripts/up/flow_spec.js.coffee
|
@@ -238,6 +241,7 @@ files:
|
|
238
241
|
- spec_app/spec/javascripts/up/motion_spec.js.coffee
|
239
242
|
- spec_app/spec/javascripts/up/navigation_spec.js.coffee
|
240
243
|
- spec_app/spec/javascripts/up/popup_spec.js.coffee
|
244
|
+
- spec_app/spec/javascripts/up/proxy_spec.js.coffee
|
241
245
|
- spec_app/spec/javascripts/up/tooltip_spec.js.coffee
|
242
246
|
- spec_app/spec/javascripts/up/util_spec.js.coffee
|
243
247
|
- spec_app/test/controllers/.keep
|