unpoly-rails 0.23.0 → 0.24.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.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/dist/unpoly-bootstrap3.js +1 -1
  4. data/dist/unpoly-bootstrap3.min.js +1 -1
  5. data/dist/unpoly.css +18 -8
  6. data/dist/unpoly.js +498 -265
  7. data/dist/unpoly.min.css +1 -1
  8. data/dist/unpoly.min.js +3 -3
  9. data/lib/assets/javascripts/unpoly-bootstrap3/modal-ext.js.coffee +5 -2
  10. data/lib/assets/javascripts/unpoly/flow.js.coffee +3 -1
  11. data/lib/assets/javascripts/unpoly/form.js.coffee +3 -6
  12. data/lib/assets/javascripts/unpoly/layout.js.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/link.js.coffee +4 -2
  14. data/lib/assets/javascripts/unpoly/log.js.coffee +2 -2
  15. data/lib/assets/javascripts/unpoly/modal.js.coffee +125 -36
  16. data/lib/assets/javascripts/unpoly/motion.js.coffee +75 -37
  17. data/lib/assets/javascripts/unpoly/navigation.js.coffee +38 -26
  18. data/lib/assets/javascripts/unpoly/proxy.js.coffee +77 -52
  19. data/lib/assets/javascripts/unpoly/syntax.js.coffee +1 -0
  20. data/lib/assets/javascripts/unpoly/tooltip.js.coffee +2 -0
  21. data/lib/assets/javascripts/unpoly/util.js.coffee +138 -46
  22. data/lib/assets/stylesheets/unpoly/link.css.sass +1 -1
  23. data/lib/assets/stylesheets/unpoly/modal.css.sass +28 -15
  24. data/lib/unpoly/rails/version.rb +1 -1
  25. data/spec_app/Gemfile.lock +7 -7
  26. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +2 -0
  27. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +5 -0
  28. data/spec_app/spec/javascripts/up/flow_spec.js.coffee +2 -2
  29. data/spec_app/spec/javascripts/up/history_spec.js.coffee +6 -4
  30. data/spec_app/spec/javascripts/up/link_spec.js.coffee +114 -5
  31. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +28 -18
  32. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +112 -7
  33. data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +157 -121
  34. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
  35. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +42 -3
  36. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -2
  37. data/spec_app/spec/javascripts/up/util_spec.js.coffee +26 -1
  38. data/spec_app/vendor/assets/bower_components/jquery/.bower.json +1 -1
  39. metadata +3 -3
  40. data/spec_app/spec/javascripts/helpers/set_timer.js.coffee +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec9d0678d3c0fa042de8cb5e10f94014dfbe7f36
4
- data.tar.gz: 28e851c819c201c8e65fd1e1945a3b44f4310e56
3
+ metadata.gz: c68b539b44ddfe0d7ded37b84b03a668efcb4d81
4
+ data.tar.gz: ae1d157e125c1250b4f0324bc6b9b8861e5a9129
5
5
  SHA512:
6
- metadata.gz: 8a70cad251b68e888be109193209eea3101aab04efb98a62abd0e2a282ef10977f64569c208cbd9041c4f7c9bc74ea5147e140341badf15b5f58c91eb1e4efc8
7
- data.tar.gz: 6b13838de87ece08a23be26531c3bfa0258cffc958f3495d0a692288bba6bf3859c55e020cd63c15842c37227e5f618c3484415c9530e3dd19912797b6e001c3
6
+ metadata.gz: 14726bfdd87382037bc837893c7a62b889233d6b8904c72ba6131a3ffdfb9b13bdb70daa4e63af70e4755eafd85f4b5392bfefe9cc69c274ad1aa49dd20996ce
7
+ data.tar.gz: 5987b1663195c17b44b56b46323e1d38142518c0a52f614c69f0d77f197ba7f9a30af92a526d764122fd02fb8c2ef40c244df306b2031813300dc9bcb1abfcc4
data/CHANGELOG.md CHANGED
@@ -14,6 +14,73 @@ Unreleased
14
14
  ### Breaking changes
15
15
 
16
16
 
17
+ 0.24.0
18
+ ------
19
+
20
+ ### Compatible changes
21
+
22
+ - New function [`up.modal.extract`](/up.modal.extract) to open a modal from an
23
+ existing HTML string.
24
+ - [`up.ajax`](/up.ajax) now also accepts the URL as a first string argument.
25
+ - [Expanded](/up.expand) links to modals or popups now get a pointer cursor via CSS
26
+ - New options for [up.modal.config](/up.modal.config):
27
+ - `up.modal.config.openDuration`
28
+ - `up.modal.config.closeDuration`
29
+ - `up.modal.config.openEasing`
30
+ - `up.modal.config.closeEasing`
31
+ - `up.modal.config.backdropOpenAnimation`
32
+ - `up.modal.config.backdropCloseAnimation`
33
+ - Also see the breaking changes regarding modal structure below.
34
+ - Calling [`up.motion.finish`](/up.motion.finish) without arguments will now
35
+ complete all animations and transitions on the screen.
36
+ - Fix a bug where [`up.motion.finish`](/up.motion.finish) would not cancel CSS transitions that were still in progress.
37
+ - Fix a bug where [`up-active`](/up-active) classes where not removed from links when the destination
38
+ was already [preloaded](/up.preload).
39
+
40
+
41
+ ### Breaking changes
42
+
43
+ - Animations when opening or closing a [modal](/up.modal) now only affect the viewport around the dialog.
44
+ The backdrop is animated separately. This allows animations like "zoom in", which would look strange if
45
+ the backdrop would zoom in together with the dialog.
46
+ - The modal's HTML structure has been changed to include a `.up-modal-backdrop` element:
47
+
48
+ ```
49
+ <div class="up-modal">
50
+ <div class="up-modal-backdrop">
51
+ <div class="up-modal-viewport">
52
+ <div class="up-modal-dialog">
53
+ <div class="up-modal-content">
54
+ ...
55
+ </div>
56
+ <div class="up-modal-close" up-close>X</div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ ```
61
+
62
+ - The `z-index` properties for modal elements have been [changed](https://github.com/unpoly/unpoly/blob/master/lib/assets/stylesheets/unpoly/modal.css.sass).
63
+ They might change again in the future.
64
+ - The modal will now take over the document's scrollbars after the open animation has finished.
65
+ In earlier versions the modal took over as soon as the animation had started.
66
+ - Calling [`up.motion.finish`](/up.motion.finish) with an element will now also
67
+ complete animations/transitions on children of the given element.
68
+
69
+
70
+ 0.23.1
71
+ ------
72
+
73
+ ### Compatible changes
74
+
75
+ - [Animations](/up.motion) `move-to-*` and `move-from-*` now use CSS transforms instead of manipulating the
76
+ bounding box margins.
77
+ - Fix [`up.util.trim`](/up.util.trim) not working properly.
78
+ - [`up.morph`](/up.morph) no longer throws an error if called without an `options` object
79
+ - Custom transitions can now call [`up.morph`](/up.morph) to refer to other transitions
80
+ - Fix a bug where following a link to a [preloaded](/up-preload) destination would keep the
81
+ link marked with a [up-active](/up-active) class forever.
82
+
83
+
17
84
  0.23.0
18
85
  ------
19
86
 
@@ -41,6 +108,7 @@ Unreleased
41
108
  - Fix a bug where a link would be followed multiple times if the link's
42
109
  click area was expanded using [`[up-expand]`](/up-expand) and if the
43
110
  link also had an [`up-dash`](/up-dash) attribute.
111
+ - [`up.destroy`](/up.destroy) now returns a resolved deferred if the given selector or jQuery collection does not exist
44
112
 
45
113
 
46
114
  0.22.0
@@ -15,7 +15,7 @@
15
15
 
16
16
  }).call(this);
17
17
  (function() {
18
- up.modal.config.template = "<div class=\"up-modal\">\n <div class=\"up-modal-dialog modal-dialog\">\n <div class=\"up-modal-content modal-content\"></div>\n </div>\n</div>";
18
+ up.modal.config.template = "<div class=\"up-modal\">\n <div class=\"up-modal-backdrop\"></div>\n <div class=\"up-modal-viewport\">\n <div class=\"up-modal-dialog modal-dialog\">\n <div class=\"up-modal-content modal-content\"></div>\n </div>\n </div>\n</div>";
19
19
 
20
20
  }).call(this);
