upjs-rails 0.8.2 → 0.9.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.
@@ -98,13 +98,16 @@ up.link = (->
98
98
  @method up.visit
99
99
  @param {String} url
100
100
  The URL to visit.
101
+ @param {String} [options.target='body']
102
+ The selector to replace.
103
+ See options for [`up.replace`](/up.flow#up.replace)
101
104
  @param {Object} options
102
105
  See options for [`up.replace`](/up.flow#up.replace)
103
106
  ###
104
107
  visit = (url, options) ->
105
- u.debug "Visiting #{url}"
106
- # options = util.options(options, )
107
- up.replace('body', url, options)
108
+ options = u.options(options)
109
+ selector = u.option(options.target, 'body')
110
+ up.replace(selector, url, options)
108
111
 
109
112
  ###*
110
113
  Follows the given link via AJAX and replaces a CSS selector in the current page
@@ -138,13 +138,6 @@ up.modal = (->
138
138
 
139
139
  Any option attributes for [`a[up-modal]`](#a.up-modal) will be honored.
140
140
 
141
- You can also open a URL directly like this:
142
-
143
- up.modal.open({ url: '/foo', target: '.list' })
144
-
145
- This will request `/foo`, extract the `.list` selector from the response
146
- and open the selected container in a modal dialog.
147
-
148
141
  \#\#\#\# Events
149
142
 
150
143
  - Emits an [event](/up.bus) `modal:open` when the modal
@@ -153,12 +146,8 @@ up.modal = (->
153
146
  animation has finished and the modal contents are fully visible.
154
147
 
155
148
  @method up.modal.open
156
- @param {Element|jQuery|String} [elementOrSelector]
149
+ @param {Element|jQuery|String} elementOrSelector
157
150
  The link to follow.
158
- Can be omitted if you give `options.url` instead.
159
- @param {String} [options.url]
160
- The URL to open.
161
- Can be omitted if you give `elementOrSelector` instead.
162
151
  @param {String} [options.target]
163
152
  The selector to extract from the response and open in a modal dialog.
164
153
  @param {Number} [options.width]
@@ -183,6 +172,26 @@ up.modal = (->
183
172
  @return {Promise}
184
173
  A promise that will be resolved when the modal has finished loading.
185
174
  ###
175
+
176
+ ###*
177
+ Opens a modal for the given URL.
178
+
179
+ Example:
180
+
181
+ up.modal.open({ url: '/foo', target: '.list' })
182
+
183
+ This will request `/foo`, extract the `.list` selector from the response
184
+ and open the selected container in a modal dialog.
185
+
186
+ @method up.modal.open
187
+ @param {String} options.url
188
+ The URL to load.
189
+ @param {String} options.target
190
+ The CSS selector to extract from the response.
191
+ The extracted content will be placed into the dialog window.
192
+ @param {Object} options
193
+ See options for [previous `up.modal.open` variant](#up.modal.open).
194
+ ###
186
195
  open = (args...) ->
187
196
  if u.isObject(args[0]) && !u.isElement(args[0]) && !u.isJQuery(args[0])
188
197
  $link = u.nullJquery()
@@ -63,7 +63,7 @@ up.util = (->
63
63
  # https://gist.github.com/jlong/2428561#comment-1461205
64
64
  anchor.href = anchor.href if isBlank(anchor.hostname)
65
65
  else
66
- anchor = unwrap(urlOrAnchor)
66
+ anchor = unJquery(urlOrAnchor)
67
67
  normalized = anchor.protocol + "//" + anchor.hostname
68
68
  normalized += ":#{anchor.port}" unless isStandardPort(anchor.protocol, anchor.port)
69
69
  pathname = anchor.pathname
@@ -142,7 +142,7 @@ up.util = (->
142
142
  stringifyConsoleArgs = (args) ->
143
143
  message = args[0]
144
144
  i = 0
145
- maxLength = 30
145
+ maxLength = 50
146
146
  message.replace CONSOLE_PLACEHOLDERS, ->
147
147
  i += 1
148
148
  arg = args[i]
@@ -150,11 +150,13 @@ up.util = (->
150
150
  if argType == 'string'
151
151
  arg = arg.replace(/\s+/g, ' ')
152
152
  arg = "#{arg.substr(0, maxLength)}…" if arg.length > maxLength
153
- "\"#{arg}\""
153
+ arg = "\"#{arg}\""
154
154
  else if argType == 'number'
155
- arg.toString()
155
+ arg = arg.toString()
156
156
  else
157
- "(#{argType})"
157
+ arg = JSON.stringify(arg)
158
+ arg = "#{arg.substr(0, maxLength)}…" if arg.length > maxLength
159
+ arg
158
160
 
159
161
  createSelectorFromElement = ($element) ->
160
162
  debug("Creating selector from element %o", $element)
@@ -225,6 +227,10 @@ up.util = (->
225
227
  each = (collection, block) ->
226
228
  block(item, index) for item, index in collection
227
229
 
230
+ map = each
231
+
232
+ identity = (x) -> x
233
+
228
234
  times = (count, block) ->
229
235
  block(iteration) for iteration in [0..(count - 1)]
230
236
 
@@ -294,7 +300,7 @@ up.util = (->
294
300
  else
295
301
  extend({}, object)
296
302
 
297
- unwrap = (object) ->
303
+ unJquery = (object) ->
298
304
  if isJQuery(object)
299
305
  object.get(0)
300
306
  else
@@ -557,7 +563,13 @@ up.util = (->
557
563
 
558
564
  escapePressed = (event) ->
559
565
  event.keyCode == 27
560
-
566
+
567
+ startsWith = (string, element) ->
568
+ string.indexOf(element) == 0
569
+
570
+ endsWith = (string, element) ->
571
+ string.indexOf(element) == string.length - element.length
572
+
561
573
  contains = (stringOrArray, element) ->
562
574
  stringOrArray.indexOf(element) >= 0
563
575
 
@@ -627,16 +639,25 @@ up.util = (->
627
639
  for key in ownKeys
628
640
  delete hash[key] unless contains(apiKeys, key)
629
641
  hash.update copy(factoryOptions)
630
- update: (options) ->
642
+ update: (options = {}) ->
631
643
  for key, value of options
632
644
  if factoryOptions.hasOwnProperty(key)
633
645
  hash[key] = value
634
646
  else
635
647
  error("Unknown setting %o", key)
648
+ hash
636
649
  apiKeys = Object.getOwnPropertyNames(hash)
637
650
  hash.reset()
638
651
  hash
639
652
 
653
+ unwrapElement = (wrapper) ->
654
+ wrapper = unJquery(wrapper)
655
+ parent = wrapper.parentNode;
656
+ wrappedNodes = toArray(wrapper.childNodes)
657
+ each wrappedNodes, (wrappedNode) ->
658
+ parent.insertBefore(wrappedNode, wrapper)
659
+ parent.removeChild(wrapper)
660
+
640
661
  presentAttr: presentAttr
641
662
  createElement: createElement
642
663
  normalizeUrl: normalizeUrl
@@ -655,6 +676,8 @@ up.util = (->
655
676
  debug: debug
656
677
  warn: warn
657
678
  each: each
679
+ map: map
680
+ identity: identity
658
681
  times: times
659
682
  detect: detect
660
683
  select: select
@@ -681,7 +704,7 @@ up.util = (->
681
704
  isUnmodifiedKeyEvent: isUnmodifiedKeyEvent
682
705
  isUnmodifiedMouseEvent: isUnmodifiedMouseEvent
683
706
  nullJquery: nullJquery
684
- unwrap: unwrap
707
+ unJquery: unJquery
685
708
  nextFrame: nextFrame
686
709
  measure: measure
687
710
  temporaryCss: temporaryCss
@@ -693,6 +716,8 @@ up.util = (->
693
716
  copyAttributes: copyAttributes
694
717
  findWithSelf: findWithSelf
695
718
  contains: contains
719
+ startsWith: startsWith
720
+ endsWith: endsWith
696
721
  isArray: isArray
697
722
  toArray: toArray
698
723
  castsToTrue: castsToTrue
@@ -711,5 +736,6 @@ up.util = (->
711
736
  memoize: memoize
712
737
  scrollbarWidth: scrollbarWidth
713
738
  config: config
739
+ unwrapElement: unwrapElement
714
740
 
715
741
  )()
@@ -1,5 +1,5 @@
1
1
  module Upjs
2
2
  module Rails
3
- VERSION = '0.8.2'
3
+ VERSION = '0.9.0'
4
4
  end
5
5
  end
@@ -1,4 +1,6 @@
1
1
  describe 'up.flow', ->
2
+
3
+ u = up.util
2
4
 
3
5
  describe 'Javascript functions', ->
4
6
 
@@ -8,9 +10,9 @@ describe 'up.flow', ->
8
10
 
9
11
  beforeEach ->
10
12
 
11
- affix('.before').text('old-before')
12
- affix('.middle').text('old-middle')
13
- affix('.after').text('old-after')
13
+ @oldBefore = affix('.before').text('old-before')
14
+ @oldMiddle = affix('.middle').text('old-middle')
15
+ @oldAfter = affix('.after').text('old-after')
14
16
 
15
17
  @responseText =
16
18
  """
@@ -62,6 +64,7 @@ describe 'up.flow', ->
62
64
  @respond()
63
65
  @request.then ->
64
66
  expect($('.before')).toHaveText('old-before')
67
+ console.log("foooo", $('.middle').text())
65
68
  expect($('.middle')).toHaveText('new-middleold-middle')
66
69
  expect($('.after')).toHaveText('old-after')
67
70
  done()
@@ -111,6 +114,47 @@ describe 'up.flow', ->
111
114
  expect(window.scriptTagExecuted).toHaveBeenCalledWith('middle')
112
115
  done()
113
116
 
117
+ describe 'automatic scrolling', ->
118
+
119
+ beforeEach ->
120
+ @revealedHTML = ''
121
+ spyOn(up, 'reveal').and.callFake ($revealedElement) =>
122
+ @revealedHTML = $revealedElement.get(0).outerHTML
123
+ u.resolvedPromise()
124
+
125
+ it 'reveals an old element before it is being replaced', (done) ->
126
+ @request = up.replace('.middle', '/path')
127
+ @respond()
128
+ @request.then =>
129
+ expect(up.reveal).toHaveBeenCalledWith(@oldMiddle, jasmine.any(Object))
130
+ done()
131
+
132
+ it 'reveals a new element that is being appended', (done) ->
133
+ @request = up.replace('.middle:after', '/path')
134
+ @respond()
135
+ @request.then =>
136
+ expect(up.reveal).not.toHaveBeenCalledWith(@oldMiddle, jasmine.any(Object))
137
+ # Text nodes are wrapped in a .up-insertion container so we can
138
+ # animate them and measure their position/size for scrolling.
139
+ # This is not possible for container-less text nodes.
140
+ expect(@revealedHTML).toEqual('<span class="up-insertion">new-middle</span>')
141
+ # Show that the wrapper is done after the insertion.
142
+ expect($('.up-insertion')).not.toExist()
143
+ done()
144
+
145
+ it 'reveals a new element that is being prepended', (done) ->
146
+ @request = up.replace('.middle:before', '/path')
147
+ @respond()
148
+ @request.then =>
149
+ expect(up.reveal).not.toHaveBeenCalledWith(@oldMiddle, jasmine.any(Object))
150
+ # Text nodes are wrapped in a .up-insertion container so we can
151
+ # animate them and measure their position/size for scrolling.
152
+ # This is not possible for container-less text nodes.
153
+ expect(@revealedHTML).toEqual('<span class="up-insertion">new-middle</span>')
154
+ # Show that the wrapper is done after the insertion.
155
+ expect($('.up-insertion')).not.toExist()
156
+ done()
157
+
114
158
  else
115
159
 
116
160
  it 'makes a full page load', ->
@@ -0,0 +1,240 @@
1
+ describe 'up.layout', ->
2
+
3
+ u = up.util
4
+
5
+ describe 'Javascript functions', ->
6
+
7
+ describe 'up.reveal', ->
8
+
9
+ beforeEach ->
10
+ up.layout.defaults(snap: 0)
11
+
12
+ describe 'when the container is body', ->
13
+
14
+ beforeEach ->
15
+ @$viewport = $('body')
16
+ @restoreMargin = u.temporaryCss(@$viewport, 'margin-top': 0)
17
+ @$viewport.scrollTop(0)
18
+
19
+ @$elements = []
20
+ @$container = $('<div class="container">').prependTo(@$viewport)
21
+
22
+ @clientHeight = u.clientSize().height
23
+
24
+ for height in [@clientHeight, '50px', '5000px']
25
+ $element = $('<div>').css(height: height)
26
+ $element.appendTo(@$container)
27
+ @$elements.push($element)
28
+
29
+ afterEach ->
30
+ @$container.remove()
31
+ @restoreMargin()
32
+
33
+ it 'reveals the given element', ->
34
+ up.reveal(@$elements[0], viewport: @$viewport)
35
+ # ---------------------
36
+ # [0] 0 .......... ch-1
37
+ # ---------------------
38
+ # [1] ch+0 ...... ch+49
39
+ # [2] ch+50 ... ch+5049
40
+ expect(@$viewport.scrollTop()).toBe(0)
41
+
42
+ up.reveal(@$elements[1], viewport: @$viewport)
43
+ # ---------------------
44
+ # [0] 0 .......... ch-1
45
+ # [1] ch+0 ...... ch+49
46
+ # ---------------------
47
+ # [2] ch+50 ... ch+5049
48
+ expect(@$viewport.scrollTop()).toBe(50)
49
+
50
+ up.reveal(@$elements[2], viewport: @$viewport)
51
+ # [0] 0 .......... ch-1
52
+ # [1] ch+0 ...... ch+49
53
+ # ---------------------
54
+ # [2] ch+50 ... ch+5049
55
+ # ---------------------
56
+ expect(@$viewport.scrollTop()).toBe(@clientHeight + 50)
57
+
58
+ it 'snaps to the top if the space above the future-visible area is smaller than the value of config.snap', ->
59
+
60
+ up.layout.defaults(snap: 30)
61
+
62
+ @$elements[0].css(height: '20px')
63
+
64
+ up.reveal(@$elements[2], viewport: @$viewport)
65
+ # [0] 0 ............ 19
66
+ # [1] 20 ........... 69
67
+ # ---------------------
68
+ # [2] 70 ......... 5069
69
+ # ---------------------
70
+ expect(@$viewport.scrollTop()).toBe(70)
71
+
72
+ # Even though we're revealing the second element, the viewport
73
+ # snaps to the top edge.
74
+ up.reveal(@$elements[1], viewport: @$viewport)
75
+ # ---------------------
76
+ # [0] 0 ............ 19
77
+ # [1] 20 ........... 69
78
+ # ---------------------
79
+ # [2] 70 ......... 5069
80
+ expect(@$viewport.scrollTop()).toBe(0)
81
+
82
+ it 'scrolls far enough so the element is not obstructed by an element fixed to the top', ->
83
+ $topNav = affix('[up-fixed=top]').css(
84
+ position: 'fixed',
85
+ top: '0',
86
+ left: '0',
87
+ right: '0'
88
+ height: '100px'
89
+ )
90
+
91
+ up.reveal(@$elements[0], viewport: @viewport)
92
+ # ---------------------
93
+ # [F] 0 ............ 99
94
+ # [0] 0 .......... ch-1
95
+ # ---------------------
96
+ # [1] ch+0 ...... ch+49
97
+ # [2] ch+50 ... ch+5049
98
+ expect(@$viewport.scrollTop()).toBe(0) # would need to be -100
99
+
100
+ up.reveal(@$elements[1], viewport: @$viewport)
101
+ # ---------------------
102
+ # [F] 0 ............ 99
103
+ # [0] 00000 ...... ch-1
104
+ # [1] ch+0 ...... ch+49
105
+ # ---------------------
106
+ # [2] ch+50 ... ch+5049
107
+
108
+ expect(@$viewport.scrollTop()).toBe(50)
109
+
110
+ up.reveal(@$elements[2], viewport: @$viewport)
111
+ # [0] 00000 ...... ch-1
112
+ # [1] ch+0 ...... ch+49
113
+ # ---------------------
114
+ # [F] 0 ............ 99
115
+ # [2] ch+50 ... ch+5049
116
+ # ----------------
117
+ expect(@$viewport.scrollTop()).toBe(@clientHeight + 50 - 100)
118
+
119
+ up.reveal(@$elements[1], viewport: @$viewport)
120
+ # [0] 00000 ...... ch-1
121
+ # ---------------------
122
+ # [F] 0 ............ 99
123
+ # [1] ch+0 ...... ch+49
124
+ # [2] ch+50 ... ch+5049
125
+ # ----------------
126
+ expect(@$viewport.scrollTop()).toBe(@clientHeight + 50 - 100 - 50)
127
+
128
+
129
+ it 'scrolls far enough so the element is not obstructed by an element fixed to the bottom', ->
130
+ $bottomNav = affix('[up-fixed=bottom]').css(
131
+ position: 'fixed',
132
+ bottom: '0',
133
+ left: '0',
134
+ right: '0'
135
+ height: '100px'
136
+ )
137
+
138
+ up.reveal(@$elements[0], viewport: @$viewport)
139
+ # ---------------------
140
+ # [0] 0 .......... ch-1
141
+ # [F] 0 ............ 99
142
+ # ---------------------
143
+ # [1] ch+0 ...... ch+49
144
+ # [2] ch+50 ... ch+5049
145
+ expect(@$viewport.scrollTop()).toBe(0)
146
+
147
+ up.reveal(@$elements[1], viewport: @$viewport)
148
+ # ---------------------
149
+ # [0] 0 .......... ch-1
150
+ # [1] ch+0 ...... ch+49
151
+ # [F] 0 ............ 99
152
+ # ---------------------
153
+ # [2] ch+50 ... ch+5049
154
+ expect(@$viewport.scrollTop()).toBe(150)
155
+
156
+ up.reveal(@$elements[2], viewport: @$viewport)
157
+ # ---------------------
158
+ # [0] 0 .......... ch-1
159
+ # [1] ch+0 ...... ch+49
160
+ # ---------------------
161
+ # [2] ch+50 ... ch+5049
162
+ # [F] 0 ............ 99
163
+ expect(@$viewport.scrollTop()).toBe(@clientHeight + 50)
164
+
165
+
166
+
167
+
168
+ describe 'when the viewport is a container with overflow-y: scroll', ->
169
+
170
+ it 'reveals the given element', ->
171
+ $viewport = affix('div').css
172
+ 'position': 'absolute'
173
+ 'top': '50px'
174
+ 'left': '50px'
175
+ 'width': '100px'
176
+ 'height': '100px'
177
+ 'overflow-y': 'scroll'
178
+ $elements = []
179
+ u.each [0..5], ->
180
+ $element = $('<div>').css(height: '50px')
181
+ $element.appendTo($viewport)
182
+ $elements.push($element)
183
+
184
+ # ------------
185
+ # [0] 000..049
186
+ # [1] 050..099
187
+ # ------------
188
+ # [2] 100..149
189
+ # [3] 150..199
190
+ # [4] 200..249
191
+ # [5] 250..399
192
+ expect($viewport.scrollTop()).toBe(0)
193
+
194
+ # See that the view only scrolls down as little as possible
195
+ # in order to reveal the element
196
+ up.reveal($elements[3], viewport: $viewport)
197
+ # [0] 000..049
198
+ # [1] 050..099
199
+ # ------------
200
+ # [2] 100..149
201
+ # [3] 150..199
202
+ # ------------
203
+ # [4] 200..249
204
+ # [5] 250..399
205
+ expect($viewport.scrollTop()).toBe(100)
206
+
207
+ # See that the view doesn't move if the element
208
+ # is already revealed
209
+ up.reveal($elements[2], viewport: $viewport)
210
+ expect($viewport.scrollTop()).toBe(100)
211
+
212
+ # See that the view scrolls as far down as it cans
213
+ # to show the bottom element
214
+ up.reveal($elements[5], viewport: $viewport)
215
+ # [0] 000..049
216
+ # [1] 050..099
217
+ # [2] 100..149
218
+ # [3] 150..199
219
+ # ------------
220
+ # [4] 200..249
221
+ # [5] 250..399
222
+ # ------------
223
+ expect($viewport.scrollTop()).toBe(200)
224
+
225
+ # See that the view only scrolls up as little as possible
226
+ # in order to reveal the element
227
+ up.reveal($elements[1], viewport: $viewport)
228
+ # [0] 000..049
229
+ # ------------
230
+ # [1] 050..099
231
+ # [2] 100..149
232
+ # ------------
233
+ # [3] 150..199
234
+ # [4] 200..249
235
+ # [5] 250..399
236
+ expect($viewport.scrollTop()).toBe(50)
237
+
238
+ describe 'up.scroll', ->
239
+
240
+ it 'should have tests'