upjs-rails 0.18.1 → 0.19.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 +4 -4
- data/CHANGELOG.md +13 -0
- data/dist/up.js +906 -453
- data/dist/up.min.js +3 -2
- data/lib/assets/javascripts/up/browser.js.coffee +3 -1
- data/lib/assets/javascripts/up/flow.js.coffee +277 -67
- data/lib/assets/javascripts/up/log.js.coffee +1 -0
- data/lib/assets/javascripts/up/motion.js.coffee +12 -3
- data/lib/assets/javascripts/up/proxy.js.coffee +8 -5
- data/lib/assets/javascripts/up/syntax.js.coffee +41 -57
- data/lib/assets/javascripts/up/util.js.coffee +38 -21
- data/lib/upjs/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -4
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +1 -2
- data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +269 -18
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +5 -5
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +5 -5
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +36 -36
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +43 -2
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +150 -6
- metadata +5 -2
@@ -346,10 +346,13 @@ up.motion = (($) ->
|
|
346
346
|
if transitionOrName == 'none'
|
347
347
|
transitionOrName = false
|
348
348
|
|
349
|
-
|
349
|
+
$old = $(source)
|
350
|
+
$new = $(target)
|
351
|
+
|
352
|
+
ensureMorphable($old, transitionOrName)
|
353
|
+
ensureMorphable($new, transitionOrName)
|
350
354
|
|
351
|
-
|
352
|
-
$new = $(target)
|
355
|
+
up.log.group ('Morphing %o to %o (using %o)' if transitionOrName), source, target, transitionOrName, ->
|
353
356
|
|
354
357
|
parsedOptions = u.only(options, 'reveal', 'restoreScroll', 'source')
|
355
358
|
parsedOptions = u.extend(parsedOptions, animateOptions(options))
|
@@ -380,6 +383,12 @@ up.motion = (($) ->
|
|
380
383
|
else
|
381
384
|
return skipMorph($old, $new, parsedOptions)
|
382
385
|
|
386
|
+
ensureMorphable = ($element, transition) ->
|
387
|
+
if transition && $element.parents('body').length == 0
|
388
|
+
element = $element.get(0)
|
389
|
+
u.error("Can't morph a <%s> element (%o)", element.tagName, element)
|
390
|
+
|
391
|
+
|
383
392
|
###*
|
384
393
|
This causes the side effects of a successful transition, but instantly.
|
385
394
|
We use this to skip morphing for old browsers, or when the developer
|
@@ -231,7 +231,7 @@ up.proxy = (($) ->
|
|
231
231
|
Once the response is received, a `up:proxy:receive` event will
|
232
232
|
be emitted.
|
233
233
|
|
234
|
-
@function up.
|
234
|
+
@function up.ajax
|
235
235
|
@param {String} request.url
|
236
236
|
@param {String} [request.method='GET']
|
237
237
|
@param {String} [request.target='body']
|
@@ -341,7 +341,7 @@ up.proxy = (($) ->
|
|
341
341
|
emission()
|
342
342
|
|
343
343
|
###*
|
344
|
-
This event is [emitted]/(up.emit) when [AJAX requests](/up.
|
344
|
+
This event is [emitted]/(up.emit) when [AJAX requests](/up.ajax)
|
345
345
|
are taking long to finish.
|
346
346
|
|
347
347
|
By default Up.js will wait 300 ms for an AJAX request to finish
|
@@ -366,7 +366,7 @@ up.proxy = (($) ->
|
|
366
366
|
busyEventEmitted = false
|
367
367
|
|
368
368
|
###*
|
369
|
-
This event is [emitted]/(up.emit) when [AJAX requests](/up.
|
369
|
+
This event is [emitted]/(up.emit) when [AJAX requests](/up.ajax)
|
370
370
|
have [taken long to finish](/up:proxy:busy), but have finished now.
|
371
371
|
|
372
372
|
@event up:proxy:idle
|
@@ -398,6 +398,7 @@ up.proxy = (($) ->
|
|
398
398
|
|
399
399
|
request.headers ||= {}
|
400
400
|
request.headers['X-Up-Target'] = request.target
|
401
|
+
|
401
402
|
request.data = u.requestDataAsArray(request.data)
|
402
403
|
|
403
404
|
if u.contains(config.wrapMethods, request.method)
|
@@ -422,7 +423,7 @@ up.proxy = (($) ->
|
|
422
423
|
promise.fail (args...) -> entry.deferred.reject(args...)
|
423
424
|
|
424
425
|
###*
|
425
|
-
This event is [emitted]/(up.emit) before an [AJAX request](/up.
|
426
|
+
This event is [emitted]/(up.emit) before an [AJAX request](/up.ajax)
|
426
427
|
is starting to load.
|
427
428
|
|
428
429
|
@event up:proxy:load
|
@@ -433,7 +434,7 @@ up.proxy = (($) ->
|
|
433
434
|
###
|
434
435
|
|
435
436
|
###*
|
436
|
-
This event is [emitted]/(up.emit) when the response to an [AJAX request](/up.
|
437
|
+
This event is [emitted]/(up.emit) when the response to an [AJAX request](/up.ajax)
|
437
438
|
has been received.
|
438
439
|
|
439
440
|
@event up:proxy:received
|
@@ -516,3 +517,5 @@ up.proxy = (($) ->
|
|
516
517
|
defaults: -> u.error('up.proxy.defaults(...) no longer exists. Set values on he up.proxy.config property instead.')
|
517
518
|
|
518
519
|
)(jQuery)
|
520
|
+
|
521
|
+
up.ajax = up.proxy.ajax
|
@@ -181,6 +181,11 @@ up.syntax = (($) ->
|
|
181
181
|
If set to `true` and a fragment insertion contains multiple
|
182
182
|
elements matching the selector, `compiler` is only called once
|
183
183
|
with a jQuery collection containing all matching elements.
|
184
|
+
@param {Boolean} [options.keep=false]
|
185
|
+
If set to `true` compiled fragment will be [persisted](/up-keep) during
|
186
|
+
[page updates](/a-up-target).
|
187
|
+
|
188
|
+
This has the same effect as setting an `up-keep` attribute on the element.
|
184
189
|
@param {Function($element, data)} compiler
|
185
190
|
The function to call when a matching element is inserted.
|
186
191
|
The function takes the new element as the first argument (as a jQuery object).
|
@@ -200,23 +205,45 @@ up.syntax = (($) ->
|
|
200
205
|
# Silently discard any compilers that are registered on unsupported browsers
|
201
206
|
return unless up.browser.isSupported()
|
202
207
|
compiler = args.pop()
|
203
|
-
options = u.options(args[0]
|
208
|
+
options = u.options(args[0])
|
204
209
|
compilers.push
|
205
210
|
selector: selector
|
206
211
|
callback: compiler
|
207
212
|
batch: options.batch
|
208
|
-
|
213
|
+
keep: options.keep
|
214
|
+
|
209
215
|
applyCompiler = (compiler, $jqueryElement, nativeElement) ->
|
210
216
|
up.puts ("Compiling '%s' on %o" unless compiler.isDefault), compiler.selector, nativeElement
|
217
|
+
if compiler.keep
|
218
|
+
value = if u.isString(compiler.keep) then compiler.keep else ''
|
219
|
+
$jqueryElement.attr('up-keep', value)
|
211
220
|
destroyer = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)])
|
212
221
|
if u.isFunction(destroyer)
|
213
222
|
$jqueryElement.addClass(DESTROYABLE_CLASS)
|
214
223
|
$jqueryElement.data(DESTROYER_KEY, destroyer)
|
215
224
|
|
216
|
-
|
225
|
+
###*
|
226
|
+
Applies all compilers on the given element and its descendants.
|
227
|
+
Unlike [`up.hello`](/up.hello), this doesn't emit any events.
|
228
|
+
|
229
|
+
@function up.syntax.compile
|
230
|
+
@param {Array<Element>} [options.skip]
|
231
|
+
A list of elements whose subtrees should not be compiled.
|
232
|
+
@internal
|
233
|
+
###
|
234
|
+
compile = ($fragment, options) ->
|
235
|
+
options = u.options(options)
|
236
|
+
$skipSubtrees = $(options.skip)
|
237
|
+
|
217
238
|
up.log.group "Compiling fragment %o", $fragment.get(0), ->
|
218
239
|
for compiler in compilers
|
219
240
|
$matches = u.findWithSelf($fragment, compiler.selector)
|
241
|
+
|
242
|
+
$matches = $matches.filter ->
|
243
|
+
$match = $(this)
|
244
|
+
u.all $skipSubtrees, (element) ->
|
245
|
+
$match.closest(element).length == 0
|
246
|
+
|
220
247
|
if $matches.length
|
221
248
|
up.log.group ("Compiling '%s' on %d element(s)" unless compiler.isDefault), compiler.selector, $matches.length, ->
|
222
249
|
if compiler.batch
|
@@ -224,7 +251,15 @@ up.syntax = (($) ->
|
|
224
251
|
else
|
225
252
|
$matches.each -> applyCompiler(compiler, $(this), this)
|
226
253
|
|
227
|
-
|
254
|
+
###*
|
255
|
+
Runs any destroyers on the given fragment and its descendants.
|
256
|
+
Unlike [`up.destroy`](/up.destroy), this doesn't emit any events
|
257
|
+
and does not remove the element from the DOM.
|
258
|
+
|
259
|
+
@function up.syntax.clean
|
260
|
+
@internal
|
261
|
+
###
|
262
|
+
clean = ($fragment) ->
|
228
263
|
u.findWithSelf($fragment, ".#{DESTROYABLE_CLASS}").each ->
|
229
264
|
$element = $(this)
|
230
265
|
destroyer = $element.data(DESTROYER_KEY)
|
@@ -286,68 +321,17 @@ up.syntax = (($) ->
|
|
286
321
|
reset = ->
|
287
322
|
compilers = u.select compilers, (compiler) -> compiler.isDefault
|
288
323
|
|
289
|
-
###*
|
290
|
-
Compiles a page fragment that has been inserted into the DOM
|
291
|
-
without Up.js.
|
292
|
-
|
293
|
-
**As long as you manipulate the DOM using Up.js, you will never
|
294
|
-
need to call this method.** You only need to use `up.hello` if the
|
295
|
-
DOM is manipulated without Up.js' involvement, e.g. by setting
|
296
|
-
the `innerHTML` property or calling jQuery methods like
|
297
|
-
`html`, `insertAfter` or `appendTo`:
|
298
|
-
|
299
|
-
$element = $('.element');
|
300
|
-
$element.html('<div>...</div>');
|
301
|
-
up.hello($element);
|
302
|
-
|
303
|
-
This function emits the [`up:fragment:inserted`](/up:fragment:inserted)
|
304
|
-
event.
|
305
|
-
|
306
|
-
@function up.hello
|
307
|
-
@param {String|Element|jQuery} selectorOrElement
|
308
|
-
@param {String|Element|jQuery} [options.origin]
|
309
|
-
@return {jQuery}
|
310
|
-
The compiled element
|
311
|
-
@stable
|
312
|
-
###
|
313
|
-
hello = (selectorOrElement, options) ->
|
314
|
-
$element = $(selectorOrElement)
|
315
|
-
eventAttrs = u.options options,
|
316
|
-
$element: $element
|
317
|
-
message: ['Inserted fragment %o', $element.get(0)]
|
318
|
-
up.emit('up:fragment:inserted', eventAttrs)
|
319
|
-
$element
|
320
|
-
|
321
|
-
###*
|
322
|
-
When a page fragment has been [inserted or updated](/up.replace),
|
323
|
-
this event is [emitted](/up.emit) on the fragment.
|
324
|
-
|
325
|
-
\#\#\#\# Example
|
326
|
-
|
327
|
-
up.on('up:fragment:inserted', function(event, $fragment) {
|
328
|
-
console.log("Looks like we have a new %o!", $fragment);
|
329
|
-
});
|
330
|
-
|
331
|
-
@event up:fragment:inserted
|
332
|
-
@param {jQuery} event.$element
|
333
|
-
The fragment that has been inserted or updated.
|
334
|
-
@stable
|
335
|
-
###
|
336
|
-
|
337
|
-
up.on 'ready', (-> hello(document.body))
|
338
|
-
up.on 'up:fragment:inserted', (event, $element) -> compile($element)
|
339
|
-
up.on 'up:fragment:destroy', (event, $element) -> runDestroyers($element)
|
340
324
|
up.on 'up:framework:boot', snapshot
|
341
325
|
up.on 'up:framework:reset', reset
|
342
326
|
|
343
327
|
compiler: compiler
|
344
|
-
|
328
|
+
compile: compile
|
329
|
+
clean: clean
|
345
330
|
data: data
|
346
331
|
|
347
332
|
)(jQuery)
|
348
333
|
|
349
334
|
up.compiler = up.syntax.compiler
|
350
|
-
up.hello = up.syntax.hello
|
351
335
|
|
352
336
|
up.ready = -> up.util.error('up.ready no longer exists. Please use up.hello instead.')
|
353
337
|
up.awaken = -> up.util.error('up.awaken no longer exists. Please use up.compiler instead.')
|
@@ -163,7 +163,7 @@ up.util = (($) ->
|
|
163
163
|
$element = $(element)
|
164
164
|
selector = undefined
|
165
165
|
|
166
|
-
|
166
|
+
up.puts("Creating selector from element %o", $element.get(0))
|
167
167
|
|
168
168
|
if upId = presence($element.attr("up-id"))
|
169
169
|
selector = "[up-id='#{upId}']"
|
@@ -172,7 +172,6 @@ up.util = (($) ->
|
|
172
172
|
else if name = presence($element.attr("name"))
|
173
173
|
selector = "[name='#{name}']"
|
174
174
|
else if classes = presence(nonUpClasses($element))
|
175
|
-
console.log("using klass!", classes)
|
176
175
|
selector = ''
|
177
176
|
for klass in classes
|
178
177
|
selector += ".#{klass}"
|
@@ -646,6 +645,24 @@ up.util = (($) ->
|
|
646
645
|
break
|
647
646
|
match
|
648
647
|
|
648
|
+
###*
|
649
|
+
Returns whether the given function returns a truthy value
|
650
|
+
for all elements in the given array.
|
651
|
+
|
652
|
+
@function up.util.all
|
653
|
+
@param {Array<T>} array
|
654
|
+
@param {Function<T>} tester
|
655
|
+
@return {Boolean}
|
656
|
+
@experimental
|
657
|
+
###
|
658
|
+
all = (array, tester) ->
|
659
|
+
match = true
|
660
|
+
for element in array
|
661
|
+
unless tester(element)
|
662
|
+
match = false
|
663
|
+
break
|
664
|
+
match
|
665
|
+
|
649
666
|
###*
|
650
667
|
Returns all elements from the given array that are
|
651
668
|
neither `null` or `undefined`.
|
@@ -1436,31 +1453,30 @@ up.util = (($) ->
|
|
1436
1453
|
@internal
|
1437
1454
|
###
|
1438
1455
|
requestDataAsArray = (data) ->
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1456
|
+
query = requestDataAsQuery(data)
|
1457
|
+
array = []
|
1458
|
+
for part in query.split('&')
|
1459
|
+
if isPresent(part)
|
1460
|
+
pair = part.split('=')
|
1461
|
+
array.push
|
1462
|
+
name: decodeURIComponent(pair[0])
|
1463
|
+
value: decodeURIComponent(pair[1])
|
1464
|
+
array
|
1447
1465
|
|
1448
1466
|
###*
|
1449
1467
|
Returns an URL-encoded query string for the given params object.
|
1450
1468
|
|
1451
|
-
@function up.util.
|
1469
|
+
@function up.util.requestDataAsQuery
|
1452
1470
|
@param {Object|Array|Undefined|Null} data
|
1453
1471
|
@internal
|
1454
1472
|
###
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
query
|
1460
|
-
|
1461
|
-
|
1462
|
-
query += encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value)
|
1463
|
-
query
|
1473
|
+
requestDataAsQuery = (data) ->
|
1474
|
+
if data
|
1475
|
+
query = $.param(data)
|
1476
|
+
query = query.replace(/\+/g, '%20')
|
1477
|
+
query
|
1478
|
+
else
|
1479
|
+
""
|
1464
1480
|
|
1465
1481
|
###*
|
1466
1482
|
Throws a fatal error with the given message.
|
@@ -1485,7 +1501,7 @@ up.util = (($) ->
|
|
1485
1501
|
throw new Error(asString)
|
1486
1502
|
|
1487
1503
|
requestDataAsArray: requestDataAsArray
|
1488
|
-
|
1504
|
+
requestDataAsQuery: requestDataAsQuery
|
1489
1505
|
offsetParent: offsetParent
|
1490
1506
|
fixedToAbsolute: fixedToAbsolute
|
1491
1507
|
presentAttr: presentAttr
|
@@ -1507,6 +1523,7 @@ up.util = (($) ->
|
|
1507
1523
|
map: map
|
1508
1524
|
times: times
|
1509
1525
|
any: any
|
1526
|
+
all: all
|
1510
1527
|
detect: detect
|
1511
1528
|
select: select
|
1512
1529
|
reject: reject
|
data/lib/upjs/rails/version.rb
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -47,9 +47,9 @@ describe 'up.flow', ->
|
|
47
47
|
expect(@lastRequest().data()['bar-key']).toEqual(['bar-value'])
|
48
48
|
|
49
49
|
it "encodes the given params into the URL of a GET request", ->
|
50
|
-
givenParams = { 'foo-key': 'foo
|
50
|
+
givenParams = { 'foo-key': 'foo-value', 'bar-key': 'bar-value' }
|
51
51
|
up.replace('.middle', '/path', method: 'get', data: givenParams)
|
52
|
-
expect(@lastRequest().url).toEndWith('/path?foo-key=foo
|
52
|
+
expect(@lastRequest().url).toEndWith('/path?foo-key=foo-value&bar-key=bar-value')
|
53
53
|
|
54
54
|
it 'uses a HTTP method given as { method } option', ->
|
55
55
|
up.replace('.middle', '/path', method: 'put')
|
@@ -58,7 +58,7 @@ describe 'up.flow', ->
|
|
58
58
|
describe 'if the server responds with a non-200 status code', ->
|
59
59
|
|
60
60
|
it 'replaces the <body> instead of the given selector', ->
|
61
|
-
implantSpy = up.flow.knife.mock('
|
61
|
+
implantSpy = up.flow.knife.mock('extract') # can't have the example replace the Jasmine test runner UI
|
62
62
|
up.replace('.middle', '/path')
|
63
63
|
@respond(status: 500)
|
64
64
|
expect(implantSpy).toHaveBeenCalledWith('body', jasmine.any(String), jasmine.any(Object))
|
@@ -391,7 +391,7 @@ describe 'up.flow', ->
|
|
391
391
|
up.replace('.selector', '/path')
|
392
392
|
expect(up.browser.loadPage).toHaveBeenCalledWith('/path', jasmine.anything())
|
393
393
|
|
394
|
-
describe 'up.
|
394
|
+
describe 'up.extract', ->
|
395
395
|
|
396
396
|
it 'Updates a selector on the current page with the same selector from the given HTML string', ->
|
397
397
|
|
@@ -406,7 +406,7 @@ describe 'up.flow', ->
|
|
406
406
|
<div class="after">new-after</div>
|
407
407
|
"""
|
408
408
|
|
409
|
-
up.
|
409
|
+
up.extract('.middle', html)
|
410
410
|
|
411
411
|
expect($('.before')).toHaveText('old-before')
|
412
412
|
expect($('.middle')).toHaveText('new-middle')
|
@@ -414,49 +414,299 @@ describe 'up.flow', ->
|
|
414
414
|
|
415
415
|
it "throws an error if the selector can't be found on the current page", ->
|
416
416
|
html = '<div class="foo-bar">text</div>'
|
417
|
-
|
418
|
-
expect(
|
417
|
+
extract = -> up.extract('.foo-bar', html)
|
418
|
+
expect(extract).toThrowError(/Could not find selector ".foo-bar" in current body/i)
|
419
419
|
|
420
420
|
it "throws an error if the selector can't be found in the given HTML string", ->
|
421
421
|
affix('.foo-bar')
|
422
|
-
|
423
|
-
expect(
|
422
|
+
extract = -> up.extract('.foo-bar', '')
|
423
|
+
expect(extract).toThrowError(/Could not find selector ".foo-bar" in response/i)
|
424
424
|
|
425
425
|
it "ignores an element that matches the selector but also matches .up-destroying", ->
|
426
426
|
html = '<div class="foo-bar">text</div>'
|
427
427
|
affix('.foo-bar.up-destroying')
|
428
|
-
|
429
|
-
expect(
|
428
|
+
extract = -> up.extract('.foo-bar', html)
|
429
|
+
expect(extract).toThrowError(/Could not find selector/i)
|
430
430
|
|
431
431
|
it "ignores an element that matches the selector but also matches .up-ghost", ->
|
432
432
|
html = '<div class="foo-bar">text</div>'
|
433
433
|
affix('.foo-bar.up-ghost')
|
434
|
-
|
435
|
-
expect(
|
434
|
+
extract = -> up.extract('.foo-bar', html)
|
435
|
+
expect(extract).toThrowError(/Could not find selector/i)
|
436
436
|
|
437
437
|
it "ignores an element that matches the selector but also has a parent matching .up-destroying", ->
|
438
438
|
html = '<div class="foo-bar">text</div>'
|
439
439
|
$parent = affix('.up-destroying')
|
440
440
|
$child = affix('.foo-bar').appendTo($parent)
|
441
|
-
|
442
|
-
expect(
|
441
|
+
extract = -> up.extract('.foo-bar', html)
|
442
|
+
expect(extract).toThrowError(/Could not find selector/i)
|
443
443
|
|
444
444
|
it "ignores an element that matches the selector but also has a parent matching .up-ghost", ->
|
445
445
|
html = '<div class="foo-bar">text</div>'
|
446
446
|
$parent = affix('.up-ghost')
|
447
447
|
$child = affix('.foo-bar').appendTo($parent)
|
448
|
-
|
449
|
-
expect(
|
448
|
+
extract = -> up.extract('.foo-bar', html)
|
449
|
+
expect(extract).toThrowError(/Could not find selector/i)
|
450
450
|
|
451
451
|
it 'only replaces the first element matching the selector', ->
|
452
452
|
html = '<div class="foo-bar">text</div>'
|
453
453
|
affix('.foo-bar')
|
454
454
|
affix('.foo-bar')
|
455
|
-
up.
|
455
|
+
up.extract('.foo-bar', html)
|
456
456
|
elements = $('.foo-bar')
|
457
457
|
expect($(elements.get(0)).text()).toEqual('text')
|
458
458
|
expect($(elements.get(1)).text()).toEqual('')
|
459
459
|
|
460
|
+
describe 'handling of [up-keep] elements', ->
|
461
|
+
|
462
|
+
squish = (string) ->
|
463
|
+
if u.isString(string)
|
464
|
+
string = string.replace(/^\s+/g, '')
|
465
|
+
string = string.replace(/\s+$/g, '')
|
466
|
+
string = string.replace(/\s+/g, ' ')
|
467
|
+
string
|
468
|
+
|
469
|
+
beforeEach ->
|
470
|
+
# Need to refactor this spec file so examples don't all share one example
|
471
|
+
$('.before, .middle, .after').remove()
|
472
|
+
|
473
|
+
it 'keeps an [up-keep] element, but does replace other elements around it', ->
|
474
|
+
$container = affix('.container')
|
475
|
+
$container.affix('.before').text('old-before')
|
476
|
+
$container.affix('.middle[up-keep]').text('old-middle')
|
477
|
+
$container.affix('.after').text('old-after')
|
478
|
+
up.extract '.container', """
|
479
|
+
<div class='container'>
|
480
|
+
<div class='before'>new-before</div>
|
481
|
+
<div class='middle' up-keep>new-middle</div>
|
482
|
+
<div class='after'>new-after</div>
|
483
|
+
</div>
|
484
|
+
"""
|
485
|
+
expect($('.before')).toHaveText('new-before')
|
486
|
+
expect($('.middle')).toHaveText('old-middle')
|
487
|
+
expect($('.after')).toHaveText('new-after')
|
488
|
+
|
489
|
+
it 'keeps an [up-keep] element, but does replace text nodes around it', ->
|
490
|
+
$container = affix('.container')
|
491
|
+
$container.html """
|
492
|
+
old-before
|
493
|
+
<div class='element' up-keep>old-inside</div>
|
494
|
+
old-after
|
495
|
+
"""
|
496
|
+
up.extract '.container', """
|
497
|
+
<div class='container'>
|
498
|
+
new-before
|
499
|
+
<div class='element' up-keep>new-inside</div>
|
500
|
+
new-after
|
501
|
+
</div>
|
502
|
+
"""
|
503
|
+
expect(squish($('.container').text())).toEqual('new-before old-inside new-after')
|
504
|
+
|
505
|
+
describe 'if an [up-keep] element is itself a direct replacement target', ->
|
506
|
+
|
507
|
+
it "keeps that element", ->
|
508
|
+
affix('.keeper[up-keep]').text('old-inside')
|
509
|
+
up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
|
510
|
+
expect($('.keeper')).toHaveText('old-inside')
|
511
|
+
|
512
|
+
it "does not compile the kept element a second time"
|
513
|
+
|
514
|
+
it "only emits an event up:fragment:kept, but not an event up:fragment:inserted", ->
|
515
|
+
insertedListener = jasmine.createSpy('subscriber to up:fragment:inserted')
|
516
|
+
up.on('up:fragment:inserted', insertedListener)
|
517
|
+
keptListener = jasmine.createSpy('subscriber to up:fragment:kept')
|
518
|
+
up.on('up:fragment:kept', keptListener)
|
519
|
+
up.on 'up:fragment:inserted', insertedListener
|
520
|
+
$keeper = affix('.keeper[up-keep]').text('old-inside')
|
521
|
+
up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
|
522
|
+
expect(insertedListener).not.toHaveBeenCalled()
|
523
|
+
expect(keptListener).toHaveBeenCalledWith(jasmine.anything(), $('.keeper'), jasmine.anything())
|
524
|
+
|
525
|
+
it "removes an [up-keep] element if no matching element is found in the response", ->
|
526
|
+
$container = affix('.container')
|
527
|
+
$container.html """
|
528
|
+
<div class='foo'>old-foo</div>
|
529
|
+
<div class='bar' up-keep>old-bar</div>
|
530
|
+
"""
|
531
|
+
up.extract '.container', """
|
532
|
+
<div class='container'>
|
533
|
+
<div class='foo'>new-foo</div>
|
534
|
+
</div>
|
535
|
+
"""
|
536
|
+
expect($('.container .foo')).toExist()
|
537
|
+
expect($('.container .bar')).not.toExist()
|
538
|
+
|
539
|
+
it "updates an element if a matching element is found in the response, but that other element is no longer [up-keep]", ->
|
540
|
+
$container = affix('.container')
|
541
|
+
$container.html """
|
542
|
+
<div class='foo'>old-foo</div>
|
543
|
+
<div class='bar' up-keep>old-bar</div>
|
544
|
+
"""
|
545
|
+
up.extract '.container', """
|
546
|
+
<div class='container'>
|
547
|
+
<div class='foo'>new-foo</div>
|
548
|
+
<div class='bar'>new-bar</div>
|
549
|
+
</div>
|
550
|
+
"""
|
551
|
+
expect($('.container .foo')).toHaveText('new-foo')
|
552
|
+
expect($('.container .bar')).toHaveText('new-bar')
|
553
|
+
|
554
|
+
it 'moves a kept element to the ancestry position of the matching element in the response', ->
|
555
|
+
$container = affix('.container')
|
556
|
+
$container.html """
|
557
|
+
<div class="parent1">
|
558
|
+
<div class="keeper" up-keep>old-inside</div>
|
559
|
+
</div>
|
560
|
+
<div class="parent2">
|
561
|
+
</div>
|
562
|
+
"""
|
563
|
+
up.extract '.container', """
|
564
|
+
<div class='container'>
|
565
|
+
<div class="parent1">
|
566
|
+
</div>
|
567
|
+
<div class="parent2">
|
568
|
+
<div class="keeper" up-keep>old-inside</div>
|
569
|
+
</div>
|
570
|
+
</div>
|
571
|
+
"""
|
572
|
+
expect($('.keeper')).toHaveText('old-inside')
|
573
|
+
expect($('.keeper').parent()).toEqual($('.parent2'))
|
574
|
+
|
575
|
+
it 'lets developers choose a selector to match against as the value of the up-keep attribute', ->
|
576
|
+
$container = affix('.container')
|
577
|
+
$container.html """
|
578
|
+
<div class="keeper" up-keep=".stayer"></div>
|
579
|
+
"""
|
580
|
+
up.extract '.container', """
|
581
|
+
<div class='container'>
|
582
|
+
<div up-keep class="stayer"></div>
|
583
|
+
</div>
|
584
|
+
"""
|
585
|
+
expect('.keeper').toExist()
|
586
|
+
|
587
|
+
it 'does not compile a kept element a second time', ->
|
588
|
+
compiler = jasmine.createSpy('compiler')
|
589
|
+
up.compiler('.keeper', compiler)
|
590
|
+
$container = affix('.container')
|
591
|
+
$container.html """
|
592
|
+
<div class="keeper" up-keep>old-text</div>
|
593
|
+
"""
|
594
|
+
|
595
|
+
console.log '*** before hello ***'
|
596
|
+
up.hello($container)
|
597
|
+
console.log '*** after hello ***'
|
598
|
+
expect(compiler.calls.count()).toEqual(1)
|
599
|
+
|
600
|
+
up.extract '.container', """
|
601
|
+
<div class='container'>
|
602
|
+
<div class="keeper" up-keep>new-text</div>
|
603
|
+
</div>
|
604
|
+
"""
|
605
|
+
expect(compiler.calls.count()).toEqual(1)
|
606
|
+
expect('.keeper').toExist()
|
607
|
+
|
608
|
+
it 'lets listeners cancel the keeping by preventing default on an up:fragment:keep event', ->
|
609
|
+
$keeper = affix('.keeper[up-keep]').text('old-inside')
|
610
|
+
$keeper.on 'up:fragment:keep', (event) -> event.preventDefault()
|
611
|
+
up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
|
612
|
+
expect($('.keeper')).toHaveText('new-inside')
|
613
|
+
|
614
|
+
it 'lets listeners prevent up:fragment:keep event if the element was kept before (bugfix)', ->
|
615
|
+
$keeper = affix('.keeper[up-keep]').text('version 1')
|
616
|
+
$keeper.on 'up:fragment:keep', (event) ->
|
617
|
+
event.preventDefault() if event.$newElement.text() == 'version 3'
|
618
|
+
up.extract '.keeper', "<div class='keeper' up-keep>version 2</div>"
|
619
|
+
expect($('.keeper')).toHaveText('version 1')
|
620
|
+
up.extract '.keeper', "<div class='keeper' up-keep>version 3</div>"
|
621
|
+
expect($('.keeper')).toHaveText('version 3')
|
622
|
+
|
623
|
+
it 'emits an up:fragment:kept event on a kept element and up:fragment:inserted on an updated parent', ->
|
624
|
+
insertedListener = jasmine.createSpy()
|
625
|
+
up.on('up:fragment:inserted', insertedListener)
|
626
|
+
keptListener = jasmine.createSpy()
|
627
|
+
up.on('up:fragment:kept', keptListener)
|
628
|
+
|
629
|
+
$container = affix('.container')
|
630
|
+
$container.html """
|
631
|
+
<div class="keeper" up-keep></div>
|
632
|
+
"""
|
633
|
+
up.extract '.container', """
|
634
|
+
<div class='container'>
|
635
|
+
<div class="keeper" up-keep></div>
|
636
|
+
</div>
|
637
|
+
"""
|
638
|
+
expect(insertedListener).toHaveBeenCalledWith(jasmine.anything(), $('.container'), jasmine.anything())
|
639
|
+
expect(keptListener).toHaveBeenCalledWith(jasmine.anything(), $('.container .keeper'), jasmine.anything())
|
640
|
+
|
641
|
+
it 'emits an up:fragment:kept event on a kept element with a newData property corresponding to the up-data attribute value of the discarded element', ->
|
642
|
+
keptListener = jasmine.createSpy()
|
643
|
+
up.on 'up:fragment:kept', (event) -> keptListener(event.$element, event.newData)
|
644
|
+
$container = affix('.container')
|
645
|
+
$keeper = $container.affix('.keeper[up-keep]').text('old-inside')
|
646
|
+
up.extract '.container', """
|
647
|
+
<div class='container'>
|
648
|
+
<div class='keeper' up-keep up-data='{ "foo": "bar" }'>new-inside</div>
|
649
|
+
</div>
|
650
|
+
"""
|
651
|
+
expect($('.keeper')).toHaveText('old-inside')
|
652
|
+
expect(keptListener).toHaveBeenCalledWith($keeper, { 'foo': 'bar' })
|
653
|
+
|
654
|
+
it 'emits an up:fragment:kept with { newData: {} } if the discarded element had no up-data value', ->
|
655
|
+
keptListener = jasmine.createSpy()
|
656
|
+
up.on('up:fragment:kept', keptListener)
|
657
|
+
$container = affix('.container')
|
658
|
+
$keeper = $container.affix('.keeper[up-keep]').text('old-inside')
|
659
|
+
up.extract '.keeper', """
|
660
|
+
<div class='container'>
|
661
|
+
<div class='keeper' up-keep>new-inside</div>
|
662
|
+
</div>
|
663
|
+
"""
|
664
|
+
expect($('.keeper')).toHaveText('old-inside')
|
665
|
+
expect(keptListener).toEqual(jasmine.anything(), $('.keeper'), {})
|
666
|
+
|
667
|
+
it 'reuses the same element and emits up:fragment:kept during multiple extractions', ->
|
668
|
+
keptListener = jasmine.createSpy()
|
669
|
+
up.on 'up:fragment:kept', (event) -> keptListener(event.$element, event.newData)
|
670
|
+
$container = affix('.container')
|
671
|
+
$keeper = $container.affix('.keeper[up-keep]').text('old-inside')
|
672
|
+
up.extract '.keeper', """
|
673
|
+
<div class='container'>
|
674
|
+
<div class='keeper' up-keep up-data='{ \"key\": \"value1\" }'>new-inside</div>
|
675
|
+
</div>
|
676
|
+
"""
|
677
|
+
up.extract '.keeper', """
|
678
|
+
<div class='container'>
|
679
|
+
<div class='keeper' up-keep up-data='{ \"key\": \"value2\" }'>new-inside</div>
|
680
|
+
"""
|
681
|
+
$keeper = $('.keeper')
|
682
|
+
expect($keeper).toHaveText('old-inside')
|
683
|
+
expect(keptListener).toHaveBeenCalledWith($keeper, { key: 'value1' })
|
684
|
+
expect(keptListener).toHaveBeenCalledWith($keeper, { key: 'value2' })
|
685
|
+
|
686
|
+
it "doesn't let the discarded element appear in a transition", (done) ->
|
687
|
+
oldTextDuringTransition = undefined
|
688
|
+
newTextDuringTransition = undefined
|
689
|
+
transition = ($old, $new) ->
|
690
|
+
oldTextDuringTransition = squish($old.text())
|
691
|
+
newTextDuringTransition = squish($new.text())
|
692
|
+
u.resolvedDeferred()
|
693
|
+
$container = affix('.container')
|
694
|
+
$container.html """
|
695
|
+
<div class='foo'>old-foo</div>
|
696
|
+
<div class='bar' up-keep>old-bar</div>
|
697
|
+
"""
|
698
|
+
newHtml = """
|
699
|
+
<div class='container'>
|
700
|
+
<div class='foo'>new-foo</div>
|
701
|
+
<div class='bar' up-keep>new-bar</div>
|
702
|
+
</div>
|
703
|
+
"""
|
704
|
+
promise = up.extract('.container', newHtml, transition: transition)
|
705
|
+
promise.then ->
|
706
|
+
expect(oldTextDuringTransition).toEqual('old-foo old-bar')
|
707
|
+
expect(newTextDuringTransition).toEqual('new-foo old-bar')
|
708
|
+
done()
|
709
|
+
|
460
710
|
describe 'up.destroy', ->
|
461
711
|
|
462
712
|
it 'removes the element with the given selector', ->
|
@@ -502,3 +752,4 @@ describe 'up.flow', ->
|
|
502
752
|
describe 'up.reset', ->
|
503
753
|
|
504
754
|
it 'should have tests'
|
755
|
+
|