21
21
  (function() {
@@ -1 +1 @@
1
- (function(){up.form.config.validateTargets.unshift(".form-group:has(&)")}).call(this),function(){up.layout.config.fixedTop.push(".navbar-fixed-top"),up.layout.config.fixedBottom.push(".navbar-fixed-bottom"),up.layout.config.anchoredRight.push(".navbar-fixed-top"),up.layout.config.anchoredRight.push(".navbar-fixed-bottom"),up.layout.config.anchoredRight.push(".footer")}.call(this),function(){up.modal.config.template='<div class="up-modal">\n <div class="up-modal-dialog modal-dialog">\n <div class="up-modal-content modal-content"></div>\n </div>\n</div>'}.call(this),function(){up.navigation.config.currentClasses.push("active")}.call(this),function(){}.call(this);
1
+ (function(){up.form.config.validateTargets.unshift(".form-group:has(&)")}).call(this),function(){up.layout.config.fixedTop.push(".navbar-fixed-top"),up.layout.config.fixedBottom.push(".navbar-fixed-bottom"),up.layout.config.anchoredRight.push(".navbar-fixed-top"),up.layout.config.anchoredRight.push(".navbar-fixed-bottom"),up.layout.config.anchoredRight.push(".footer")}.call(this),function(){up.modal.config.template='<div class="up-modal">\n <div class="up-modal-backdrop"></div>\n <div class="up-modal-viewport">\n <div class="up-modal-dialog modal-dialog">\n <div class="up-modal-content modal-content"></div>\n </div>\n </div>\n</div>'}.call(this),function(){up.navigation.config.currentClasses.push("active")}.call(this),function(){}.call(this);
data/dist/unpoly.css CHANGED
@@ -14,22 +14,32 @@
14
14
  max-width: 100%;
15
15
  box-sizing: border-box;
16
16
  z-index: 99999999; }
17
- [up-href][up-follow], [up-href][up-target] {
17
+ [up-href] {
18
18
  cursor: pointer; }
19
- .up-modal {
19
+ .up-modal-backdrop {
20
+ z-index: 10000;
21
+ background-color: rgba(90, 90, 90, 0.4);
22
+ position: fixed;
23
+ top: 0;
24
+ right: 0;
25
+ bottom: 0;
26
+ left: 0; }
27
+
28
+ .up-modal-viewport {
29
+ z-index: 11000;
20
30
  position: fixed;
21
31
  top: 0;
22
32
  left: 0;
23
33
  bottom: 0;
24
34
  right: 0;
25
- z-index: 10000;
26
- background-color: rgba(90, 90, 90, 0.4);
27
35
  overflow-x: hidden;
28
- overflow-y: scroll;
36
+ overflow-y: hidden;
29
37
  text-align: center; }
38
+ .up-modal.up-modal-ready .up-modal-viewport {
39
+ overflow-y: scroll; }
30
40
 
31
41
  .up-modal-dialog {
32
- z-index: 1000;
42
+ z-index: 12000;
33
43
  position: relative;
34
44
  box-sizing: border-box;
35
45
  margin: 30px 10px;
@@ -38,13 +48,13 @@
38
48
  text-align: left; }
39
49
 
40
50
  .up-modal-content {
41
- z-index: 2000;
51
+ z-index: 13000;
42
52
  padding: 20px;
43
53
  background-color: #fff;
44
54
  box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3); }
45
55
 
46
56
  .up-modal-close {
47
- z-index: 3000;
57
+ z-index: 14000;
48
58
  position: absolute;
49
59
  right: 0;
50
60
  top: 0;
data/dist/unpoly.js CHANGED
@@ -23,11 +23,19 @@ that might save you from loading something like [Underscore.js](http://underscor
23
23
 
24
24
  up.util = (function($) {
25
25
 
26
+ /**
27
+ A function that does nothing.
28
+
29
+ @function up.util.noop
30
+ @experimental
31
+ */
32
+ var $createElementFromSelector, $createPlaceholder, ANIMATION_DEFERRED_KEY, all, any, appendRequestData, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, cssAnimate, detect, each, error, escapePressed, except, extend, extractOptions, findWithSelf, finishCssAnimate, fixedToAbsolute, forceCompositing, forceRepaint, intersect, isArray, isBlank, isDeferred, isDefined, isDetached, isElement, isFormData, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, multiSelector, nextFrame, nonUpClasses, noop, normalizeMethod, normalizeUrl, nullJQuery, offsetParent, once, only, option, options, parseUrl, pluckData, pluckKey, presence, presentAttr, reject, remove, requestDataAsArray, requestDataAsQuery, requestDataFromForm, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, selectorForElement, setMissingAttrs, setTimer, temporaryCss, times, titleFromXhr, toArray, trim, unJQuery, uniq, unresolvableDeferred, unresolvablePromise, unwrapElement;
33
+ noop = $.noop;
34
+
26
35
  /**
27
36
  @function up.util.memoize
28
37
  @internal
29
38
  */
30
- var $createElementFromSelector, $createPlaceholder, ANIMATION_DEFERRED_KEY, all, any, appendRequestData, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, cssAnimate, detect, each, error, escapePressed, except, extend, findWithSelf, finishCssAnimate, fixedToAbsolute, forceCompositing, intersect, isArray, isBlank, isDeferred, isDefined, isElement, isFormData, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, multiSelector, nextFrame, nonUpClasses, normalizeMethod, normalizeUrl, nullJQuery, offsetParent, once, only, option, options, parseUrl, pluckData, presence, presentAttr, reject, remove, requestDataAsArray, requestDataAsQuery, requestDataFromForm, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, selectorForElement, setMissingAttrs, temporaryCss, times, titleFromXhr, toArray, trim, unJQuery, uniq, unresolvableDeferred, unresolvablePromise, unwrapElement;
31
39
  memoize = function(func) {
32
40
  var cache, cached;
33
41
  cache = void 0;
@@ -899,6 +907,25 @@ that might save you from loading something like [Underscore.js](http://underscor
899
907
  return detect(values, isPresent);
900
908
  };
901
909
 
910
+ /**
911
+ Waits for the given number of milliseconds, the nruns the given callback.
912
+
913
+ If the number of milliseconds is zero, the callback is run in the current execution frame.
914
+ See [`up.util.nextFrame`] for running a function in the next executation frame.
915
+
916
+ @function up.util.setTimer
917
+ @param {Number} millis
918
+ @param {Function} callback
919
+ @experimental
920
+ */
921
+ setTimer = function(millis, callback) {
922
+ if (millis > 0) {
923
+ return setTimeout(callback, millis);
924
+ } else {
925
+ return callback();
926
+ }
927
+ };
928
+
902
929
  /**
903
930
  Schedules the given function to be called in the
904
931
  next Javascript execution frame.
@@ -1035,6 +1062,17 @@ that might save you from loading something like [Underscore.js](http://underscor
1035
1062
  return memo;
1036
1063
  };
1037
1064
 
1065
+ /**
1066
+ Forces a repaint of the given element.
1067
+
1068
+ @function up.util.forceRepaint
1069
+ @internal
1070
+ */
1071
+ forceRepaint = function(element) {
1072
+ element = unJQuery(element);
1073
+ return element.offsetHeight;
1074
+ };
1075
+
1038
1076
  /**
1039
1077
  Animates the given element's CSS properties using CSS transitions.
1040
1078
 
@@ -1062,7 +1100,7 @@ that might save you from loading something like [Underscore.js](http://underscor
1062
1100
  @internal
1063
1101
  */
1064
1102
  cssAnimate = function(elementOrSelector, lastFrame, opts) {
1065
- var $element, deferred, endTimeout, transition, withoutCompositing, withoutTransition;
1103
+ var $element, animationEnd, deferred, endTimeout, oldTransition, transition, withoutCompositing;
1066
1104
  $element = $(elementOrSelector);
1067
1105
  opts = options(opts, {
1068
1106
  duration: 300,
@@ -1076,18 +1114,32 @@ that might save you from loading something like [Underscore.js](http://underscor
1076
1114
  'transition-delay': opts.delay + "ms",
1077
1115
  'transition-timing-function': opts.easing
1078
1116
  };
1117
+ oldTransition = $element.css(Object.keys(transition));
1118
+ $element.addClass('up-animating');
1079
1119
  withoutCompositing = forceCompositing($element);
1080
- withoutTransition = temporaryCss($element, transition);
1120
+ $element.css(transition);
1081
1121
  $element.css(lastFrame);
1082
- deferred.then(withoutCompositing);
1083
- deferred.then(withoutTransition);
1084
1122
  $element.data(ANIMATION_DEFERRED_KEY, deferred);
1085
1123
  deferred.then(function() {
1086
- return $element.removeData(ANIMATION_DEFERRED_KEY);
1124
+ var hadTransitionBefore;
1125
+ $element.removeData(ANIMATION_DEFERRED_KEY);
1126
+ withoutCompositing();
1127
+ $element.css({
1128
+ 'transition': 'none'
1129
+ });
1130
+ hadTransitionBefore = !(oldTransition['transition-property'] === 'none' || (oldTransition['transition-property'] === 'all' && oldTransition['transition-duration'][0] === '0'));
1131
+ if (hadTransitionBefore) {
1132
+ forceRepaint($element);
1133
+ return $element.css(oldTransition);
1134
+ }
1135
+ });
1136
+ animationEnd = opts.duration + opts.delay;
1137
+ endTimeout = setTimer(animationEnd, function() {
1138
+ $element.removeClass('up-animating');
1139
+ if (!isDetached($element)) {
1140
+ return deferred.resolve();
1141
+ }
1087
1142
  });
1088
- endTimeout = setTimeout((function() {
1089
- return deferred.resolve();
1090
- }), opts.duration + opts.delay);
1091
1143
  deferred.then(function() {
1092
1144
  return clearTimeout(endTimeout);
1093
1145
  });
@@ -1526,11 +1578,36 @@ that might save you from loading something like [Underscore.js](http://underscor
1526
1578
  @internal
1527
1579
  */
1528
1580
  cache = function(config) {
1529
- var alias, clear, expiryMilis, get, isFresh, keys, log, maxSize, normalizeStoreKey, set, store, timestamp;
1581
+ var alias, clear, expiryMillis, get, isEnabled, isFresh, keys, log, makeRoomForAnotherKey, maxKeys, normalizeStoreKey, optionEvaluator, set, store, timestamp;
1530
1582
  if (config == null) {
1531
1583
  config = {};
1532
1584
  }
1533
1585
  store = void 0;
1586
+ optionEvaluator = function(name) {
1587
+ return function() {
1588
+ var value;
1589
+ value = config[name];
1590
+ if (isNumber(value)) {
1591
+ return value;
1592
+ } else if (isFunction(value)) {
1593
+ return value();
1594
+ } else {
1595
+ return void 0;
1596
+ }
1597
+ };
1598
+ };
1599
+ maxKeys = optionEvaluator('size');
1600
+ expiryMillis = optionEvaluator('expiry');
1601
+ normalizeStoreKey = function(key) {
1602
+ if (config.key) {
1603
+ return config.key(key);
1604
+ } else {
1605
+ return key.toString();
1606
+ }
1607
+ };
1608
+ isEnabled = function() {
1609
+ return maxKeys() !== 0 && expiryMillis() !== 0;
1610
+ };
1534
1611
  clear = function() {
1535
1612
  return store = {};
1536
1613
  };
@@ -1538,48 +1615,19 @@ that might save you from loading something like [Underscore.js](http://underscor
1538
1615
  log = function() {
1539
1616
  var args;
1540
1617
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
1541
- if (config.log) {
1542
- args[0] = "[" + config.log + "] " + args[0];
1618
+ if (config.logPrefix) {
1619
+ args[0] = "[" + config.logPrefix + "] " + args[0];
1543
1620
  return up.puts.apply(up, args);
1544
1621
  }
1545
1622
  };
1546
1623
  keys = function() {
1547
1624
  return Object.keys(store);
1548
1625
  };
1549
- maxSize = function() {
1550
- if (isMissing(config.size)) {
1551
- return void 0;
1552
- } else if (isFunction(config.size)) {
1553
- return config.size();
1554
- } else if (isNumber(config.size)) {
1555
- return config.size;
1556
- } else {
1557
- return error("Invalid size config: %o", config.size);
1558
- }
1559
- };
1560
- expiryMilis = function() {
1561
- if (isMissing(config.expiry)) {
1562
- return void 0;
1563
- } else if (isFunction(config.expiry)) {
1564
- return config.expiry();
1565
- } else if (isNumber(config.expiry)) {
1566
- return config.expiry;
1567
- } else {
1568
- return error("Invalid expiry config: %o", config.expiry);
1569
- }
1570
- };
1571
- normalizeStoreKey = function(key) {
1572
- if (config.key) {
1573
- return config.key(key);
1574
- } else {
1575
- return key.toString();
1576
- }
1577
- };
1578
- trim = function() {
1579
- var oldestKey, oldestTimestamp, size, storeKeys;
1626
+ makeRoomForAnotherKey = function() {
1627
+ var max, oldestKey, oldestTimestamp, storeKeys;
1580
1628
  storeKeys = copy(keys());
1581
- size = maxSize();
1582
- if (size && storeKeys.length > size) {
1629
+ max = maxKeys();
1630
+ if (max && storeKeys.length >= max) {
1583
1631
  oldestKey = null;
1584
1632
  oldestTimestamp = null;
1585
1633
  each(storeKeys, function(key) {
@@ -1610,11 +1658,14 @@ that might save you from loading something like [Underscore.js](http://underscor
1610
1658
  };
1611
1659
  set = function(key, value) {
1612
1660
  var storeKey;
1613
- storeKey = normalizeStoreKey(key);
1614
- return store[storeKey] = {
1615
- timestamp: timestamp(),
1616
- value: value
1617
- };
1661
+ if (isEnabled()) {
1662
+ makeRoomForAnotherKey();
1663
+ storeKey = normalizeStoreKey(key);
1664
+ return store[storeKey] = {
1665
+ timestamp: timestamp(),
1666
+ value: value
1667
+ };
1668
+ }
1618
1669
  };
1619
1670
  remove = function(key) {
1620
1671
  var storeKey;
@@ -1622,11 +1673,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1622
1673
  return delete store[storeKey];
1623
1674
  };
1624
1675
  isFresh = function(entry) {
1625
- var expiry, timeSinceTouch;
1626
- expiry = expiryMilis();
1627
- if (expiry) {
1676
+ var millis, timeSinceTouch;
1677
+ millis = expiryMillis();
1678
+ if (millis) {
1628
1679
  timeSinceTouch = timestamp() - entry.timestamp;
1629
- return timeSinceTouch < expiryMilis();
1680
+ return timeSinceTouch < millis;
1630
1681
  } else {
1631
1682
  return true;
1632
1683
  }
@@ -1868,6 +1919,12 @@ that might save you from loading something like [Underscore.js](http://underscor
1868
1919
  $error.text(asString);
1869
1920
  throw new Error(asString);
1870
1921
  };
1922
+ pluckKey = function(object, key) {
1923
+ var value;
1924
+ value = object[key];
1925
+ delete object[key];
1926
+ return value;
1927
+ };
1871
1928
  pluckData = function(elementOrSelector, key) {
1872
1929
  var $element, value;
1873
1930
  $element = $(elementOrSelector);
@@ -1875,7 +1932,29 @@ that might save you from loading something like [Underscore.js](http://underscor
1875
1932
  $element.removeData(key);
1876
1933
  return value;
1877
1934
  };
1935
+ extractOptions = function(args) {
1936
+ var lastArg;
1937
+ lastArg = last(args);
1938
+ if (isObject(lastArg)) {
1939
+ return args.pop();
1940
+ } else {
1941
+ return {};
1942
+ }
1943
+ };
1944
+
1945
+ /**
1946
+ Returns whether the given element has been detached from the DOM
1947
+ (or whether it was never attached).
1948
+
1949
+ @function up.util.isDetached
1950
+ @internal
1951
+ */
1952
+ isDetached = function(element) {
1953
+ element = unJQuery(element);
1954
+ return !jQuery.contains(document.documentElement, element);
1955
+ };
1878
1956
  return {
1957
+ isDetached: isDetached,
1879
1958
  requestDataAsArray: requestDataAsArray,
1880
1959
  requestDataAsQuery: requestDataAsQuery,
1881
1960
  appendRequestData: appendRequestData,
@@ -1931,12 +2010,14 @@ that might save you from loading something like [Underscore.js](http://underscor
1931
2010
  isUnmodifiedMouseEvent: isUnmodifiedMouseEvent,
1932
2011
  nullJQuery: nullJQuery,
1933
2012
  unJQuery: unJQuery,
2013
+ setTimer: setTimer,
1934
2014
  nextFrame: nextFrame,
1935
2015
  measure: measure,
1936
2016
  temporaryCss: temporaryCss,
1937
2017
  cssAnimate: cssAnimate,
1938
2018
  finishCssAnimate: finishCssAnimate,
1939
2019
  forceCompositing: forceCompositing,
2020
+ forceRepaint: forceRepaint,
1940
2021
  escapePressed: escapePressed,
1941
2022
  copyAttributes: copyAttributes,
1942
2023
  findWithSelf: findWithSelf,
@@ -1964,7 +2045,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1964
2045
  unwrapElement: unwrapElement,
1965
2046
  multiSelector: multiSelector,
1966
2047
  error: error,
1967
- pluckData: pluckData
2048
+ pluckData: pluckData,
2049
+ pluckKey: pluckKey,
2050
+ extractOptions: extractOptions,
2051
+ isDetached: isDetached,
2052
+ noop: noop
1968
2053
  };
1969
2054
  })($);
1970
2055
 
@@ -2046,7 +2131,7 @@ printed message.
2046
2131
  message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
2047
2132
  block = args.pop();
2048
2133
  if (message) {
2049
- (ref = up.browser).puts.apply(ref, ['groupCollapsed', prefix(message)].concat(slice.call(args)));
2134
+ (ref = up.browser).puts.apply(ref, ['group', prefix(message)].concat(slice.call(args)));
2050
2135
  try {
2051
2136
  return block();
2052
2137
  } finally {
@@ -3096,6 +3181,7 @@ later.
3096
3181
 
3097
3182
  Examples for built-in macros are [`up-dash`](/up-dash) and [`up-expand`](/up-expand).
3098
3183
 
3184
+ @function up.macro
3099
3185
  @param {String} selector
3100
3186
  The selector to match.
3101
3187
  @param {Object} options
@@ -3649,7 +3735,7 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
3649
3735
  */
3650
3736
  config = u.config({
3651
3737
  duration: 0,
3652
- viewports: [document, '.up-modal', '[up-viewport]'],
3738
+ viewports: [document, '.up-modal-viewport', '[up-viewport]'],
3653
3739
  fixedTop: ['[up-fixed~=top]'],
3654
3740
  fixedBottom: ['[up-fixed~=bottom]'],
3655
3741
  anchoredRight: ['[up-anchored~=right]', '[up-fixed~=top]', '[up-fixed~=bottom]', '[up-fixed~=right]'],
@@ -4993,7 +5079,9 @@ are based on this module.
4993
5079
  destroyMessage = ['Destroying fragment %o', $element.get(0)];
4994
5080
  destroyedMessage = ['Destroyed fragment %o', $element.get(0)];
4995
5081
  }
4996
- if (up.bus.nobodyPrevents('up:fragment:destroy', {
5082
+ if ($element.length === 0) {
5083
+ return u.resolvedDeferred();
5084
+ } else if (up.bus.nobodyPrevents('up:fragment:destroy', {
4997
5085
  $element: $element,
4998
5086
  message: destroyMessage
4999
5087
  })) {
@@ -5151,8 +5239,10 @@ or [transitions](/up.transition) using Javascript or CSS.
5151
5239
  */
5152
5240
 
5153
5241
  (function() {
5242
+ var slice = [].slice;
5243
+
5154
5244
  up.motion = (function($) {
5155
- var GHOSTING_DEFERRED_KEY, animate, animateOptions, animation, animations, assertIsDeferred, config, defaultAnimations, defaultTransitions, ensureMorphable, findAnimation, finish, finishGhosting, isEnabled, morph, none, prependCopy, reset, resolvableWhen, skipMorph, snapshot, transition, transitions, u, withGhosts;
5245
+ var GHOSTING_CLASS, GHOSTING_DEFERRED_KEY, animate, animateOptions, animation, animations, assertIsDeferred, config, defaultAnimations, defaultTransitions, ensureMorphable, findAnimation, finish, finishGhosting, isEnabled, isNone, morph, none, prependCopy, reset, resolvableWhen, skipMorph, snapshot, transition, transitions, translateCss, u, withGhosts;
5156
5246
  u = up.util;
5157
5247
  animations = {};
5158
5248
  defaultAnimations = {};
@@ -5164,8 +5254,14 @@ or [transitions](/up.transition) using Javascript or CSS.
5164
5254
 
5165
5255
  @property up.motion.config
5166
5256
  @param {Number} [config.duration=300]
5257
+ The default duration for all animations and transitions (in milliseconds).
5167
5258
  @param {Number} [config.delay=0]
5259
+ The default delay for all animations and transitions (in milliseconds).
5168
5260
  @param {String} [config.easing='ease']
5261
+ The default timing function that controls the acceleration of animations and transitions.
5262
+
5263
+ See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
5264
+ for a list of pre-defined timing functions.
5169
5265
  @param {Boolean} [config.enabled=true]
5170
5266
  Whether animation is enabled.
5171
5267
 
@@ -5268,6 +5364,7 @@ or [transitions](/up.transition) using Javascript or CSS.
5268
5364
  The delay before the animation starts, in milliseconds.
5269
5365
  @param {String} [options.easing='ease']
5270
5366
  The timing function that controls the animation's acceleration.
5367
+
5271
5368
  See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
5272
5369
  for a list of pre-defined timing functions.
5273
5370
  @return {Promise}
@@ -5305,29 +5402,34 @@ or [transitions](/up.transition) using Javascript or CSS.
5305
5402
  @function up.motion.animateOptions
5306
5403
  @internal
5307
5404
  */
5308
- animateOptions = function(allOptions, $element) {
5309
- var options;
5310
- if ($element == null) {
5311
- $element = null;
5312
- }
5313
- allOptions = u.options(allOptions);
5405
+ animateOptions = function() {
5406
+ var $element, args, moduleDefaults, options, userOptions;
5407
+ userOptions = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
5408
+ userOptions = u.options(userOptions);
5409
+ moduleDefaults = u.extractOptions(args);
5410
+ $element = args.length > 1 ? args[1] : void 0;
5314
5411
  options = {};
5315
- options.easing = u.option(allOptions.easing, $element != null ? $element.attr('up-easing') : void 0, config.easing);
5316
- options.duration = Number(u.option(allOptions.duration, $element != null ? $element.attr('up-duration') : void 0, config.duration));
5317
- options.delay = Number(u.option(allOptions.delay, $element != null ? $element.attr('up-delay') : void 0, config.delay));
5412
+ options.easing = u.option(userOptions.easing, $element != null ? $element.attr('up-easing') : void 0, moduleDefaults.easing, config.easing);
5413
+ options.duration = Number(u.option(userOptions.duration, $element != null ? $element.attr('up-duration') : void 0, moduleDefaults.duration, config.duration));
5414
+ options.delay = Number(u.option(userOptions.delay, $element != null ? $element.attr('up-delay') : void 0, moduleDefaults.delay, config.delay));
5318
5415
  return options;
5319
5416
  };
5320
5417
  findAnimation = function(name) {
5321
5418
  return animations[name] || u.error("Unknown animation %o", name);
5322
5419
  };
5323
5420
  GHOSTING_DEFERRED_KEY = 'up-ghosting-deferred';
5421
+ GHOSTING_CLASS = 'up-ghosting';
5324
5422
  withGhosts = function($old, $new, options, block) {
5325
- var $viewport, deferred, newCopy, newScrollTop, oldCopy, oldScrollTop, showNew;
5423
+ var $both, $viewport, deferred, newCopy, newScrollTop, oldCopy, oldScrollTop, showNew;
5424
+ if (options.copy === false || $old.is('.up-ghost') || $new.is('.up-ghost')) {
5425
+ return block($old, $new);
5426
+ }
5326
5427
  oldCopy = void 0;
5327
5428
  newCopy = void 0;
5328
5429
  oldScrollTop = void 0;
5329
5430
  newScrollTop = void 0;
5330
5431
  $viewport = up.layout.viewportOf($old);
5432
+ $both = $old.add($new);
5331
5433
  u.temporaryCss($new, {
5332
5434
  display: 'none'
5333
5435
  }, function() {
@@ -5347,11 +5449,11 @@ or [transitions](/up.transition) using Javascript or CSS.
5347
5449
  opacity: '0'
5348
5450
  });
5349
5451
  deferred = block(oldCopy.$ghost, newCopy.$ghost);
5350
- $old.data(GHOSTING_DEFERRED_KEY, deferred);
5351
- $new.data(GHOSTING_DEFERRED_KEY, deferred);
5452
+ $both.data(GHOSTING_DEFERRED_KEY, deferred);
5453
+ $both.addClass(GHOSTING_CLASS);
5352
5454
  deferred.then(function() {
5353
- $old.removeData(GHOSTING_DEFERRED_KEY);
5354
- $new.removeData(GHOSTING_DEFERRED_KEY);
5455
+ $both.removeData(GHOSTING_DEFERRED_KEY);
5456
+ $both.removeClass(GHOSTING_CLASS);
5355
5457
  showNew();
5356
5458
  oldCopy.$bounds.remove();
5357
5459
  return newCopy.$bounds.remove();
@@ -5360,22 +5462,30 @@ or [transitions](/up.transition) using Javascript or CSS.
5360
5462
  };
5361
5463
 
5362
5464
  /**
5363
- Completes all [animations](/up.animate) and [transitions](/up.morph)
5364
- for the given element by jumping to the last animation frame instantly.
5465
+ Completes [animations](/up.animate) and [transitions](/up.morph).
5365
5466
 
5366
- All callbacks chained to the original animation's promise will be called.
5467
+ If called without arguments, all animations on the screen are completed.
5468
+ If given an element (or selector), animations on that element and its children
5469
+ are completed.
5367
5470
 
5368
- Does nothing if the given element is not currently animating.
5471
+ Animations are completed by jumping to the last animation frame instantly.
5472
+
5473
+ Does nothing if there are no animation to complete.
5369
5474
 
5370
5475
  @function up.motion.finish
5371
- @param {Element|jQuery|String} elementOrSelector
5476
+ @param {Element|jQuery|String} [elementOrSelector]
5372
5477
  @stable
5373
5478
  */
5374
5479
  finish = function(elementOrSelector) {
5375
- var $element;
5480
+ var $animatingSubtree, $element, $ghostingSubtree;
5481
+ if (elementOrSelector == null) {
5482
+ elementOrSelector = '.up-animating';
5483
+ }
5376
5484
  $element = $(elementOrSelector);
5377
- u.finishCssAnimate($element);
5378
- return finishGhosting($element);
5485
+ $animatingSubtree = u.findWithSelf($element, '.up-animating');
5486
+ u.finishCssAnimate($animatingSubtree);
5487
+ $ghostingSubtree = u.findWithSelf($element, "." + GHOSTING_CLASS);
5488
+ return finishGhosting($ghostingSubtree);
5379
5489
  };
5380
5490
  finishGhosting = function($collection) {
5381
5491
  return $collection.each(function() {
@@ -5449,6 +5559,7 @@ or [transitions](/up.transition) using Javascript or CSS.
5449
5559
  The delay before the animation starts, in milliseconds.
5450
5560
  @param {String} [options.easing='ease']
5451
5561
  The timing function that controls the transition's acceleration.
5562
+
5452
5563
  See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
5453
5564
  for a list of pre-defined timing functions.
5454
5565
  @param {Boolean} [options.reveal=false]
@@ -5462,6 +5573,7 @@ or [transitions](/up.transition) using Javascript or CSS.
5462
5573
  if (transitionOrName === 'none') {
5463
5574
  transitionOrName = false;
5464
5575
  }
5576
+ options = u.options(options);
5465
5577
  $old = $(source);
5466
5578
  $new = $(target);
5467
5579
  ensureMorphable($old, transitionOrName);
@@ -5672,6 +5784,17 @@ or [transitions](/up.transition) using Javascript or CSS.
5672
5784
  @stable
5673
5785
  */
5674
5786
  none = u.resolvedDeferred;
5787
+
5788
+ /**
5789
+ Returns whether the given animation option will cause the animation
5790
+ to be skipped.
5791
+
5792
+ @function up.motion.isNone
5793
+ @internal
5794
+ */
5795
+ isNone = function(animation) {
5796
+ return animation === false || animation === 'none' || animation === none;
5797
+ };
5675
5798
  animation('none', none);
5676
5799
  animation('fade-in', function($ghost, options) {
5677
5800
  $ghost.css({
@@ -5689,104 +5812,79 @@ or [transitions](/up.transition) using Javascript or CSS.
5689
5812
  opacity: 0
5690
5813
  }, options);
5691
5814
  });
5815
+ translateCss = function(x, y) {
5816
+ return {
5817
+ transform: "translate(" + x + "px, " + y + "px)"
5818
+ };
5819
+ };
5692
5820
  animation('move-to-top', function($ghost, options) {
5693
5821
  var box, travelDistance;
5694
5822
  box = u.measure($ghost);
5695
5823
  travelDistance = box.top + box.height;
5696
- $ghost.css({
5697
- 'margin-top': '0px'
5698
- });
5699
- return animate($ghost, {
5700
- 'margin-top': "-" + travelDistance + "px"
5701
- }, options);
5824
+ $ghost.css(translateCss(0, 0));
5825
+ return animate($ghost, translateCss(0, -travelDistance), options);
5702
5826
  });
5703
5827
  animation('move-from-top', function($ghost, options) {
5704
5828
  var box, travelDistance;
5705
5829
  box = u.measure($ghost);
5706
5830
  travelDistance = box.top + box.height;
5707
- $ghost.css({
5708
- 'margin-top': "-" + travelDistance + "px"
5709
- });
5710
- return animate($ghost, {
5711
- 'margin-top': '0px'
5712
- }, options);
5831
+ $ghost.css(translateCss(0, -travelDistance));
5832
+ return animate($ghost, translateCss(0, 0), options);
5713
5833
  });
5714
5834
  animation('move-to-bottom', function($ghost, options) {
5715
5835
  var box, travelDistance;
5716
5836
  box = u.measure($ghost);
5717
5837
  travelDistance = u.clientSize().height - box.top;
5718
- $ghost.css({
5719
- 'margin-top': '0px'
5720
- });
5721
- return animate($ghost, {
5722
- 'margin-top': travelDistance + "px"
5723
- }, options);
5838
+ $ghost.css(translateCss(0, 0));
5839
+ return animate($ghost, translateCss(0, travelDistance), options);
5724
5840
  });
5725
5841
  animation('move-from-bottom', function($ghost, options) {
5726
5842
  var box, travelDistance;
5727
5843
  box = u.measure($ghost);
5728
5844
  travelDistance = u.clientSize().height - box.top;
5729
- $ghost.css({
5730
- 'margin-top': travelDistance + "px"
5731
- });
5732
- return animate($ghost, {
5733
- 'margin-top': '0px'
5734
- }, options);
5845
+ $ghost.css(translateCss(0, travelDistance));
5846
+ return animate($ghost, translateCss(0, 0), options);
5735
5847
  });
5736
5848
  animation('move-to-left', function($ghost, options) {
5737
5849
  var box, travelDistance;
5738
5850
  box = u.measure($ghost);
5739
5851
  travelDistance = box.left + box.width;
5740
- $ghost.css({
5741
- 'margin-left': '0px'
5742
- });
5743
- return animate($ghost, {
5744
- 'margin-left': "-" + travelDistance + "px"
5745
- }, options);
5852
+ $ghost.css(translateCss(0, 0));
5853
+ return animate($ghost, translateCss(-travelDistance, 0), options);
5746
5854
  });
5747
5855
  animation('move-from-left', function($ghost, options) {
5748
5856
  var box, travelDistance;
5749
5857
  box = u.measure($ghost);
5750
5858
  travelDistance = box.left + box.width;
5751
- $ghost.css({
5752
- 'margin-left': "-" + travelDistance + "px"
5753
- });
5754
- return animate($ghost, {
5755
- 'margin-left': '0px'
5756
- }, options);
5859
+ $ghost.css(translateCss(-travelDistance, 0));
5860
+ return animate($ghost, translateCss(0, 0), options);
5757
5861
  });
5758
5862
  animation('move-to-right', function($ghost, options) {
5759
5863
  var box, travelDistance;
5760
5864
  box = u.measure($ghost);
5761
5865
  travelDistance = u.clientSize().width - box.left;
5762
- $ghost.css({
5763
- 'margin-left': '0px'
5764
- });
5765
- return animate($ghost, {
5766
- 'margin-left': travelDistance + "px"
5767
- }, options);
5866
+ $ghost.css(translateCss(0, 0));
5867
+ return animate($ghost, translateCss(travelDistance, 0), options);
5768
5868
  });
5769
5869
  animation('move-from-right', function($ghost, options) {
5770
5870
  var box, travelDistance;
5771
5871
  box = u.measure($ghost);
5772
5872
  travelDistance = u.clientSize().width - box.left;
5773
- $ghost.css({
5774
- 'margin-left': travelDistance + "px"
5775
- });
5776
- return animate($ghost, {
5777
- 'margin-left': '0px'
5778
- }, options);
5873
+ $ghost.css(translateCss(travelDistance, 0));
5874
+ return animate($ghost, translateCss(0, 0), options);
5779
5875
  });
5780
5876
  animation('roll-down', function($ghost, options) {
5781
- var fullHeight, styleMemo;
5877
+ var deferred, fullHeight, styleMemo;
5782
5878
  fullHeight = $ghost.height();
5783
5879
  styleMemo = u.temporaryCss($ghost, {
5784
5880
  height: '0px',
5785
5881
  overflow: 'hidden'
5786
5882
  });
5787
- return animate($ghost, {
5883
+ deferred = animate($ghost, {
5788
5884
  height: fullHeight + "px"
5789
- }, options).then(styleMemo);
5885
+ }, options);
5886
+ deferred.then(styleMemo);
5887
+ return deferred;
5790
5888
  });
5791
5889
  transition('none', none);
5792
5890
  transition('move-left', function($old, $new, options) {
@@ -5820,7 +5918,8 @@ or [transitions](/up.transition) using Javascript or CSS.
5820
5918
  },
5821
5919
  none: none,
5822
5920
  when: resolvableWhen,
5823
- prependCopy: prependCopy
5921
+ prependCopy: prependCopy,
5922
+ isNone: isNone
5824
5923
  };
5825
5924
  })(jQuery);
5826
5925
 
@@ -5858,7 +5957,7 @@ the user performs the click.
5858
5957
  var slice = [].slice;
5859
5958
 
5860
5959
  up.proxy = (function($) {
5861
- var $waitingLink, ajax, alias, cache, cacheKey, cancelBusyDelay, cancelPreloadDelay, checkPreload, clear, config, get, isBusy, isIdempotent, isIdle, load, loadEnded, loadOrQueue, loadStarted, normalizeRequest, pendingCount, pokeQueue, preload, preloadDelayTimer, queue, queuedRequests, remove, reset, responseReceived, set, slowDelayTimer, slowEventEmitted, startPreloadDelay, u;
5960
+ var $waitingLink, ajax, alias, cache, cacheKey, cancelPreloadDelay, cancelSlowDelay, checkPreload, clear, config, get, isBusy, isIdempotent, isIdle, load, loadEnded, loadOrQueue, loadStarted, normalizeRequest, pendingCount, pokeQueue, preload, preloadDelayTimer, queue, queuedRequests, remove, reset, responseReceived, set, slowDelayTimer, slowEventEmitted, startPreloadDelay, u;
5862
5961
  u = up.util;
5863
5962
  $waitingLink = void 0;
5864
5963
  preloadDelayTimer = void 0;
@@ -5960,65 +6059,25 @@ the user performs the click.
5960
6059
  }
5961
6060
  }
5962
6061
  };
5963
-
5964
- /**
5965
- Manually stores a promise for the response to the given request.
5966
-
5967
- @function up.proxy.set
5968
- @param {String} request.url
5969
- @param {String} [request.method='GET']
5970
- @param {String} [request.target='body']
5971
- @param {Promise} response
5972
- A promise for the response that is API-compatible with the
5973
- promise returned by [`jQuery.ajax`](http://api.jquery.com/jquery.ajax/).
5974
- @experimental
5975
- */
5976
- set = cache.set;
5977
-
5978
- /**
5979
- Manually removes the given request from the cache.
5980
-
5981
- You can also [configure](/up.proxy.config) when the proxy
5982
- automatically removes cache entries.
5983
-
5984
- @function up.proxy.remove
5985
- @param {String} request.url
5986
- @param {String} [request.method='GET']
5987
- @param {String} [request.target='body']
5988
- @experimental
5989
- */
5990
- remove = cache.remove;
5991
-
5992
- /**
5993
- Removes all cache entries.
5994
-
5995
- Unpoly also automatically clears the cache whenever it processes
5996
- a request with a non-GET HTTP method.
5997
-
5998
- @function up.proxy.clear
5999
- @stable
6000
- */
6001
- clear = cache.clear;
6002
6062
  cancelPreloadDelay = function() {
6003
6063
  clearTimeout(preloadDelayTimer);
6004
6064
  return preloadDelayTimer = null;
6005
6065
  };
6006
- cancelBusyDelay = function() {
6066
+ cancelSlowDelay = function() {
6007
6067
  clearTimeout(slowDelayTimer);
6008
6068
  return slowDelayTimer = null;
6009
6069
  };
6010
6070
  reset = function() {
6011
6071
  $waitingLink = null;
6012
6072
  cancelPreloadDelay();
6013
- cancelBusyDelay();
6073
+ cancelSlowDelay();
6014
6074
  pendingCount = 0;
6015
6075
  config.reset();
6016
- slowEventEmitted = false;
6017
6076
  cache.clear();
6077
+ slowEventEmitted = false;
6018
6078
  return queuedRequests = [];
6019
6079
  };
6020
6080
  reset();
6021
- alias = cache.alias;
6022
6081
  normalizeRequest = function(request) {
6023
6082
  if (!request._normalized) {
6024
6083
  request.method = u.normalizeMethod(request.method);
@@ -6040,13 +6099,23 @@ the user performs the click.
6040
6099
  Only requests with a method of `GET`, `OPTIONS` and `HEAD`
6041
6100
  are considered to be read-only.
6042
6101
 
6102
+ \#\#\#\# Example
6103
+
6104
+ up.ajax('/search', data: { query: 'sunshine' }).then(function(data, status, xhr) {
6105
+ console.log('The response body is %o', data);
6106
+ }).fail(function(xhr, status, error) {
6107
+ console.error('The request failed');
6108
+ });
6109
+
6110
+ \#\#\#\# Events
6111
+
6043
6112
  If a network connection is attempted, the proxy will emit
6044
- a `up:proxy:load` event with the `request` as its argument.
6045
- Once the response is received, a `up:proxy:receive` event will
6113
+ a [`up:proxy:load`](/up:proxy:load) event with the `request` as its argument.
6114
+ Once the response is received, a [`up:proxy:receive`](/up:proxy:receive) event will
6046
6115
  be emitted.
6047
6116
 
6048
6117
  @function up.ajax
6049
- @param {String} request.url
6118
+ @param {String} url
6050
6119
  @param {String} [request.method='GET']
6051
6120
  @param {String} [request.target='body']
6052
6121
  @param {Boolean} [request.cache]
@@ -6057,13 +6126,21 @@ the user performs the click.
6057
6126
  with the request.
6058
6127
  @param {Object} [request.data={}]
6059
6128
  An object of request parameters.
6129
+ @param {String} [request.url]
6130
+ You can omit the first string argument and pass the URL as
6131
+ a `request` property instead.
6060
6132
  @return
6061
6133
  A promise for the response that is API-compatible with the
6062
6134
  promise returned by [`jQuery.ajax`](http://api.jquery.com/jquery.ajax/).
6063
6135
  @stable
6064
6136
  */
6065
- ajax = function(options) {
6066
- var forceCache, ignoreCache, pending, promise, request;
6137
+ ajax = function() {
6138
+ var args, forceCache, ignoreCache, options, pending, promise, request;
6139
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
6140
+ options = u.extractOptions(args);
6141
+ if (u.isGiven(args[0])) {
6142
+ options.url = args[0];
6143
+ }
6067
6144
  forceCache = options.cache === true;
6068
6145
  ignoreCache = options.cache === false;
6069
6146
  request = u.only(options, 'url', 'method', 'data', 'target', 'headers', '_normalized');
@@ -6128,11 +6205,7 @@ the user performs the click.
6128
6205
  return slowEventEmitted = true;
6129
6206
  }
6130
6207
  };
6131
- if (config.slowDelay > 0) {
6132
- return slowDelayTimer = setTimeout(emission, config.slowDelay);
6133
- } else {
6134
- return emission();
6135
- }
6208
+ return slowDelayTimer = u.setTimer(config.slowDelay, emission);
6136
6209
  }
6137
6210
  };
6138
6211
 
@@ -6279,6 +6352,59 @@ the user performs the click.
6279
6352
  }
6280
6353
  };
6281
6354
 
6355
+ /**
6356
+ Makes the proxy assume that `newRequest` has the same response as the
6357
+ already cached `oldRequest`.
6358
+
6359
+ Unpoly uses this internally when the user redirects from `/old` to `/new`.
6360
+ In that case, both `/old` and `/new` will cache the same response from `/new`.
6361
+
6362
+ @function up.proxy.alias
6363
+ @param {Object} oldRequest
6364
+ @param {Object} newRequest
6365
+ @experimental
6366
+ */
6367
+ alias = cache.alias;
6368
+
6369
+ /**
6370
+ Manually stores a promise for the response to the given request.
6371
+
6372
+ @function up.proxy.set
6373
+ @param {String} request.url
6374
+ @param {String} [request.method='GET']
6375
+ @param {String} [request.target='body']
6376
+ @param {Promise} response
6377
+ A promise for the response that is API-compatible with the
6378
+ promise returned by [`jQuery.ajax`](http://api.jquery.com/jquery.ajax/).
6379
+ @experimental
6380
+ */
6381
+ set = cache.set;
6382
+
6383
+ /**
6384
+ Manually removes the given request from the cache.
6385
+
6386
+ You can also [configure](/up.proxy.config) when the proxy
6387
+ automatically removes cache entries.
6388
+
6389
+ @function up.proxy.remove
6390
+ @param {String} request.url
6391
+ @param {String} [request.method='GET']
6392
+ @param {String} [request.target='body']
6393
+ @experimental
6394
+ */
6395
+ remove = cache.remove;
6396
+
6397
+ /**
6398
+ Removes all cache entries.
6399
+
6400
+ Unpoly also automatically clears the cache whenever it processes
6401
+ a request with a non-GET HTTP method.
6402
+
6403
+ @function up.proxy.clear
6404
+ @stable
6405
+ */
6406
+ clear = cache.clear;
6407
+
6282
6408
  /**
6283
6409
  This event is [emitted]/(up.emit) before an [AJAX request](/up.ajax)
6284
6410
  is starting to load.
@@ -6617,14 +6743,22 @@ Read on
6617
6743
  */
6618
6744
  allowDefault = function(event) {};
6619
6745
  onAction = function(selector, handler) {
6746
+ var handlerWithActiveMark;
6620
6747
  followVariantSelectors.push(selector);
6748
+ handlerWithActiveMark = function($link) {
6749
+ return up.navigation.withActiveMark($link, {
6750
+ enlarge: true
6751
+ }, function() {
6752
+ return handler($link);
6753
+ });
6754
+ };
6621
6755
  up.on('click', "a" + selector + ", [up-href]" + selector, function(event, $link) {
6622
6756
  if (shouldProcessLinkEvent(event, $link)) {
6623
6757
  if ($link.is('[up-instant]')) {
6624
6758
  return event.preventDefault();
6625
6759
  } else {
6626
6760
  event.preventDefault();
6627
- return handler($link);
6761
+ return handlerWithActiveMark($link);
6628
6762
  }
6629
6763
  } else {
6630
6764
  return allowDefault(event);
@@ -6633,7 +6767,7 @@ Read on
6633
6767
  return up.on('mousedown', "a" + selector + "[up-instant], [up-href]" + selector + "[up-instant]", function(event, $link) {
6634
6768
  if (shouldProcessLinkEvent(event, $link)) {
6635
6769
  event.preventDefault();
6636
- return handler($link);
6770
+ return handlerWithActiveMark($link);
6637
6771
  }
6638
6772
  });
6639
6773
  };
@@ -7056,14 +7190,14 @@ open dialogs with sub-forms, etc. all without losing form state.
7056
7190
  return u.unresolvablePromise();
7057
7191
  }
7058
7192
  }
7059
- $form.addClass('up-active');
7193
+ up.navigation.markActive($form);
7060
7194
  if (!(canAjaxSubmit && canHistoryOption)) {
7061
7195
  $form.get(0).submit();
7062
7196
  return u.unresolvablePromise();
7063
7197
  }
7064
7198
  promise = up.replace(target, url, options);
7065
7199
  promise.always(function() {
7066
- return $form.removeClass('up-active');
7200
+ return up.navigation.unmarkActive($form);
7067
7201
  });
7068
7202
  return promise;
7069
7203
  };
@@ -7184,11 +7318,7 @@ open dialogs with sub-forms, etc. all without losing form state.
7184
7318
  }
7185
7319
  });
7186
7320
  };
7187
- if (delay === 0) {
7188
- return runAndChain();
7189
- } else {
7190
- return setTimeout(runAndChain, delay);
7191
- }
7321
+ return u.setTimer(delay, runAndChain);
7192
7322
  }
7193
7323
  }
7194
7324
  };
@@ -8333,10 +8463,13 @@ The easiest way to change how the dialog looks is by overriding the [default CSS
8333
8463
  By default the dialog uses the following DOM structure:
8334
8464
 
8335
8465
  <div class="up-modal">
8336
- <div class="up-modal-dialog">
8337
- <div class="up-modal-close" up-close>X</div>
8338
- <div class="up-modal-content">
8339
- ...
8466
+ <div class="up-modal-backdrop">
8467
+ <div class="up-modal-viewport">
8468
+ <div class="up-modal-dialog">
8469
+ <div class="up-modal-content">
8470
+ ...
8471
+ </div>
8472
+ <div class="up-modal-close" up-close>X</div>
8340
8473
  </div>
8341
8474
  </div>
8342
8475
  </div>
@@ -8363,13 +8496,15 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8363
8496
 
8364
8497
  (function() {
8365
8498
  up.modal = (function($) {
8366
- var autoclose, close, config, contains, coveredUrl, createFrame, currentUrl, discardHistory, follow, isOpen, open, reset, shiftElements, templateHtml, u, unshiftElements, unshifters, visit;
8499
+ var autoclose, close, config, contains, coveredUrl, createFrame, currentUrl, discardHistory, extract, follow, isOpen, open, reset, shiftElements, templateHtml, u, unshiftElements, unshifters, visit;
8367
8500
  u = up.util;
8368
8501
 
8369
8502
  /**
8370
8503
  Sets default options for future modals.
8371
8504
 
8372
8505
  @property up.modal.config
8506
+ @param {String} [config.history=true]
8507
+ Whether opening a modal will add a browser history entry.
8373
8508
  @param {Number} [config.width]
8374
8509
  The width of the dialog as a CSS value like `'400px'` or `50%`.
8375
8510
 
@@ -8389,20 +8524,28 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8389
8524
  A string containing the HTML structure of the modal.
8390
8525
  You can supply an alternative template string, but make sure that it
8391
8526
  defines tag with the classes `up-modal`, `up-modal-dialog` and `up-modal-content`.
8392
-
8527
+ yy
8393
8528
  You can also supply a function that returns a HTML string.
8394
8529
  The function will be called with the modal options (merged from these defaults
8395
8530
  and any per-open overrides) whenever a modal opens.
8396
8531
  @param {String} [config.closeLabel='X']
8397
8532
  The label of the button that closes the dialog.
8398
8533
  @param {String} [config.openAnimation='fade-in']
8399
- The animation used to open the modal. The animation will be applied
8400
- to both the dialog box and the overlay dimming the page.
8534
+ The animation used to open the viewport around the dialog.
8401
8535
  @param {String} [config.closeAnimation='fade-out']
8402
- The animation used to close the modal. The animation will be applied
8403
- to both the dialog box and the overlay dimming the page.
8404
- @param {String} [config.history=true]
8405
- Whether opening a modal will add a browser history entry.
8536
+ The animation used to close the viewport the dialog.
8537
+ @param {String} [config.backdropOpenAnimation='fade-in']
8538
+ The animation used to open the backdrop that dims the page below the dialog.
8539
+ @param {String} [config.backdropCloseAnimation='fade-out']
8540
+ The animation used to close the backdrop that dims the page below the dialog.
8541
+ @param {String} [config.openDuration]
8542
+ The duration of the open animation (in milliseconds).
8543
+ @param {String} [config.closeDuration]
8544
+ The duration of the close animation (in milliseconds).
8545
+ @param {String} [config.openEasing]
8546
+ The timing function controlling the acceleration of the opening animation.
8547
+ @param {String} [config.closeEasing]
8548
+ The timing function controlling the acceleration of the closing animation.
8406
8549
  @stable
8407
8550
  */
8408
8551
  config = u.config({
@@ -8413,9 +8556,15 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8413
8556
  history: true,
8414
8557
  openAnimation: 'fade-in',
8415
8558
  closeAnimation: 'fade-out',
8559
+ closeDuration: null,
8560
+ closeEasing: null,
8561
+ openDuration: null,
8562
+ openEasing: null,
8563
+ backdropOpenAnimation: 'fade-in',
8564
+ backdropCloseAnimation: 'fade-out',
8416
8565
  closeLabel: '×',
8417
8566
  template: function(config) {
8418
- return "<div class=\"up-modal\">\n <div class=\"up-modal-dialog\">\n <div class=\"up-modal-close\" up-close>" + config.closeLabel + "</div>\n <div class=\"up-modal-content\"></div>\n </div>\n</div>";
8567
+ return "<div class=\"up-modal\">\n <div class=\"up-modal-backdrop\"></div>\n <div class=\"up-modal-viewport\">\n <div class=\"up-modal-dialog\">\n <div class=\"up-modal-close\" up-close>" + config.closeLabel + "</div>\n <div class=\"up-modal-content\"></div>\n </div>\n </div>\n</div>";
8419
8568
  }
8420
8569
  });
8421
8570
 
@@ -8464,7 +8613,6 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8464
8613
  };
8465
8614
  createFrame = function(target, options) {
8466
8615
  var $content, $dialog, $modal;
8467
- shiftElements();
8468
8616
  $modal = $(templateHtml());
8469
8617
  if (options.sticky) {
8470
8618
  $modal.attr('up-sticky', '');
@@ -8489,6 +8637,10 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8489
8637
  unshifters = [];
8490
8638
  shiftElements = function() {
8491
8639
  var bodyRightPadding, bodyRightShift, scrollbarWidth, unshiftBody;
8640
+ if (unshifters.length) {
8641
+ u.error('Tried to call shiftElements multiple times %o', unshifters.length);
8642
+ }
8643
+ $('.up-modal').addClass('up-modal-ready');
8492
8644
  scrollbarWidth = u.scrollbarWidth();
8493
8645
  bodyRightPadding = parseInt($('body').css('padding-right'));
8494
8646
  bodyRightShift = scrollbarWidth + bodyRightPadding;
@@ -8510,6 +8662,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8510
8662
  };
8511
8663
  unshiftElements = function() {
8512
8664
  var results, unshifter;
8665
+ $('.up-modal').removeClass('up-modal-ready');
8513
8666
  results = [];
8514
8667
  while (unshifter = unshifters.pop()) {
8515
8668
  results.push(unshifter());
@@ -8580,7 +8733,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8580
8733
 
8581
8734
  Example:
8582
8735
 
8583
- up.modal.visit('/foo', { target: '.list' })
8736
+ up.modal.visit('/foo', { target: '.list' });
8584
8737
 
8585
8738
  This will request `/foo`, extract the `.list` selector from the response
8586
8739
  and open the selected container in a modal dialog.
@@ -8606,26 +8759,67 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8606
8759
  return open(options);
8607
8760
  };
8608
8761
 
8762
+ /**
8763
+ [Extracts](/up.extract) the given CSS selector from the given HTML string and
8764
+ opens the results in a modal.
8765
+
8766
+ Example:
8767
+
8768
+ var html = 'before <div class="content">inner</div> after';
8769
+ up.modal.extract('/foo', '.content', html);
8770
+
8771
+ The would open a modal with the following contents:
8772
+
8773
+ <div class="content">inner</div>
8774
+
8775
+ Emits events [`up:modal:open`](/up:modal:open) and [`up:modal:opened`](/up:modal:opened).
8776
+
8777
+ @function up.modal.extract
8778
+ @param {String} url
8779
+ The URL to load.
8780
+ @param {Object} options
8781
+ See options for [`up.modal.follow`](/up.modal.follow).
8782
+ @return {Promise}
8783
+ A promise that will be resolved when the modal has been opened and the opening
8784
+ animation has completed.
8785
+ @stable
8786
+ */
8787
+ extract = function(selector, html, options) {
8788
+ options = u.options(options);
8789
+ options.html = html;
8790
+ options.history = u.option(options.history, false);
8791
+ options.target = selector;
8792
+ return open(options);
8793
+ };
8794
+
8609
8795
  /**
8610
8796
  @function open
8611
8797
  @internal
8612
8798
  */
8613
8799
  open = function(options) {
8614
- var $link, animateOptions, target, url;
8800
+ var $link, animateOptions, html, target, url;
8615
8801
  options = u.options(options);
8616
- $link = u.option(options.$link, u.nullJQuery());
8617
- url = u.option(options.url, $link.attr('up-href'), $link.attr('href'));
8618
- target = u.option(options.target, $link.attr('up-modal'), 'body');
8802
+ $link = u.option(u.pluckKey(options, '$link'), u.nullJQuery());
8803
+ url = u.option(u.pluckKey(options, 'url'), $link.attr('up-href'), $link.attr('href'));
8804
+ html = u.pluckKey(options, 'html');
8805
+ target = u.option(u.pluckKey(options, 'target'), $link.attr('up-modal'), 'body');
8619
8806
  options.width = u.option(options.width, $link.attr('up-width'), config.width);
8620
8807
  options.maxWidth = u.option(options.maxWidth, $link.attr('up-max-width'), config.maxWidth);
8621
8808
  options.height = u.option(options.height, $link.attr('up-height'), config.height);
8622
8809
  options.animation = u.option(options.animation, $link.attr('up-animation'), config.openAnimation);
8810
+ options.backdropAnimation = u.option(options.backdropAnimation, $link.attr('up-backdrop-animation'), config.backdropOpenAnimation);
8623
8811
  options.sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'));
8624
- options.history = up.browser.canPushState() ? u.option(options.history, u.castedAttr($link, 'up-history'), config.history) : false;
8625
8812
  options.confirm = u.option(options.confirm, $link.attr('up-confirm'));
8626
- animateOptions = up.motion.animateOptions(options, $link);
8813
+ animateOptions = up.motion.animateOptions(options, $link, {
8814
+ duration: config.openDuration,
8815
+ easing: config.openEasing
8816
+ });
8817
+ options.history = u.option(options.history, u.castedAttr($link, 'up-history'), config.history);
8818
+ if (!up.browser.canPushState()) {
8819
+ options.history = false;
8820
+ }
8627
8821
  return up.browser.confirm(options).then(function() {
8628
- var promise, wasOpen;
8822
+ var extractOptions, promise, wasOpen;
8629
8823
  if (up.bus.nobodyPrevents('up:modal:open', {
8630
8824
  url: url,
8631
8825
  message: 'Opening modal'
@@ -8639,15 +8833,21 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8639
8833
  options.beforeSwap = function() {
8640
8834
  return createFrame(target, options);
8641
8835
  };
8642
- promise = up.replace(target, url, u.merge(options, {
8836
+ extractOptions = u.merge(options, {
8643
8837
  animation: false
8644
- }));
8645
- if (!wasOpen) {
8838
+ });
8839
+ if (url) {
8840
+ promise = up.replace(target, url, extractOptions);
8841
+ } else {
8842
+ promise = up.extract(target, html, extractOptions);
8843
+ }
8844
+ if (!(wasOpen || up.motion.isNone(options.animation))) {
8646
8845
  promise = promise.then(function() {
8647
- return up.animate($('.up-modal'), options.animation, animateOptions);
8846
+ return $.when(up.animate($('.up-modal-backdrop'), options.backdropAnimation, animateOptions), up.animate($('.up-modal-viewport'), options.animation, animateOptions));
8648
8847
  });
8649
8848
  }
8650
8849
  promise = promise.then(function() {
8850
+ shiftElements();
8651
8851
  return up.emit('up:modal:opened', {
8652
8852
  message: 'Modal opened'
8653
8853
  });
@@ -8685,28 +8885,40 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8685
8885
  @function up.modal.close
8686
8886
  @param {Object} options
8687
8887
  See options for [`up.animate`](/up.animate)
8688
- @return {Deferred}
8888
+ @return {Promise}
8689
8889
  A promise that will be resolved once the modal's close
8690
8890
  animation has finished.
8691
8891
  @stable
8692
8892
  */
8693
8893
  close = function(options) {
8694
- var $modal, promise;
8894
+ var $modal, animateOptions, backdropCloseAnimation, promise, viewportCloseAnimation;
8895
+ options = u.options(options);
8695
8896
  $modal = $('.up-modal');
8696
8897
  if ($modal.length) {
8697
8898
  if (up.bus.nobodyPrevents('up:modal:close', {
8698
8899
  $element: $modal,
8699
8900
  message: 'Closing modal'
8700
8901
  })) {
8701
- options = u.options(options, {
8702
- animation: config.closeAnimation,
8703
- url: $modal.attr('up-covered-url'),
8704
- title: $modal.attr('up-covered-title')
8902
+ unshiftElements();
8903
+ viewportCloseAnimation = u.option(options.animation, config.closeAnimation);
8904
+ backdropCloseAnimation = u.option(options.backdropAnimation, config.backdropCloseAnimation);
8905
+ animateOptions = up.motion.animateOptions(options, {
8906
+ duration: config.closeDuration,
8907
+ easing: config.closeEasing
8705
8908
  });
8706
- currentUrl = void 0;
8707
- promise = up.destroy($modal, options);
8909
+ if (up.motion.isNone(viewportCloseAnimation)) {
8910
+ promise = u.resolvedPromise();
8911
+ } else {
8912
+ promise = $.when(up.animate($('.up-modal-viewport'), viewportCloseAnimation, animateOptions), up.animate($('.up-modal-backdrop'), backdropCloseAnimation, animateOptions));
8913
+ }
8708
8914
  promise = promise.then(function() {
8709
- unshiftElements();
8915
+ var destroyOptions;
8916
+ destroyOptions = u.options(u.except(options, 'animation', 'duration', 'easing', 'delay'), {
8917
+ url: $modal.attr('up-covered-url'),
8918
+ title: $modal.attr('up-covered-title')
8919
+ });
8920
+ currentUrl = void 0;
8921
+ up.destroy($modal, destroyOptions);
8710
8922
  return up.emit('up:modal:closed', {
8711
8923
  message: 'Modal closed'
8712
8924
  });
@@ -8779,7 +8991,9 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8779
8991
  If set to `"true"`, the modal remains
8780
8992
  open even if the page changes in the background.
8781
8993
  @param {String} [up-animation]
8782
- The animation to use when opening the modal.
8994
+ The animation to use when opening the viewport containing the dialog.
8995
+ @param {String} [up-backdrop-animation]
8996
+ The animation to use when opening the backdrop that dims the page below the dialog.
8783
8997
  @param {String} [up-height]
8784
8998
  The width of the dialog in pixels.
8785
8999
  By [default](/up.modal.config) the dialog will grow to fit its contents.
@@ -8838,6 +9052,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
8838
9052
  knife: eval(typeof Knife !== "undefined" && Knife !== null ? Knife.point : void 0),
8839
9053
  visit: visit,
8840
9054
  follow: follow,
9055
+ extract: extract,
8841
9056
  open: function() {
8842
9057
  return up.error('up.modal.open no longer exists. Please use either up.modal.follow or up.modal.visit.');
8843
9058
  },
@@ -8916,6 +9131,9 @@ The tooltip element is appended to the end of `<body>`.
8916
9131
  closeAnimation: 'fade-out'
8917
9132
  });
8918
9133
  reset = function() {
9134
+ close({
9135
+ animation: false
9136
+ });
8919
9137
  return config.reset();
8920
9138
  };
8921
9139
  setPosition = function($link, $tooltip, position) {
@@ -9096,7 +9314,7 @@ by providing instant feedback for user interactions.
9096
9314
 
9097
9315
  (function() {
9098
9316
  up.navigation = (function($) {
9099
- var CLASS_ACTIVE, SELECTORS_SECTION, SELECTOR_ACTIVE, SELECTOR_SECTION, SELECTOR_SECTION_INSTANT, config, currentClass, enlargeClickArea, locationChanged, normalizeUrl, reset, sectionClicked, sectionUrls, selector, u, unmarkActive, urlSet;
9317
+ var CLASS_ACTIVE, SELECTOR_SECTION, config, currentClass, findClickArea, locationChanged, markActive, normalizeUrl, reset, sectionUrls, u, unmarkActive, urlSet, withActiveMark;
9100
9318
  u = up.util;
9101
9319
 
9102
9320
  /**
@@ -9121,18 +9339,7 @@ by providing instant feedback for user interactions.
9121
9339
  return classes.join(' ');
9122
9340
  };
9123
9341
  CLASS_ACTIVE = 'up-active';
9124
- SELECTORS_SECTION = ['a', '[up-href]', '[up-alias]'];
9125
- SELECTOR_SECTION = SELECTORS_SECTION.join(', ');
9126
- SELECTOR_SECTION_INSTANT = ((function() {
9127
- var i, len, results;
9128
- results = [];
9129
- for (i = 0, len = SELECTORS_SECTION.length; i < len; i++) {
9130
- selector = SELECTORS_SECTION[i];
9131
- results.push(selector + "[up-instant]");
9132
- }
9133
- return results;
9134
- })()).join(', ');
9135
- SELECTOR_ACTIVE = "." + CLASS_ACTIVE;
9342
+ SELECTOR_SECTION = 'a, [up-href]';
9136
9343
  normalizeUrl = function(url) {
9137
9344
  if (u.isPresent(url)) {
9138
9345
  return u.normalizeUrl(url, {
@@ -9201,6 +9408,27 @@ by providing instant feedback for user interactions.
9201
9408
  });
9202
9409
  };
9203
9410
 
9411
+ /**
9412
+ @function findClickArea
9413
+ @param {String|Element|jQuery} elementOrSelector
9414
+ @param {Boolean} options.enlarge
9415
+ If `true`, tries to find a containing link that has expanded the link's click area.
9416
+ If we find one, we prefer to mark the larger area as active.
9417
+ @internal
9418
+ */
9419
+ findClickArea = function(elementOrSelector, options) {
9420
+ var $area;
9421
+ $area = $(elementOrSelector);
9422
+ options = u.options(options, {
9423
+ enlarge: false
9424
+ });
9425
+ if (options.enlarge) {
9426
+ return u.presence($area.parent(SELECTOR_SECTION)) || $area;
9427
+ } else {
9428
+ return $area;
9429
+ }
9430
+ };
9431
+
9204
9432
  /**
9205
9433
  Links that are currently loading are assigned the `up-active`
9206
9434
  class automatically. Style `.up-active` in your CSS to improve the
@@ -9228,27 +9456,30 @@ by providing instant feedback for user interactions.
9228
9456
  @selector .up-active
9229
9457
  @stable
9230
9458
  */
9231
- sectionClicked = function($section) {
9232
- unmarkActive();
9233
- $section = enlargeClickArea($section);
9234
- return $section.addClass(CLASS_ACTIVE);
9235
- };
9236
- enlargeClickArea = function($section) {
9237
- return u.presence($section.parents(SELECTOR_SECTION)) || $section;
9459
+ markActive = function(elementOrSelector, options) {
9460
+ var $element;
9461
+ $element = findClickArea(elementOrSelector, options);
9462
+ return $element.addClass(CLASS_ACTIVE);
9238
9463
  };
9239
- unmarkActive = function() {
9240
- return $(SELECTOR_ACTIVE).removeClass(CLASS_ACTIVE);
9464
+ unmarkActive = function(elementOrSelector, options) {
9465
+ var $element;
9466
+ $element = findClickArea(elementOrSelector, options);
9467
+ return $element.removeClass(CLASS_ACTIVE);
9241
9468
  };
9242
- up.on('click', SELECTOR_SECTION, function(event, $section) {
9243
- if (u.isUnmodifiedMouseEvent(event) && !$section.is('[up-instant]')) {
9244
- return sectionClicked($section);
9245
- }
9246
- });
9247
- up.on('mousedown', SELECTOR_SECTION_INSTANT, function(event, $section) {
9248
- if (u.isUnmodifiedMouseEvent(event)) {
9249
- return sectionClicked($section);
9469
+ withActiveMark = function(elementOrSelector, options, block) {
9470
+ var $element, promise;
9471
+ $element = $(elementOrSelector);
9472
+ markActive($element, options);
9473
+ promise = block();
9474
+ if (u.isPromise(promise)) {
9475
+ promise.always(function() {
9476
+ return unmarkActive($element, options);
9477
+ });
9478
+ } else {
9479
+ up.warn('Expected block to return a promise, but got %o', promise);
9250
9480
  }
9251
- });
9481
+ return promise;
9482
+ };
9252
9483
 
9253
9484
  /**
9254
9485
  Links that point to the current location are assigned
@@ -9295,7 +9526,6 @@ by providing instant feedback for user interactions.
9295
9526
  @stable
9296
9527
  */
9297
9528
  up.on('up:fragment:inserted', function() {
9298
- unmarkActive();
9299
9529
  return locationChanged();
9300
9530
  });
9301
9531
  up.on('up:fragment:destroyed', function(event, $fragment) {
@@ -9308,7 +9538,10 @@ by providing instant feedback for user interactions.
9308
9538
  config: config,
9309
9539
  defaults: function() {
9310
9540
  return u.error('up.navigation.defaults(...) no longer exists. Set values on he up.navigation.config property instead.');
9311
- }
9541
+ },
9542
+ markActive: markActive,
9543
+ unmarkActive: unmarkActive,
9544
+ withActiveMark: withActiveMark
9312
9545
  };
9313
9546
  })(jQuery);
9314
9547