upjs-rails 0.18.1 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|