unpoly-rails 0.56.7 → 0.57.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  3. data/dist/unpoly.js +1569 -793
  4. data/dist/unpoly.min.js +4 -4
  5. data/lib/assets/javascripts/unpoly.coffee +2 -0
  6. data/lib/assets/javascripts/unpoly/browser.coffee.erb +25 -41
  7. data/lib/assets/javascripts/unpoly/bus.coffee.erb +20 -6
  8. data/lib/assets/javascripts/unpoly/classes/cache.coffee +23 -13
  9. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +87 -0
  10. data/lib/assets/javascripts/unpoly/classes/focus_tracker.coffee +29 -0
  11. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +7 -4
  12. data/lib/assets/javascripts/unpoly/classes/record.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/request.coffee +38 -45
  14. data/lib/assets/javascripts/unpoly/classes/response.coffee +16 -1
  15. data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +26 -0
  16. data/lib/assets/javascripts/unpoly/classes/store/session.coffee +59 -0
  17. data/lib/assets/javascripts/unpoly/cookie.coffee +56 -0
  18. data/lib/assets/javascripts/unpoly/dom.coffee.erb +67 -39
  19. data/lib/assets/javascripts/unpoly/feedback.coffee +2 -2
  20. data/lib/assets/javascripts/unpoly/form.coffee.erb +23 -12
  21. data/lib/assets/javascripts/unpoly/history.coffee +2 -2
  22. data/lib/assets/javascripts/unpoly/layout.coffee.erb +118 -99
  23. data/lib/assets/javascripts/unpoly/link.coffee.erb +12 -5
  24. data/lib/assets/javascripts/unpoly/log.coffee +6 -5
  25. data/lib/assets/javascripts/unpoly/modal.coffee.erb +9 -2
  26. data/lib/assets/javascripts/unpoly/motion.coffee.erb +2 -6
  27. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +2 -2
  28. data/lib/assets/javascripts/unpoly/params.coffee.erb +522 -0
  29. data/lib/assets/javascripts/unpoly/popup.coffee.erb +3 -3
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +42 -34
  31. data/lib/assets/javascripts/unpoly/{syntax.coffee → syntax.coffee.erb} +59 -117
  32. data/lib/assets/javascripts/unpoly/{util.coffee → util.coffee.erb} +206 -171
  33. data/lib/unpoly/rails/version.rb +1 -1
  34. data/package.json +1 -1
  35. data/spec_app/Gemfile.lock +1 -1
  36. data/spec_app/app/assets/javascripts/integration_test.coffee +0 -4
  37. data/spec_app/app/assets/stylesheets/integration_test.sass +7 -1
  38. data/spec_app/app/controllers/pages_controller.rb +4 -0
  39. data/spec_app/app/views/form_test/basics/new.erb +34 -5
  40. data/spec_app/app/views/form_test/submission_result.erb +2 -2
  41. data/spec_app/app/views/form_test/uploads/new.erb +15 -2
  42. data/spec_app/app/views/hash_test/unpoly.erb +30 -0
  43. data/spec_app/app/views/pages/start.erb +2 -1
  44. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +17 -2
  45. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +5 -0
  46. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +1 -1
  47. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +5 -0
  48. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +8 -8
  49. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +58 -20
  50. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +78 -0
  51. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +31 -0
  52. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +50 -0
  53. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +67 -0
  54. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +113 -0
  55. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +133 -45
  56. data/spec_app/spec/javascripts/up/form_spec.js.coffee +13 -13
  57. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +110 -26
  58. data/spec_app/spec/javascripts/up/link_spec.js.coffee +1 -1
  59. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +1 -0
  60. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +52 -51
  61. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +2 -2
  62. data/spec_app/spec/javascripts/up/params_spec.coffee +768 -0
  63. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +75 -36
  64. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +48 -15
  65. data/spec_app/spec/javascripts/up/util_spec.js.coffee +148 -131
  66. metadata +17 -5
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 303ad1afad69662cbcabe59b9dea6eba8a842bcb1a59de08a4b5aec07411bbdd
4
- data.tar.gz: 2e94082f78832ae6a946e84165c727b926218bbfb19b7b30ba29127523e89ae0
3
+ metadata.gz: af0d5493649ecec8227682297684d979b948b1f80bbf9cfeae29f38f7a8d6188
4
+ data.tar.gz: ef3e30eca247452227534043db0b5e69e4f815299366cedca964fc8c3bc1e9e4
5
5
  SHA512:
6
- metadata.gz: 9e191cab850aac8c80d884ff4ef3c94d905627e5772ae6d1f00359ec2f7b72c42b6648c8fd4ba1b8367a6ee791aa62d186fc4cbbb63447f717c798b109c6a8bf
7
- data.tar.gz: 217216041b1a6b1be19fb9d78c5b098cb46cadff06d85aa8cfae8081a26276294be942dfce06b8310462da96476c1d315cb9496dd4a5bb01021ed89a944d9034
6
+ metadata.gz: 1393189464a4148ffd4a5bc1392607345b493a31847c999a0f45916c62cb912d255ca39c2ab95a2703007d14ada30930f7313585ac6e4d742e0c9dee2303e374
7
+ data.tar.gz: 6e42685e20553c08ad58e4d89296ebe009766f602af8e383e6e5a40aafce27dfdde0f186ba56ecadcb41bf9a6648ae0bf3090ea6b8cfb37225c982f7940475e8
@@ -6,6 +6,79 @@ Changes to this project will be documented in this file.
6
6
  This project mostly adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
8
 
9
+ 0.57.0
10
+ ------
11
+
12
+ ### Request parameters
13
+
14
+ To prevent confusion with [`[up-data]`](/up-data), Unpoly now uses the word "params" when talking about form values or request parameters:
15
+
16
+ - [`up.request()`](/up.request) option `{ data }` has been renamed to `{ params }`.
17
+ - [`up.replace()`](/up.replace) option `{ data }` has been renamed to `{ params }`.
18
+
19
+ Parameters may be passed in one of the following types:
20
+
21
+ 1. an object like `{ email: 'foo@bar.com' }`
22
+ 2. a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
23
+ 3. a query string like `email=foo%40bar.com`
24
+ 4. an array of `{ name, value }` objects like `[{ name: 'email', value: 'foo@bar.com' }]`
25
+
26
+ To help working with form values and request parameters, an experimental module [`up.params`](/up.params) has been added. It offers a consistent API to manipulate request parameters independent of their type.
27
+
28
+ - [`up.params.fromForm()`](/up.params.fromForm) - serialize a `<form>`
29
+ - [`up.params.fromURL()`](/up.params.fromURL) - extract params from a URL's query string
30
+ - [`up.params.toArray()`](/up.params.toArray) - convert any params type to an array of `{ name, value }` elements
31
+ - [`up.params.toObject()`](/up.params.toObject) - convert any params type to an object
32
+ - [`up.params.toQuery()`](/up.params.toQuery) - convert any params type to a query string
33
+ - [`up.params.toFormData()`](/up.params.toFormData) - convert any params type to a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
34
+ - [`up.params.buildURL()`](/up.params.buildURL) - composes a URL with query section from a base URL and a params value of any type
35
+ - [`up.params.get()`](/up.params.get) - retrieve the value for the given params key
36
+ - [`up.params.add()`](/up.params.add) - adds a single key/value to a params value of any type
37
+ - [`up.params.assign()`](/up.params.assign) - adds the params to another params
38
+ - [`up.params.merge()`](/up.params.merge) - [merges](/up.util.merge) two params
39
+
40
+
41
+ ### Application layout
42
+
43
+ - When Unpoly cannot find the [viewport](/up.layout.config#config.viewports) of an element, it now uses the scrolling root element. This is either `<body>` or `<html>`, depending on the browser.
44
+ - Fix a bug where linking back and forth between multiple `#anchor` hashes of the same URL would always reveal the first anchor.
45
+ - Revealing elements below [fixed navigation bars](/up-fixed-top) now honors the navigation bar's `padding`, `border`, `margin`, `top` and `bottom` properties.
46
+ - Fix a bug where revealing elements [fixed navigation bars](/up-fixed-top) would scroll 1 pixel too short.
47
+ - [`up.layout.revealHash()`](/up.layout.revealHash) no longer retrieves the hash anchor from the current URL. You need to pass in a `#hash` value as a first argument.
48
+ - Fix a bug where a `#hash` anchor would not be revealed if it included non-word characters like spaces or dots.
49
+
50
+
51
+ ### Compilers
52
+
53
+ - To improve performance, Unpoly no longer parses [`[up-data]`](/up-data) attributes when a [compiler function](/up.compiler) does not require a second `data` argument.
54
+ - Compilers that return [destructor functions](/up.compiler#cleaning-up-after-yourself) now run slightly faster.
55
+ - [Compilers](/up.compiler) with `{ batch: true }` now receive an array of [`[up-data]`](/up-data) objects as their second `data` argument.
56
+ - [Compilers](/up.compiler) with `{ batch: true }` can no longer return destructor functions. Previously the behavior of batch destructors was undefined, now it throws an error.
57
+ - Returning an array of [destructor functions](/up.compiler#cleaning-up-after-yourself) from [`up.compiler()`](/up.compiler) is now deprecated. Please return a single destructor function instead.
58
+ - [`up.syntax.data()`](/up.syntax.data) now returns `undefined` if the given object has no (or an empty) [`[up-data]`](/up-data) attribute. It previously returned an empty object.
59
+
60
+
61
+ ### Event listeners
62
+
63
+ - To improve performance, Unpoly no longer parses [`[up-data]`](/up-data) attributes when an [`up.on()`](/up.on) listener does not require a third `data` argument.
64
+ - [`up.on()`](/up.on) now throws an error when the same callback function is registered multiple times.
65
+
66
+
67
+ ### Fragment update API
68
+
69
+ - New experimental function [`up.all()`](/up.all), which returns all elements matching the given selector. Like [`up.first()`](/up.first) it ignores elements that are being [destroyed](/up.destroy) or [transitioned](/up.morph).
70
+
71
+
72
+ ### Various
73
+
74
+ - New experimental function [`up.util.isBoolean()`](/up.util.isBoolean).
75
+ - [`up.follow()`](/up.follow) now accepts a `{ url }` option. It can be used to override the given link's `[href]` attribute.
76
+ - New configuration option [`up.form.config.submitButtons`](/up.form.config#config.submitButtons)
77
+ - [`up.preload()`](/up.preload) now accepts an options hash that will be passed on to the function making the preload request.
78
+ - New experimental function [`up.Response#getHeader()`](/up.Response.prototype.getHeader). It looks up the header value for the given name in the HTTP response header.
79
+
80
+
81
+
9
82
  0.56.7
10
83
  ------
11
84
 
@@ -224,7 +297,7 @@ This release contains no new features, but will help you when using tools like B
224
297
 
225
298
  ### Preloading
226
299
 
227
- - Fix a bug where [preloading](/a-up-target) would not always be aborted when stopping to hover before [`up.proxy.config.preloadDelay`](/up.proxy.config#up.proxy.config.preloadDelay).
300
+ - Fix a bug where [preloading](/a-up-target) would not always be aborted when stopping to hover before [`up.proxy.config.preloadDelay`](/up.proxy.config#config.preloadDelay).
228
301
 
229
302
 
230
303
  0.53.1
@@ -5,11 +5,11 @@
5
5
 
6
6
  (function() {
7
7
  window.up = {
8
- version: "0.56.7",
9
- renamedModule: function(oldName, newName) {
8
+ version: "0.57.0",
9
+ deprecateRenamedModule: function(oldName, newName) {
10
10
  return typeof Object.defineProperty === "function" ? Object.defineProperty(up, oldName, {
11
11
  get: function() {
12
- up.log.warn("up." + oldName + " has been renamed to up." + newName);
12
+ up.warn("Deprecated: up." + oldName + " has been renamed to up." + newName);
13
13
  return up[newName];
14
14
  }
15
15
  }) : void 0;
@@ -41,7 +41,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
41
41
  @function up.util.noop
42
42
  @experimental
43
43
  */
44
- var $createElementFromSelector, $createPlaceholder, $submittingButton, CASE_CONVERSION_GROUP, CSS_LENGTH_PROPS, DivertibleChain, ESCAPE_HTML_ENTITY_MAP, addClass, addTemporaryClass, all, always, any, appendRequestData, arrayToSet, assign, assignPolyfill, asyncNoop, attributeSelector, camelCase, camelCaseKeys, castedAttr, changeClassList, clientSize, compact, concludeCssTransition, config, contains, convertCase, copy, copyAttributes, copyWithRenamedKeys, createElementFromHtml, cssLength, detachWith, detect, documentHasVerticalScrollbar, each, escapeHtml, escapePressed, evalOption, except, extractFromStyleObject, extractOptions, fail, fixedToAbsolute, flatten, forceRepaint, getElement, hasClass, hasCssTransition, hide, horizontalScreenHalf, identity, intersect, isArray, isBlank, isBodyDescendant, isCrossDomain, isDefined, isDetached, isElement, isEqual, isFixed, isFormData, isFunction, isGiven, isJQuery, isMissing, isNull, isNumber, isObject, isOptions, isPresent, isPromise, isStandardPort, isString, isTruthy, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, kebabCase, kebabCaseKeys, last, listBlock, map, margins, measure, memoize, merge, mergeRequestData, methodAllowsPayload, microtask, muteRejection, newDeferred, nextFrame, nonUpClasses, noop, normalizeMethod, normalizeStyleValueForWrite, normalizeUrl, nullJQuery, offsetParent, only, opacity, openConfig, option, options, parseUrl, pluckData, pluckKey, presence, presentAttr, previewable, promiseTimer, readComputedStyle, readComputedStyleNumber, readInlineStyle, reject, rejectOnError, remove, removeClass, renameKey, requestDataAsArray, requestDataAsQuery, requestDataFromForm, scrollbarWidth, select, selectInDynasty, selectInSubtree, selectorForElement, sequence, setMissingAttrs, setTimer, setToArray, submittedValue, times, toArray, trim, uniq, uniqBy, unresolvablePromise, unwrapElement, whenReady, writeInlineStyle, writeTemporaryStyle;
44
+ var $createElementFromSelector, $createPlaceholder, CASE_CONVERSION_GROUP, CSS_LENGTH_PROPS, DivertibleChain, ESCAPE_HTML_ENTITY_MAP, addClass, addTemporaryClass, all, always, any, arrayToSet, assign, assignPolyfill, asyncNoop, attributeSelector, camelCase, camelCaseKeys, castedAttr, changeClassList, clientSize, compact, concludeCssTransition, config, contains, convertCase, copy, copyAttributes, copyWithRenamedKeys, createElementFromHtml, cssLength, deprecateRenamedKey, detachWith, detect, documentHasVerticalScrollbar, each, eachIterator, elementTagName, escapeHtml, escapePressed, evalOption, except, extractFromStyleObject, extractOptions, fail, fixedToAbsolute, flatMap, flatten, forceRepaint, getElement, hasClass, hasCssTransition, hide, horizontalScreenHalf, identity, intersect, isArray, isBasicObjectProperty, isBlank, isBodyDescendant, isBoolean, isCrossDomain, isDefined, isDetached, isElement, isEqual, isFixed, isFormData, isFunction, isGiven, isJQuery, isMissing, isNull, isNumber, isObject, isOptions, isPresent, isPromise, isSingletonElement, isStandardPort, isString, isTruthy, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, jsonAttr, kebabCase, kebabCaseKeys, last, listBlock, map, margins, measure, memoize, merge, methodAllowsPayload, microtask, muteRejection, newDeferred, newOptions, nextFrame, nonUpClasses, noop, normalizeMethod, normalizeStyleValueForWrite, normalizeUrl, nullJQuery, objectValues, offsetParent, only, opacity, openConfig, option, parseUrl, pluckData, pluckKey, presence, presentAttr, previewable, promiseTimer, readComputedStyle, readComputedStyleNumber, readInlineStyle, reject, rejectOnError, remove, removeClass, renameKey, scrollbarWidth, select, selectInDynasty, selectInSubtree, selectorForElement, sequence, setMissingAttrs, setTimer, setToArray, splitValues, submittedValue, sum, times, toArray, trim, uniq, uniqBy, unresolvablePromise, unwrapElement, valuesPolyfill, whenReady, writeInlineStyle, writeTemporaryStyle;
45
45
  noop = (function() {});
46
46
 
47
47
  /***
@@ -265,11 +265,12 @@ that might save you from loading something like [Lodash](https://lodash.com/).
265
265
  @experimental
266
266
  */
267
267
  selectorForElement = function(element) {
268
- var $element, ariaLabel, classes, id, j, klass, len, name, selector, tagName, upId;
268
+ var $element, ariaLabel, classes, id, j, klass, len, name, selector, upId;
269
269
  $element = $(element);
270
270
  selector = void 0;
271
- tagName = $element.prop('tagName').toLowerCase();
272
- if (upId = presence($element.attr("up-id"))) {
271
+ if (isSingletonElement($element)) {
272
+ selector = elementTagName($element);
273
+ } else if (upId = presence($element.attr("up-id"))) {
273
274
  selector = attributeSelector('up-id', upId);
274
275
  } else if (id = presence($element.attr("id"))) {
275
276
  if (id.match(/^[a-z0-9\-_]+$/i)) {
@@ -278,7 +279,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
278
279
  selector = attributeSelector('id', id);
279
280
  }
280
281
  } else if (name = presence($element.attr("name"))) {
281
- selector = tagName + attributeSelector('name', name);
282
+ selector = elementTagName($element) + attributeSelector('name', name);
282
283
  } else if (classes = presence(nonUpClasses($element))) {
283
284
  selector = '';
284
285
  for (j = 0, len = classes.length; j < len; j++) {
@@ -288,10 +289,16 @@ that might save you from loading something like [Lodash](https://lodash.com/).
288
289
  } else if (ariaLabel = presence($element.attr("aria-label"))) {
289
290
  selector = attributeSelector('aria-label', ariaLabel);
290
291
  } else {
291
- selector = tagName;
292
+ selector = elementTagName($element);
292
293
  }
293
294
  return selector;
294
295
  };
296
+ isSingletonElement = function($element) {
297
+ return $element.is('html, body, head, title');
298
+ };
299
+ elementTagName = function($element) {
300
+ return $element.prop('tagName').toLowerCase();
301
+ };
295
302
  attributeSelector = function(attribute, value) {
296
303
  value = value.replace(/"/g, '\\"');
297
304
  return "[" + attribute + "=\"" + value + "\"]";
@@ -299,9 +306,9 @@ that might save you from loading something like [Lodash](https://lodash.com/).
299
306
  nonUpClasses = function($element) {
300
307
  var classString, classes;
301
308
  classString = $element.attr('class') || '';
302
- classes = classString.split(' ');
303
- return select(classes, function(klass) {
304
- return isPresent(klass) && !klass.match(/^up-/);
309
+ classes = splitValues(classString);
310
+ return reject(classes, function(klass) {
311
+ return klass.match(/^up-/);
305
312
  });
306
313
  };
307
314
  createElementFromHtml = function(html) {
@@ -332,6 +339,25 @@ that might save you from loading something like [Lodash](https://lodash.com/).
332
339
  @stable
333
340
  */
334
341
  assign = Object.assign || assignPolyfill;
342
+ valuesPolyfill = function(object) {
343
+ var key, results, value;
344
+ results = [];
345
+ for (key in object) {
346
+ value = object[key];
347
+ results.push(value);
348
+ }
349
+ return results;
350
+ };
351
+
352
+ /***
353
+ Returns an array of values of the given object.
354
+
355
+ @function up.util.values
356
+ @param {Object} object
357
+ @return {Array<string>}
358
+ @experimental
359
+ */
360
+ objectValues = Object.values || valuesPolyfill;
335
361
 
336
362
  /***
337
363
  Returns a new string with whitespace removed from the beginning
@@ -393,6 +419,14 @@ that might save you from loading something like [Lodash](https://lodash.com/).
393
419
  @stable
394
420
  */
395
421
  each = map;
422
+ eachIterator = function(iterator, callback) {
423
+ var entry, results;
424
+ results = [];
425
+ while ((entry = iterator.next()) && !entry.done) {
426
+ results.push(callback(entry.value));
427
+ }
428
+ return results;
429
+ };
396
430
 
397
431
  /***
398
432
  Calls the given function for the given number of times.
@@ -571,6 +605,18 @@ that might save you from loading something like [Lodash](https://lodash.com/).
571
605
  return typeof object === 'string' || object instanceof String;
572
606
  };
573
607
 
608
+ /***
609
+ Returns whether the given argument is a boolean value.
610
+
611
+ @function up.util.isBoolean
612
+ @param object
613
+ @return {boolean}
614
+ @experimental
615
+ */
616
+ isBoolean = function(object) {
617
+ return typeof object === 'boolean' || object instanceof Boolean;
618
+ };
619
+
574
620
  /***
575
621
  Returns whether the given argument is a number.
576
622
 
@@ -598,7 +644,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
598
644
  @internal
599
645
  */
600
646
  isOptions = function(object) {
601
- return typeof object === 'object' && !isNull(object) && !isJQuery(object) && !isPromise(object) && !isFormData(object) && !isArray(object);
647
+ return typeof object === 'object' && !isNull(object) && (isUndefined(object.constructor) || object.constructor === Object);
602
648
  };
603
649
 
604
650
  /***
@@ -699,13 +745,11 @@ that might save you from loading something like [Lodash](https://lodash.com/).
699
745
  @return {Object|Array}
700
746
  @stable
701
747
  */
702
- copy = function(object) {
748
+ copy = function(object, deep) {
703
749
  if (isArray(object)) {
704
750
  object = object.slice();
705
- } else if (isObject(object) && !isFunction(object)) {
751
+ } else if (isOptions(object)) {
706
752
  object = assign({}, object);
707
- } else {
708
- up.fail('Cannot copy %o', object);
709
753
  }
710
754
  return object;
711
755
  };
@@ -756,20 +800,14 @@ that might save you from loading something like [Lodash](https://lodash.com/).
756
800
  @return {Object}
757
801
  @internal
758
802
  */
759
- options = function(object, defaults) {
760
- var defaultValue, key, merged, value;
761
- merged = object ? copy(object) : {};
803
+ newOptions = function(object, defaults) {
762
804
  if (defaults) {
763
- for (key in defaults) {
764
- defaultValue = defaults[key];
765
- value = merged[key];
766
- if (isMissing(value)) {
767
- value = defaultValue;
768
- }
769
- merged[key] = value;
770
- }
805
+ return merge(defaults, object);
806
+ } else if (object) {
807
+ return copy(object);
808
+ } else {
809
+ return {};
771
810
  }
772
- return merged;
773
811
  };
774
812
 
775
813
  /***
@@ -894,7 +932,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
894
932
  return setToArray(arrayToSet(array));
895
933
  };
896
934
 
897
- /**
935
+ /***
898
936
  This function is like [`uniq`](/up.util.uniq), accept that
899
937
  the given function is invoked for each element to generate the value
900
938
  for which uniquness is computed.
@@ -924,7 +962,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
924
962
  });
925
963
  };
926
964
 
927
- /**
965
+ /***
928
966
  @function up.util.setToArray
929
967
  @internal
930
968
  */
@@ -937,7 +975,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
937
975
  return array;
938
976
  };
939
977
 
940
- /**
978
+ /***
941
979
  @function up.util.arrayToSet
942
980
  @internal
943
981
  */
@@ -1200,7 +1238,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1200
1238
  return element.offsetHeight;
1201
1239
  };
1202
1240
 
1203
- /**
1241
+ /***
1204
1242
  @function up.util.finishTransition
1205
1243
  @internal
1206
1244
  */
@@ -1235,7 +1273,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1235
1273
  */
1236
1274
  measure = function($element, opts) {
1237
1275
  var $context, box, contextCoords, coordinates, elementCoords, mgs;
1238
- opts = options(opts, {
1276
+ opts = newOptions(opts, {
1239
1277
  relative: false,
1240
1278
  inner: false,
1241
1279
  includeMargin: false
@@ -1356,21 +1394,35 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1356
1394
  @function up.util.castedAttr
1357
1395
  @internal
1358
1396
  */
1359
- castedAttr = function($element, attrName) {
1397
+ castedAttr = function($element, attribute) {
1360
1398
  var value;
1361
- value = $element.attr(attrName);
1399
+ value = $element.attr(attribute);
1362
1400
  switch (value) {
1363
1401
  case 'false':
1364
1402
  return false;
1365
1403
  case 'true':
1366
1404
  case '':
1367
- case attrName:
1405
+ case attribute:
1368
1406
  return true;
1369
1407
  default:
1370
1408
  return value;
1371
1409
  }
1372
1410
  };
1373
1411
 
1412
+ /***
1413
+ @function up.util.jsonAttr
1414
+ @internal
1415
+ */
1416
+ jsonAttr = function(elementOrSelector, attribute) {
1417
+ var element, json;
1418
+ if (element = getElement(elementOrSelector)) {
1419
+ json = typeof element.getAttribute === "function" ? element.getAttribute(attribute) : void 0;
1420
+ if (isString(json) && trim(json) !== '') {
1421
+ return JSON.parse(json);
1422
+ }
1423
+ }
1424
+ };
1425
+
1374
1426
  /***
1375
1427
  Returns a copy of the given object that only contains
1376
1428
  the given properties.
@@ -1534,12 +1586,12 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1534
1586
  }
1535
1587
  hash = {};
1536
1588
  hash.reset = function() {
1537
- var newOptions;
1538
- newOptions = blueprint;
1539
- if (isFunction(newOptions)) {
1540
- newOptions = newOptions();
1589
+ var opts;
1590
+ opts = blueprint;
1591
+ if (isFunction(opts)) {
1592
+ opts = opts();
1541
1593
  }
1542
- return assign(hash, newOptions);
1594
+ return assign(hash, opts);
1543
1595
  };
1544
1596
  hash.reset();
1545
1597
  return hash;
@@ -1618,157 +1670,6 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1618
1670
  });
1619
1671
  };
1620
1672
 
1621
- /***
1622
- Normalizes the given params object to the form returned by
1623
- [`jQuery.serializeArray`](https://api.jquery.com/serializeArray/).
1624
-
1625
- @function up.util.requestDataAsArray
1626
- @param {Object|Array|undefined|null} data
1627
- @internal
1628
- */
1629
- requestDataAsArray = function(data) {
1630
- var array, j, len, pair, part, query, ref;
1631
- if (isArray(data)) {
1632
- data;
1633
- }
1634
- if (isFormData(data)) {
1635
- return up.fail('Cannot convert FormData into an array');
1636
- } else {
1637
- query = requestDataAsQuery(data);
1638
- array = [];
1639
- ref = query.split('&');
1640
- for (j = 0, len = ref.length; j < len; j++) {
1641
- part = ref[j];
1642
- if (isPresent(part)) {
1643
- pair = part.split('=');
1644
- array.push({
1645
- name: decodeURIComponent(pair[0]),
1646
- value: decodeURIComponent(pair[1])
1647
- });
1648
- }
1649
- }
1650
- return array;
1651
- }
1652
- };
1653
-
1654
- /***
1655
- Returns an URL-encoded query string for the given params object.
1656
-
1657
- The returned string does **not** include a leading `?` character.
1658
-
1659
- @function up.util.requestDataAsQuery
1660
- @param {Object|Array|undefined|null} data
1661
- @internal
1662
- */
1663
- requestDataAsQuery = function(data, opts) {
1664
- var query;
1665
- opts = options(opts, {
1666
- purpose: 'url'
1667
- });
1668
- if (isString(data)) {
1669
- return data.replace(/^\?/, '');
1670
- } else if (isFormData(data)) {
1671
- return up.fail('Cannot convert FormData into a query string');
1672
- } else if (isPresent(data)) {
1673
- query = $.param(data);
1674
- switch (opts.purpose) {
1675
- case 'url':
1676
- query = query.replace(/\+/g, '%20');
1677
- break;
1678
- case 'form':
1679
- query = query.replace(/\%20/g, '+');
1680
- break;
1681
- default:
1682
- up.fail('Unknown purpose %o', opts.purpose);
1683
- }
1684
- return query;
1685
- } else {
1686
- return "";
1687
- }
1688
- };
1689
- $submittingButton = function($form) {
1690
- var $activeElement, submitButtonSelector;
1691
- submitButtonSelector = 'input[type=submit], button[type=submit], button:not([type])';
1692
- $activeElement = $(document.activeElement);
1693
- if ($activeElement.is(submitButtonSelector) && $form.has($activeElement)) {
1694
- return $activeElement;
1695
- } else {
1696
- return $form.find(submitButtonSelector).first();
1697
- }
1698
- };
1699
-
1700
- /***
1701
- Serializes the given form into a request data representation.
1702
-
1703
- @function up.util.requestDataFromForm
1704
- @return {Array|FormData}
1705
- @internal
1706
- */
1707
- requestDataFromForm = function(form) {
1708
- var $button, $form, buttonName, buttonValue, data, hasFileInputs;
1709
- $form = $(form);
1710
- hasFileInputs = $form.find('input[type=file]').length;
1711
- $button = $submittingButton($form);
1712
- buttonName = $button.attr('name');
1713
- buttonValue = $button.val();
1714
- data = hasFileInputs ? new FormData($form.get(0)) : $form.serializeArray();
1715
- if (isPresent(buttonName)) {
1716
- appendRequestData(data, buttonName, buttonValue);
1717
- }
1718
- return data;
1719
- };
1720
-
1721
- /***
1722
- Adds a key/value pair to the given request data representation.
1723
-
1724
- This mutates the given `data` if `data` is a `FormData`, an object
1725
- or an array. When `data` is a string a new string with the appended key/value
1726
- pair is returned.
1727
-
1728
- @function up.util.appendRequestData
1729
- @param {FormData|Object|Array|undefined|null} data
1730
- @param {string} key
1731
- @param {string|Blob|File} value
1732
- @internal
1733
- */
1734
- appendRequestData = function(data, name, value, opts) {
1735
- var newPair;
1736
- data || (data = []);
1737
- if (isArray(data)) {
1738
- data.push({
1739
- name: name,
1740
- value: value
1741
- });
1742
- } else if (isFormData(data)) {
1743
- data.append(name, value);
1744
- } else if (isObject(data)) {
1745
- data[name] = value;
1746
- } else if (isString(data)) {
1747
- newPair = requestDataAsQuery([
1748
- {
1749
- name: name,
1750
- value: value
1751
- }
1752
- ], opts);
1753
- data = [data, newPair].join('&');
1754
- }
1755
- return data;
1756
- };
1757
-
1758
- /***
1759
- Merges the request data in `source` into `target`.
1760
- Will modify the passed-in `target`.
1761
-
1762
- @return
1763
- The merged form data.
1764
- */
1765
- mergeRequestData = function(target, source) {
1766
- each(requestDataAsArray(source), function(field) {
1767
- return target = appendRequestData(target, field.name, field.value);
1768
- });
1769
- return target;
1770
- };
1771
-
1772
1673
  /***
1773
1674
  Throws a [JavaScript error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
1774
1675
  with the given message.
@@ -1838,6 +1739,12 @@ that might save you from loading something like [Lodash](https://lodash.com/).
1838
1739
  renameKey = function(object, oldKey, newKey) {
1839
1740
  return object[newKey] = pluckKey(object, oldKey);
1840
1741
  };
1742
+ deprecateRenamedKey = function(object, oldKey, newKey) {
1743
+ if (isDefined(object[oldKey])) {
1744
+ up.warn('Deprecated: Object key { %s } has been renamed to { %s } (found in %o)', oldKey, newKey, object);
1745
+ return renameKey(object, oldKey, newKey);
1746
+ }
1747
+ };
1841
1748
  pluckData = function(elementOrSelector, key) {
1842
1749
  var $element, value;
1843
1750
  $element = $(elementOrSelector);
@@ -2244,7 +2151,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2244
2151
  };
2245
2152
  CSS_LENGTH_PROPS = arrayToSet(['top', 'right', 'bottom', 'left', 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'width', 'height', 'maxWidth', 'maxHeight', 'minWidth', 'minHeight']);
2246
2153
 
2247
- /**
2154
+ /***
2248
2155
  Converts the given value to a CSS length value, adding a `px` unit if required.
2249
2156
 
2250
2157
  @function up.util.cssLength
@@ -2258,7 +2165,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2258
2165
  }
2259
2166
  };
2260
2167
 
2261
- /**
2168
+ /***
2262
2169
  Returns whether the given element has a CSS transition set.
2263
2170
 
2264
2171
  @function up.util.hasCssTransition
@@ -2302,6 +2209,9 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2302
2209
  }
2303
2210
  return flattened;
2304
2211
  };
2212
+ flatMap = function(array, block) {
2213
+ return flatten(map(array, block));
2214
+ };
2305
2215
 
2306
2216
  /***
2307
2217
  Returns whether the given value is truthy.
@@ -2387,6 +2297,19 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2387
2297
  return Promise.reject(error);
2388
2298
  }
2389
2299
  };
2300
+ sum = function(list, block) {
2301
+ var entry, entryValue, j, len, totalValue;
2302
+ block = listBlock(block);
2303
+ totalValue = 0;
2304
+ for (j = 0, len = list.length; j < len; j++) {
2305
+ entry = list[j];
2306
+ entryValue = block(entry);
2307
+ if (isGiven(entryValue)) {
2308
+ totalValue += entryValue;
2309
+ }
2310
+ }
2311
+ return totalValue;
2312
+ };
2390
2313
 
2391
2314
  /***
2392
2315
  Returns whether the given element is a descendant of the `<body>` element.
@@ -2397,6 +2320,9 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2397
2320
  isBodyDescendant = function(element) {
2398
2321
  return $(element).parents('body').length > 0;
2399
2322
  };
2323
+ isBasicObjectProperty = function(k) {
2324
+ return Object.prototype.hasOwnProperty(k);
2325
+ };
2400
2326
  isEqual = function(a, b) {
2401
2327
  if (typeof a !== typeof b) {
2402
2328
  return false;
@@ -2410,12 +2336,17 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2410
2336
  return a === b;
2411
2337
  }
2412
2338
  };
2339
+ splitValues = function(string, separator) {
2340
+ var values;
2341
+ if (separator == null) {
2342
+ separator = ' ';
2343
+ }
2344
+ values = string.split(separator);
2345
+ values = map(values, trim);
2346
+ values = select(values, isPresent);
2347
+ return values;
2348
+ };
2413
2349
  return {
2414
- requestDataAsArray: requestDataAsArray,
2415
- requestDataAsQuery: requestDataAsQuery,
2416
- appendRequestData: appendRequestData,
2417
- mergeRequestData: mergeRequestData,
2418
- requestDataFromForm: requestDataFromForm,
2419
2350
  offsetParent: offsetParent,
2420
2351
  fixedToAbsolute: fixedToAbsolute,
2421
2352
  isFixed: isFixed,
@@ -2428,15 +2359,18 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2428
2359
  $createElementFromSelector: $createElementFromSelector,
2429
2360
  $createPlaceholder: $createPlaceholder,
2430
2361
  selectorForElement: selectorForElement,
2362
+ attributeSelector: attributeSelector,
2431
2363
  assign: assign,
2432
2364
  assignPolyfill: assignPolyfill,
2433
2365
  copy: copy,
2434
2366
  merge: merge,
2435
- options: options,
2367
+ options: newOptions,
2436
2368
  option: option,
2437
2369
  fail: fail,
2438
2370
  each: each,
2371
+ eachIterator: eachIterator,
2439
2372
  map: map,
2373
+ flatMap: flatMap,
2440
2374
  times: times,
2441
2375
  any: any,
2442
2376
  all: all,
@@ -2459,6 +2393,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2459
2393
  isObject: isObject,
2460
2394
  isFunction: isFunction,
2461
2395
  isString: isString,
2396
+ isBoolean: isBoolean,
2462
2397
  isNumber: isNumber,
2463
2398
  isElement: isElement,
2464
2399
  isJQuery: isJQuery,
@@ -2487,6 +2422,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2487
2422
  contains: contains,
2488
2423
  toArray: toArray,
2489
2424
  castedAttr: castedAttr,
2425
+ jsonAttr: jsonAttr,
2490
2426
  clientSize: clientSize,
2491
2427
  only: only,
2492
2428
  except: except,
@@ -2508,6 +2444,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2508
2444
  pluckData: pluckData,
2509
2445
  pluckKey: pluckKey,
2510
2446
  renameKey: renameKey,
2447
+ deprecateRenamedKey: deprecateRenamedKey,
2511
2448
  extractOptions: extractOptions,
2512
2449
  isDetached: isDetached,
2513
2450
  noop: noop,
@@ -2526,11 +2463,13 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2526
2463
  detachWith: detachWith,
2527
2464
  flatten: flatten,
2528
2465
  isTruthy: isTruthy,
2466
+ isSingletonElement: isSingletonElement,
2529
2467
  newDeferred: newDeferred,
2530
2468
  always: always,
2531
2469
  muteRejection: muteRejection,
2532
2470
  rejectOnError: rejectOnError,
2533
2471
  isBodyDescendant: isBodyDescendant,
2472
+ isBasicObjectProperty: isBasicObjectProperty,
2534
2473
  isCrossDomain: isCrossDomain,
2535
2474
  microtask: microtask,
2536
2475
  isEqual: isEqual,
@@ -2540,7 +2479,10 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2540
2479
  readComputedStyleNumber: readComputedStyleNumber,
2541
2480
  readInlineStyle: readInlineStyle,
2542
2481
  writeInlineStyle: writeInlineStyle,
2543
- hasCssTransition: hasCssTransition
2482
+ hasCssTransition: hasCssTransition,
2483
+ splitValues: splitValues,
2484
+ sum: sum,
2485
+ values: objectValues
2544
2486
  };
2545
2487
  })(jQuery);
2546
2488
 
@@ -2570,7 +2512,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2570
2512
  @param {number|Function(): number} [config.expiry]
2571
2513
  The number of milliseconds after which a cache entry
2572
2514
  will be discarded.
2573
- @param {string} [config.log]
2515
+ @param {string} [config.logPrefix]
2574
2516
  A prefix for log entries printed by this cache object.
2575
2517
  @param {Function(any): string} [config.key]
2576
2518
  A function that takes an argument and returns a string key
@@ -2597,7 +2539,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2597
2539
  this.normalizeStoreKey = bind(this.normalizeStoreKey, this);
2598
2540
  this.expiryMillis = bind(this.expiryMillis, this);
2599
2541
  this.maxKeys = bind(this.maxKeys, this);
2600
- this.store = {};
2542
+ this.store = this.config.store || new up.store.Memory();
2601
2543
  }
2602
2544
 
2603
2545
  Cache.prototype.maxKeys = function() {
@@ -2612,7 +2554,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2612
2554
  if (this.config.key) {
2613
2555
  return this.config.key(key);
2614
2556
  } else {
2615
- return this.key.toString();
2557
+ return key.toString();
2616
2558
  }
2617
2559
  };
2618
2560
 
@@ -2629,7 +2571,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2629
2571
  };
2630
2572
 
2631
2573
  Cache.prototype.clear = function() {
2632
- return this.store = {};
2574
+ return this.store.clear();
2633
2575
  };
2634
2576
 
2635
2577
  Cache.prototype.log = function() {
@@ -2642,7 +2584,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2642
2584
  };
2643
2585
 
2644
2586
  Cache.prototype.keys = function() {
2645
- return Object.keys(this.store);
2587
+ return this.store.keys();
2646
2588
  };
2647
2589
 
2648
2590
  Cache.prototype.makeRoomForAnotherKey = function() {
@@ -2650,13 +2592,13 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2650
2592
  storeKeys = u.copy(this.keys());
2651
2593
  max = this.maxKeys();
2652
2594
  if (max && storeKeys.length >= max) {
2653
- oldestKey = null;
2654
- oldestTimestamp = null;
2595
+ oldestKey = void 0;
2596
+ oldestTimestamp = void 0;
2655
2597
  u.each(storeKeys, (function(_this) {
2656
2598
  return function(key) {
2657
- var promise, timestamp;
2658
- promise = _this.store[key];
2659
- timestamp = promise.timestamp;
2599
+ var entry, timestamp;
2600
+ entry = _this.store.get(key);
2601
+ timestamp = entry.timestamp;
2660
2602
  if (!oldestTimestamp || oldestTimestamp > timestamp) {
2661
2603
  oldestKey = key;
2662
2604
  return oldestTimestamp = timestamp;
@@ -2664,7 +2606,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2664
2606
  };
2665
2607
  })(this));
2666
2608
  if (oldestKey) {
2667
- return delete this.store[oldestKey];
2609
+ return this.store.remove(oldestKey);
2668
2610
  }
2669
2611
  }
2670
2612
  };
@@ -2684,15 +2626,16 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2684
2626
  };
2685
2627
 
2686
2628
  Cache.prototype.set = function(key, value) {
2687
- var storeKey;
2629
+ var storeKey, timestampedValue;
2688
2630
  if (this.isEnabled() && this.isCachable(key)) {
2689
2631
  this.makeRoomForAnotherKey();
2690
2632
  storeKey = this.normalizeStoreKey(key);
2691
2633
  this.log("Setting entry %o to %o", storeKey, value);
2692
- return this.store[storeKey] = {
2634
+ timestampedValue = {
2693
2635
  timestamp: this.timestamp(),
2694
2636
  value: value
2695
2637
  };
2638
+ return this.store.set(storeKey, timestampedValue);
2696
2639
  }
2697
2640
  };
2698
2641
 
@@ -2700,7 +2643,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2700
2643
  var storeKey;
2701
2644
  if (this.isCachable(key)) {
2702
2645
  storeKey = this.normalizeStoreKey(key);
2703
- return delete this.store[storeKey];
2646
+ return this.store.remove(storeKey);
2704
2647
  }
2705
2648
  };
2706
2649
 
@@ -2720,7 +2663,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2720
2663
  if (options == null) {
2721
2664
  options = {};
2722
2665
  }
2723
- if (this.isCachable(key) && (entry = this.store[this.normalizeStoreKey(key)])) {
2666
+ if (this.isCachable(key) && (entry = this.store.get(this.normalizeStoreKey(key)))) {
2724
2667
  if (this.isFresh(entry)) {
2725
2668
  if (!options.silent) {
2726
2669
  this.log("Cache hit for '%s'", key);
@@ -2745,6 +2688,160 @@ that might save you from loading something like [Lodash](https://lodash.com/).
2745
2688
 
2746
2689
  })();
2747
2690
 
2691
+ }).call(this);
2692
+ (function() {
2693
+ var u,
2694
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2695
+ slice = [].slice;
2696
+
2697
+ u = up.util;
2698
+
2699
+ up.Record = (function() {
2700
+ Record.prototype.fields = function() {
2701
+ throw 'Return an array of property names';
2702
+ };
2703
+
2704
+ function Record(options) {
2705
+ this.copy = bind(this.copy, this);
2706
+ this.attributes = bind(this.attributes, this);
2707
+ u.assign(this, this.attributes(options));
2708
+ }
2709
+
2710
+ Record.prototype.attributes = function(source) {
2711
+ if (source == null) {
2712
+ source = this;
2713
+ }
2714
+ return u.only.apply(u, [source].concat(slice.call(this.fields())));
2715
+ };
2716
+
2717
+ Record.prototype.copy = function(changes) {
2718
+ var attributesWithChanges;
2719
+ if (changes == null) {
2720
+ changes = {};
2721
+ }
2722
+ attributesWithChanges = u.merge(this.attributes(), changes);
2723
+ return new this.constructor(attributesWithChanges);
2724
+ };
2725
+
2726
+ return Record;
2727
+
2728
+ })();
2729
+
2730
+ }).call(this);
2731
+ (function() {
2732
+ var u;
2733
+
2734
+ u = up.util;
2735
+
2736
+ up.CompilePass = (function() {
2737
+ function CompilePass($root, compilers, options) {
2738
+ this.$root = $root;
2739
+ this.compilers = compilers;
2740
+ if (options == null) {
2741
+ options = {};
2742
+ }
2743
+ this.root = this.$root[0];
2744
+ this.$skipSubtrees = $(options.skip);
2745
+ if (!(this.$skipSubtrees.length && this.root.querySelector('[up-keep]'))) {
2746
+ this.$skipSubtrees = void 0;
2747
+ }
2748
+ }
2749
+
2750
+ CompilePass.prototype.compile = function() {
2751
+ return up.log.group("Compiling fragment %o", this.root, (function(_this) {
2752
+ return function() {
2753
+ var compiler, i, len, ref, results;
2754
+ ref = _this.compilers;
2755
+ results = [];
2756
+ for (i = 0, len = ref.length; i < len; i++) {
2757
+ compiler = ref[i];
2758
+ results.push(_this.runCompiler(compiler));
2759
+ }
2760
+ return results;
2761
+ };
2762
+ })(this));
2763
+ };
2764
+
2765
+ CompilePass.prototype.runCompiler = function(compiler) {
2766
+ var $matches;
2767
+ $matches = this.$select(compiler.selector);
2768
+ if (!$matches.length) {
2769
+ return;
2770
+ }
2771
+ return up.log.group((!compiler.isSystem ? "Compiling '%s' on %d element(s)" : void 0), compiler.selector, $matches.length, (function(_this) {
2772
+ return function() {
2773
+ var i, keepValue, len, match, value;
2774
+ if (compiler.batch) {
2775
+ _this.compileBatch(compiler, $matches);
2776
+ } else {
2777
+ for (i = 0, len = $matches.length; i < len; i++) {
2778
+ match = $matches[i];
2779
+ _this.compileOneElement(compiler, $(match));
2780
+ }
2781
+ }
2782
+ if (keepValue = compiler.keep) {
2783
+ value = u.isString(keepValue) ? keepValue : '';
2784
+ return $matches.attr('up-keep', value);
2785
+ }
2786
+ };
2787
+ })(this));
2788
+ };
2789
+
2790
+ CompilePass.prototype.compileOneElement = function(compiler, $element) {
2791
+ var compileArgs, data, destructor, result;
2792
+ compileArgs = [$element];
2793
+ if (compiler.length !== 1) {
2794
+ data = up.syntax.data($element);
2795
+ compileArgs.push(data);
2796
+ }
2797
+ result = compiler.apply($element[0], compileArgs);
2798
+ if (destructor = this.normalizeDestructor(result)) {
2799
+ return up.syntax.destructor($element, destructor);
2800
+ }
2801
+ };
2802
+
2803
+ CompilePass.prototype.compileBatch = function(compiler, $elements) {
2804
+ var compileArgs, dataList, result;
2805
+ compileArgs = [$elements];
2806
+ if (compiler.length !== 1) {
2807
+ dataList = u.map($elements, up.syntax.data);
2808
+ compileArgs.push(dataList);
2809
+ }
2810
+ result = compiler.apply($elements.get(), compileArgs);
2811
+ if (this.normalizeDestructor(result)) {
2812
+ return up.fail('Compilers with { batch: true } cannot return destructors');
2813
+ }
2814
+ };
2815
+
2816
+ CompilePass.prototype.normalizeDestructor = function(result) {
2817
+ if (u.isFunction(result)) {
2818
+ return result;
2819
+ } else if (u.isArray(result) && u.all(result, u.isFunction)) {
2820
+ up.warn('up.compiler(): Returning an array of destructor functions is deprecated. Return a single function instead.');
2821
+ return u.sequence.apply(u, result);
2822
+ }
2823
+ };
2824
+
2825
+ CompilePass.prototype.$select = function(selector) {
2826
+ var $matches, $skipSubtrees;
2827
+ if (u.isFunction(selector)) {
2828
+ selector = selector();
2829
+ }
2830
+ $matches = u.selectInSubtree(this.$root, selector);
2831
+ if ($skipSubtrees = this.$skipSubtrees) {
2832
+ $matches = $matches.filter(function() {
2833
+ var $match;
2834
+ $match = $(this);
2835
+ return $match.closest($skipSubtrees).length === 0;
2836
+ });
2837
+ }
2838
+ return $matches;
2839
+ };
2840
+
2841
+ return CompilePass;
2842
+
2843
+ })();
2844
+
2748
2845
  }).call(this);
2749
2846
  (function() {
2750
2847
  var u,
@@ -3291,6 +3388,10 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3291
3388
 
3292
3389
  })();
3293
3390
 
3391
+ }).call(this);
3392
+ (function() {
3393
+
3394
+
3294
3395
  }).call(this);
3295
3396
  (function() {
3296
3397
  var u,
@@ -3339,9 +3440,14 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3339
3440
  }
3340
3441
  parts = [];
3341
3442
  this.selectors.forEach(function(variantSelector) {
3342
- return ['a', '[up-href]'].forEach(function(tagSelector) {
3343
- return parts.push("" + tagSelector + variantSelector + additionalClause);
3344
- });
3443
+ var i, len, ref, results, tagSelector;
3444
+ ref = ['a', '[up-href]'];
3445
+ results = [];
3446
+ for (i = 0, len = ref.length; i < len; i++) {
3447
+ tagSelector = ref[i];
3448
+ results.push(parts.push("" + tagSelector + variantSelector + additionalClause));
3449
+ }
3450
+ return results;
3345
3451
  });
3346
3452
  return parts.join(', ');
3347
3453
  };
@@ -3364,12 +3470,14 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3364
3470
  };
3365
3471
 
3366
3472
  FollowVariant.prototype.followLink = function($link, options) {
3367
- if (options == null) {
3368
- options = {};
3369
- }
3370
- return up.bus.whenEmitted('up:link:follow', {
3473
+ var followEventAttrs;
3474
+ options = u.options(options);
3475
+ followEventAttrs = {
3476
+ message: 'Following link',
3477
+ $link: $link,
3371
3478
  $element: $link
3372
- }).then((function(_this) {
3479
+ };
3480
+ return up.bus.whenEmitted('up:link:follow', followEventAttrs).then((function(_this) {
3373
3481
  return function() {
3374
3482
  return up.feedback.start($link, options, function() {
3375
3483
  return _this.followNow($link, options);
@@ -3379,9 +3487,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3379
3487
  };
3380
3488
 
3381
3489
  FollowVariant.prototype.preloadLink = function($link, options) {
3382
- if (options == null) {
3383
- options = {};
3384
- }
3490
+ options = u.options(options);
3385
3491
  return this.preloadNow($link, options);
3386
3492
  };
3387
3493
 
@@ -3653,60 +3759,21 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3653
3759
  (function() {
3654
3760
  var u,
3655
3761
  bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3656
- slice = [].slice;
3762
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
3763
+ hasProp = {}.hasOwnProperty;
3657
3764
 
3658
3765
  u = up.util;
3659
3766
 
3660
- up.Record = (function() {
3661
- Record.prototype.fields = function() {
3662
- throw 'Return an array of property names';
3663
- };
3664
3767
 
3665
- function Record(options) {
3666
- this.copy = bind(this.copy, this);
3667
- this.attributes = bind(this.attributes, this);
3668
- u.assign(this, this.attributes(options));
3669
- }
3768
+ /***
3769
+ Instances of `up.Request` normalizes properties of an [`AJAX request`](/up.request)
3770
+ such as the requested URL, form parameters and HTTP method.
3771
+
3772
+ @class up.Request
3773
+ */
3670
3774
 
3671
- Record.prototype.attributes = function(source) {
3672
- if (source == null) {
3673
- source = this;
3674
- }
3675
- return u.only.apply(u, [source].concat(slice.call(this.fields())));
3676
- };
3677
-
3678
- Record.prototype.copy = function(changes) {
3679
- var attributesWithChanges;
3680
- if (changes == null) {
3681
- changes = {};
3682
- }
3683
- attributesWithChanges = u.merge(this.attributes(), changes);
3684
- return new this.constructor(attributesWithChanges);
3685
- };
3686
-
3687
- return Record;
3688
-
3689
- })();
3690
-
3691
- }).call(this);
3692
- (function() {
3693
- var u,
3694
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3695
- extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
3696
- hasProp = {}.hasOwnProperty;
3697
-
3698
- u = up.util;
3699
-
3700
-
3701
- /***
3702
- Instances of `up.Request` normalizes properties of an [`AJAX request`](/up.request)
3703
- such as the requested URL, form parameters and HTTP method.
3704
-
3705
- @class up.Request
3706
- */
3707
-
3708
- up.Request = (function(superClass) {
3709
- extend(Request, superClass);
3775
+ up.Request = (function(superClass) {
3776
+ extend(Request, superClass);
3710
3777
 
3711
3778
 
3712
3779
  /***
@@ -3728,16 +3795,10 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3728
3795
 
3729
3796
 
3730
3797
  /***
3731
- Parameters that should be sent as the request's payload.
3798
+ [Parameters](/up.params) that should be sent as the request's payload.
3732
3799
 
3733
- Parameters may be passed as one of the following forms:
3734
-
3735
- 1. An object where keys are param names and the values are param values
3736
- 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
3737
- 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
3738
-
3739
- @property up.Request#data
3740
- @param {String} data
3800
+ @property up.Request#params
3801
+ @param {object|FormData|string|Array} params
3741
3802
  @stable
3742
3803
  */
3743
3804
 
@@ -3781,7 +3842,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3781
3842
  */
3782
3843
 
3783
3844
  Request.prototype.fields = function() {
3784
- return ['method', 'url', 'data', 'target', 'failTarget', 'headers', 'timeout'];
3845
+ return ['method', 'url', 'params', 'data', 'target', 'failTarget', 'headers', 'timeout', 'preload', 'cache'];
3785
3846
  };
3786
3847
 
3787
3848
 
@@ -3799,8 +3860,8 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3799
3860
  this.navigate = bind(this.navigate, this);
3800
3861
  this.send = bind(this.send, this);
3801
3862
  this.isSafe = bind(this.isSafe, this);
3802
- this.transferSearchToData = bind(this.transferSearchToData, this);
3803
- this.transferDataToUrl = bind(this.transferDataToUrl, this);
3863
+ this.transferSearchToParams = bind(this.transferSearchToParams, this);
3864
+ this.transferParamsToUrl = bind(this.transferParamsToUrl, this);
3804
3865
  this.extractHashFromUrl = bind(this.extractHashFromUrl, this);
3805
3866
  this.normalize = bind(this.normalize, this);
3806
3867
  Request.__super__.constructor.call(this, options);
@@ -3808,42 +3869,38 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3808
3869
  }
3809
3870
 
3810
3871
  Request.prototype.normalize = function() {
3872
+ u.deprecateRenamedKey(this, 'data', 'params');
3811
3873
  this.method = u.normalizeMethod(this.method);
3812
3874
  this.headers || (this.headers = {});
3813
3875
  this.extractHashFromUrl();
3814
3876
  if (u.methodAllowsPayload(this.method)) {
3815
- return this.transferSearchToData();
3877
+ return this.transferSearchToParams();
3816
3878
  } else {
3817
- return this.transferDataToUrl();
3879
+ return this.transferParamsToUrl();
3818
3880
  }
3819
3881
  };
3820
3882
 
3821
3883
  Request.prototype.extractHashFromUrl = function() {
3822
3884
  var urlParts;
3823
3885
  urlParts = u.parseUrl(this.url);
3824
- this.hash = urlParts.hash;
3886
+ this.hash = u.presence(urlParts.hash);
3825
3887
  return this.url = u.normalizeUrl(urlParts, {
3826
3888
  hash: false
3827
3889
  });
3828
3890
  };
3829
3891
 
3830
- Request.prototype.transferDataToUrl = function() {
3831
- var query, separator;
3832
- if (this.data && !u.isFormData(this.data)) {
3833
- query = u.requestDataAsQuery(this.data);
3834
- separator = u.contains(this.url, '?') ? '&' : '?';
3835
- this.url += separator + query;
3836
- return this.data = void 0;
3892
+ Request.prototype.transferParamsToUrl = function() {
3893
+ if (this.params && !u.isFormData(this.params)) {
3894
+ this.url = up.params.buildURL(this.url, this.params);
3895
+ return this.params = void 0;
3837
3896
  }
3838
3897
  };
3839
3898
 
3840
- Request.prototype.transferSearchToData = function() {
3841
- var query, urlParts;
3842
- urlParts = u.parseUrl(this.url);
3843
- query = urlParts.search;
3844
- if (query) {
3845
- this.data = u.mergeRequestData(this.data, query);
3846
- return this.url = u.normalizeUrl(urlParts, {
3899
+ Request.prototype.transferSearchToParams = function() {
3900
+ var query;
3901
+ if (query = up.params.fromURL(this.url)) {
3902
+ this.params = up.params.merge(this.params, query);
3903
+ return this.url = u.normalizeUrl(this.url, {
3847
3904
  search: false
3848
3905
  });
3849
3906
  }
@@ -3856,34 +3913,31 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3856
3913
  Request.prototype.send = function() {
3857
3914
  return new Promise((function(_this) {
3858
3915
  return function(resolve, reject) {
3859
- var csrfToken, header, ref, resolveWithResponse, value, xhr, xhrData, xhrHeaders, xhrMethod, xhrUrl;
3916
+ var csrfToken, header, pc, ref, resolveWithResponse, value, xhr, xhrHeaders, xhrMethod, xhrPayload, xhrUrl;
3860
3917
  xhr = new XMLHttpRequest();
3861
3918
  xhrHeaders = u.copy(_this.headers);
3862
- xhrData = _this.data;
3919
+ xhrPayload = _this.params;
3863
3920
  xhrMethod = _this.method;
3864
3921
  xhrUrl = _this.url;
3865
- ref = up.proxy.wrapMethod(xhrMethod, xhrData), xhrMethod = ref[0], xhrData = ref[1];
3866
- if (u.isFormData(xhrData)) {
3922
+ ref = up.proxy.wrapMethod(xhrMethod, xhrPayload), xhrMethod = ref[0], xhrPayload = ref[1];
3923
+ if (xhrPayload) {
3867
3924
  delete xhrHeaders['Content-Type'];
3868
- } else if (u.isPresent(xhrData)) {
3869
- xhrData = u.requestDataAsQuery(xhrData, {
3870
- purpose: 'form'
3871
- });
3872
- xhrHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
3925
+ xhrPayload = up.params.toFormData(xhrPayload);
3873
3926
  } else {
3874
- xhrData = null;
3927
+ xhrPayload = null;
3875
3928
  }
3929
+ pc = up.protocol.config;
3876
3930
  if (_this.target) {
3877
- xhrHeaders[up.protocol.config.targetHeader] = _this.target;
3931
+ xhrHeaders[pc.targetHeader] = _this.target;
3878
3932
  }
3879
3933
  if (_this.failTarget) {
3880
- xhrHeaders[up.protocol.config.failTargetHeader] = _this.failTarget;
3934
+ xhrHeaders[pc.failTargetHeader] = _this.failTarget;
3881
3935
  }
3882
3936
  if (!_this.isCrossDomain()) {
3883
3937
  xhrHeaders['X-Requested-With'] || (xhrHeaders['X-Requested-With'] = 'XMLHttpRequest');
3884
3938
  }
3885
3939
  if (csrfToken = _this.csrfToken()) {
3886
- xhrHeaders[up.protocol.config.csrfHeader] = csrfToken;
3940
+ xhrHeaders[pc.csrfHeader] = csrfToken;
3887
3941
  }
3888
3942
  xhr.open(xhrMethod, xhrUrl);
3889
3943
  for (header in xhrHeaders) {
@@ -3905,14 +3959,14 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3905
3959
  if (_this.timeout) {
3906
3960
  xhr.timeout = _this.timeout;
3907
3961
  }
3908
- return xhr.send(xhrData);
3962
+ return xhr.send(xhrPayload);
3909
3963
  };
3910
3964
  })(this));
3911
3965
  };
3912
3966
 
3913
3967
  Request.prototype.navigate = function() {
3914
3968
  var $form, addField, csrfParam, csrfToken, formMethod;
3915
- this.transferSearchToData();
3969
+ this.transferSearchToParams();
3916
3970
  $form = $('<form class="up-page-loader"></form>');
3917
3971
  addField = function(field) {
3918
3972
  return $('<input type="hidden">').attr(field).appendTo($form);
@@ -3936,7 +3990,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3936
3990
  value: csrfToken
3937
3991
  });
3938
3992
  }
3939
- u.each(u.requestDataAsArray(this.data), addField);
3993
+ u.each(up.params.toArray(this.params), addField);
3940
3994
  $form.hide().appendTo('body');
3941
3995
  return up.browser.submitForm($form);
3942
3996
  };
@@ -3970,11 +4024,13 @@ that might save you from loading something like [Lodash](https://lodash.com/).
3970
4024
  };
3971
4025
 
3972
4026
  Request.prototype.isCachable = function() {
3973
- return this.isSafe() && !u.isFormData(this.data);
4027
+ return this.isSafe() && !u.isFormData(this.params);
3974
4028
  };
3975
4029
 
3976
4030
  Request.prototype.cacheKey = function() {
3977
- return [this.url, this.method, u.requestDataAsQuery(this.data), this.target].join('|');
4031
+ var query;
4032
+ query = up.params.toQuery(this.params);
4033
+ return [this.url, this.method, query, this.target].join('|');
3978
4034
  };
3979
4035
 
3980
4036
  Request.wrap = function(object) {
@@ -4098,6 +4154,7 @@ that might save you from loading something like [Lodash](https://lodash.com/).
4098
4154
  };
4099
4155
 
4100
4156
  function Response(options) {
4157
+ this.getHeader = bind(this.getHeader, this);
4101
4158
  this.isFatalError = bind(this.isFatalError, this);
4102
4159
  this.isError = bind(this.isError, this);
4103
4160
  this.isSuccess = bind(this.isSuccess, this);
@@ -4150,10 +4207,139 @@ that might save you from loading something like [Lodash](https://lodash.com/).
4150
4207
  return this.isError() && u.isBlank(this.text);
4151
4208
  };
4152
4209
 
4210
+
4211
+ /***
4212
+ Returns the HTTP header value with the given name.
4213
+
4214
+ The search for the header name is case-insensitive.
4215
+
4216
+ Returns `undefined` if the given header name was not included in the response.
4217
+
4218
+ @function up.Response#getHeader
4219
+ @param {string} name
4220
+ @return {string|undefined} value
4221
+ @experimental
4222
+ */
4223
+
4224
+ Response.prototype.getHeader = function(name) {
4225
+ return this.xhr.getResponseHeader(name);
4226
+ };
4227
+
4153
4228
  return Response;
4154
4229
 
4155
4230
  })(up.Record);
4156
4231
 
4232
+ }).call(this);
4233
+ (function() {
4234
+ var u,
4235
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4236
+
4237
+ up.store || (up.store = {});
4238
+
4239
+ u = up.util;
4240
+
4241
+ up.store.Memory = (function() {
4242
+ function Memory() {
4243
+ this.values = bind(this.values, this);
4244
+ this.keys = bind(this.keys, this);
4245
+ this.remove = bind(this.remove, this);
4246
+ this.set = bind(this.set, this);
4247
+ this.get = bind(this.get, this);
4248
+ this.clear = bind(this.clear, this);
4249
+ this.clear();
4250
+ }
4251
+
4252
+ Memory.prototype.clear = function() {
4253
+ return this.data = {};
4254
+ };
4255
+
4256
+ Memory.prototype.get = function(key) {
4257
+ return this.data[key];
4258
+ };
4259
+
4260
+ Memory.prototype.set = function(key, value) {
4261
+ return this.data[key] = value;
4262
+ };
4263
+
4264
+ Memory.prototype.remove = function(key) {
4265
+ return delete this.data[key];
4266
+ };
4267
+
4268
+ Memory.prototype.keys = function() {
4269
+ return Object.keys(this.data);
4270
+ };
4271
+
4272
+ Memory.prototype.values = function() {
4273
+ return u.values(this.data);
4274
+ };
4275
+
4276
+ return Memory;
4277
+
4278
+ })();
4279
+
4280
+ }).call(this);
4281
+ (function() {
4282
+ var u,
4283
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
4284
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
4285
+ hasProp = {}.hasOwnProperty;
4286
+
4287
+ u = up.util;
4288
+
4289
+ up.store.Session = (function(superClass) {
4290
+ extend(Session, superClass);
4291
+
4292
+ function Session(rootKey) {
4293
+ this.saveToSessionStorage = bind(this.saveToSessionStorage, this);
4294
+ this.loadFromSessionStorage = bind(this.loadFromSessionStorage, this);
4295
+ this.remove = bind(this.remove, this);
4296
+ this.set = bind(this.set, this);
4297
+ this.clear = bind(this.clear, this);
4298
+ this.rootKey = rootKey;
4299
+ this.loadFromSessionStorage();
4300
+ }
4301
+
4302
+ Session.prototype.clear = function() {
4303
+ Session.__super__.clear.call(this);
4304
+ return this.saveToSessionStorage();
4305
+ };
4306
+
4307
+ Session.prototype.set = function(key, value) {
4308
+ Session.__super__.set.call(this, key, value);
4309
+ return this.saveToSessionStorage();
4310
+ };
4311
+
4312
+ Session.prototype.remove = function(key) {
4313
+ Session.__super__.remove.call(this, key);
4314
+ return this.saveToSessionStorage();
4315
+ };
4316
+
4317
+ Session.prototype.loadFromSessionStorage = function() {
4318
+ var raw;
4319
+ try {
4320
+ if (raw = typeof sessionStorage !== "undefined" && sessionStorage !== null ? sessionStorage.getItem(this.rootKey) : void 0) {
4321
+ this.data = JSON.parse(raw);
4322
+ }
4323
+ } catch (error) {
4324
+
4325
+ }
4326
+ return this.data || (this.data = {});
4327
+ };
4328
+
4329
+ Session.prototype.saveToSessionStorage = function() {
4330
+ var json;
4331
+ json = JSON.stringify(this.data);
4332
+ try {
4333
+ return typeof sessionStorage !== "undefined" && sessionStorage !== null ? sessionStorage.setItem(this.rootKey, json) : void 0;
4334
+ } catch (error) {
4335
+
4336
+ }
4337
+ };
4338
+
4339
+ return Session;
4340
+
4341
+ })(up.store.Memory);
4342
+
4157
4343
  }).call(this);
4158
4344
  (function() {
4159
4345
  var u,
@@ -4231,14 +4417,14 @@ Internet Explorer 10 or lower
4231
4417
  var slice = [].slice;
4232
4418
 
4233
4419
  up.browser = (function($) {
4234
- var CONSOLE_PLACEHOLDERS, canConsole, canCssTransition, canCustomElements, canDOMParser, canFormData, canInputEvent, canPromise, canPushState, hash, isIE10OrWorse, isRecentJQuery, isSupported, navigate, polyfilledSessionStorage, popCookie, puts, sessionStorage, sprintf, sprintfWithFormattedArgs, stringifyArg, submitForm, u, url, whenConfirmed;
4420
+ var CONSOLE_PLACEHOLDERS, canConsole, canCssTransition, canCustomElements, canDOMParser, canFormData, canInputEvent, canInspectFormData, canPromise, canPushState, documentViewportSelector, isIE10OrWorse, isRecentJQuery, isSupported, navigate, popCookie, puts, sprintf, sprintfWithFormattedArgs, stringifyArg, submitForm, u, url, whenConfirmed;
4235
4421
  u = up.util;
4236
4422
 
4237
4423
  /***
4238
4424
  @method up.browser.navigate
4239
4425
  @param {string} url
4240
4426
  @param {string} [options.method='get']
4241
- @param {Object|Array} [options.data]
4427
+ @param {object|Array|FormData|string} [options.params]
4242
4428
  @internal
4243
4429
  */
4244
4430
  navigate = function(url, options) {
@@ -4430,6 +4616,15 @@ Internet Explorer 10 or lower
4430
4616
  return !!window.FormData;
4431
4617
  });
4432
4618
 
4619
+ /***
4620
+ @function up.browser.canInspectFormData
4621
+ @return {boolean}
4622
+ @internal
4623
+ */
4624
+ canInspectFormData = u.memoize(function() {
4625
+ return canFormData() && !!FormData.prototype.entries;
4626
+ });
4627
+
4433
4628
  /***
4434
4629
  Returns whether this browser supports the [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
4435
4630
  interface.
@@ -4463,14 +4658,6 @@ Internet Explorer 10 or lower
4463
4658
  minor = parseInt(parts[1]);
4464
4659
  return major >= 2 || (major === 1 && minor >= 9);
4465
4660
  });
4466
-
4467
- /***
4468
- Returns and deletes a cookie with the given name
4469
- Inspired by Turbolinks: https://github.com/rails/turbolinks/blob/83d4b3d2c52a681f07900c28adb28bc8da604733/lib/assets/javascripts/turbolinks.coffee#L292
4470
-
4471
- @function up.browser.popCookie
4472
- @internal
4473
- */
4474
4661
  popCookie = function(name) {
4475
4662
  var ref, value;
4476
4663
  value = (ref = document.cookie.match(new RegExp(name + "=(\\w+)"))) != null ? ref[1] : void 0;
@@ -4512,62 +4699,35 @@ Internet Explorer 10 or lower
4512
4699
  };
4513
4700
 
4514
4701
  /***
4515
- @internal
4516
- */
4517
- sessionStorage = u.memoize(function() {
4518
- try {
4519
- return window.sessionStorage;
4520
- } catch (error) {
4521
- return polyfilledSessionStorage();
4522
- }
4523
- });
4524
-
4525
- /***
4526
- @internal
4527
- */
4528
- polyfilledSessionStorage = function() {
4529
- var data;
4530
- data = {};
4531
- return {
4532
- getItem: function(prop) {
4533
- return data[prop];
4534
- },
4535
- setItem: function(prop, value) {
4536
- return data[prop] = value;
4537
- }
4538
- };
4539
- };
4540
-
4541
- /***
4542
- Returns `'foo'` if the hash is `'#foo'`.
4702
+ Return the [scrolling element](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)
4703
+ for the browser's main content area.
4543
4704
 
4544
- Returns undefined if the hash is `'#'`, `''` or `undefined`.
4545
-
4546
- @function up.browser.hash
4705
+ @function up.browser.documentViewportSelector
4547
4706
  @internal
4548
4707
  */
4549
- hash = function(value) {
4550
- value || (value = location.hash);
4551
- value || (value = '');
4552
- if (value[0] === '#') {
4553
- value = value.substr(1);
4708
+ documentViewportSelector = function() {
4709
+ var element;
4710
+ if (element = document.scrollingElement) {
4711
+ return element.tagName;
4712
+ } else {
4713
+ return 'html';
4554
4714
  }
4555
- return u.presence(value);
4556
4715
  };
4557
4716
  return {
4558
4717
  url: url,
4559
4718
  navigate: navigate,
4560
4719
  submitForm: submitForm,
4561
4720
  canPushState: canPushState,
4721
+ canFormData: canFormData,
4722
+ canInspectFormData: canInspectFormData,
4562
4723
  canCustomElements: canCustomElements,
4724
+ documentViewportSelector: documentViewportSelector,
4563
4725
  whenConfirmed: whenConfirmed,
4564
4726
  isSupported: isSupported,
4565
4727
  puts: puts,
4566
4728
  sprintf: sprintf,
4567
4729
  sprintfWithFormattedArgs: sprintfWithFormattedArgs,
4568
- sessionStorage: sessionStorage,
4569
- popCookie: popCookie,
4570
- hash: hash
4730
+ popCookie: popCookie
4571
4731
  };
4572
4732
  })(jQuery);
4573
4733
 
@@ -4626,7 +4786,7 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
4626
4786
  var slice = [].slice;
4627
4787
 
4628
4788
  up.bus = (function($) {
4629
- var boot, consumeAction, emit, emitReset, fixRenamedEvents, forgetUpDescription, haltEvent, live, liveUpDescriptions, logEmission, nextUpDescriptionNumber, nobodyPrevents, onEscape, rememberUpDescription, renamedEvent, renamedEvents, resetBus, snapshot, u, unbind, upDescriptionNumber, upDescriptionToJqueryDescription, upListenerToJqueryListener, whenEmitted;
4789
+ var boot, consumeAction, deprecateRenamedEvent, emit, emitReset, fixRenamedEvents, forgetUpDescription, haltEvent, live, liveUpDescriptions, logEmission, nextUpDescriptionNumber, nobodyPrevents, onEscape, rememberUpDescription, renamedEvents, resetBus, snapshot, u, unbind, upDescriptionNumber, upDescriptionToJqueryDescription, upListenerToJqueryListener, whenEmitted;
4630
4790
  u = up.util;
4631
4791
  liveUpDescriptions = {};
4632
4792
  nextUpDescriptionNumber = 0;
@@ -4641,9 +4801,14 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
4641
4801
  */
4642
4802
  upListenerToJqueryListener = function(upListener) {
4643
4803
  return function(event) {
4644
- var $me;
4804
+ var $me, args, expectedArgCount;
4645
4805
  $me = event.$element || $(this);
4646
- return upListener.apply($me.get(0), [event, $me, up.syntax.data($me)]);
4806
+ args = [event, $me];
4807
+ expectedArgCount = upListener.length;
4808
+ if (!(expectedArgCount === 1 || expectedArgCount === 2)) {
4809
+ args.push(up.syntax.data($me));
4810
+ }
4811
+ return upListener.apply($me.get(0), args);
4647
4812
  };
4648
4813
  };
4649
4814
 
@@ -4663,6 +4828,9 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
4663
4828
  if (isNew) {
4664
4829
  jqueryListener = upListenerToJqueryListener(upListener);
4665
4830
  upListener._asJqueryListener = jqueryListener;
4831
+ if (upListener._descriptionNumber) {
4832
+ up.fail('up.on(): The callback %o cannot be registered more than once');
4833
+ }
4666
4834
  upListener._descriptionNumber = ++nextUpDescriptionNumber;
4667
4835
  } else {
4668
4836
  jqueryListener = upListener._asJqueryListener;
@@ -4673,11 +4841,11 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
4673
4841
  };
4674
4842
  fixRenamedEvents = function(description) {
4675
4843
  var events;
4676
- events = description[0].split(/\s+/);
4844
+ events = u.splitValues(description[0]);
4677
4845
  events = u.map(events, function(event) {
4678
4846
  var newEvent;
4679
4847
  if (newEvent = renamedEvents[event]) {
4680
- up.log.warn(event + " has been renamed to " + newEvent);
4848
+ up.warn("Deprecated: " + event + " has been renamed to " + newEvent);
4681
4849
  return newEvent;
4682
4850
  } else {
4683
4851
  return event;
@@ -4824,8 +4992,11 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
4824
4992
  return liveUpDescriptions[number] = upDescription;
4825
4993
  };
4826
4994
  forgetUpDescription = function(upDescription) {
4827
- var number;
4995
+ var number, upListener;
4828
4996
  number = upDescriptionNumber(upDescription);
4997
+ upListener = u.last(upDescription);
4998
+ delete upListener._descriptionNumber;
4999
+ delete upListener._asJqueryListener;
4829
5000
  return delete liveUpDescriptions[number];
4830
5001
  };
4831
5002
  upDescriptionNumber = function(upDescription) {
@@ -5052,7 +5223,7 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
5052
5223
  @event up:framework:reset
5053
5224
  @experimental
5054
5225
  */
5055
- renamedEvent = function(oldEvent, newEvent) {
5226
+ deprecateRenamedEvent = function(oldEvent, newEvent) {
5056
5227
  return renamedEvents[oldEvent] = newEvent;
5057
5228
  };
5058
5229
 
@@ -5089,39 +5260,658 @@ This improves jQuery's [`on`](http://api.jquery.com/on/) in multiple ways:
5089
5260
  return typeof console.log === "function" ? console.log("Unpoly doesn't support this browser. Framework was not booted.") : void 0;
5090
5261
  }
5091
5262
  };
5092
-
5093
- /***
5094
- This event is [emitted](/up.emit) when Unpoly [starts to boot](/up.boot).
5095
-
5096
- @event up:framework:boot
5097
- @internal
5098
- */
5099
- live('up:framework:booted', snapshot);
5100
- live('up:framework:reset', resetBus);
5263
+
5264
+ /***
5265
+ This event is [emitted](/up.emit) when Unpoly [starts to boot](/up.boot).
5266
+
5267
+ @event up:framework:boot
5268
+ @internal
5269
+ */
5270
+ live('up:framework:booted', snapshot);
5271
+ live('up:framework:reset', resetBus);
5272
+ return {
5273
+ on: live,
5274
+ off: unbind,
5275
+ emit: emit,
5276
+ nobodyPrevents: nobodyPrevents,
5277
+ whenEmitted: whenEmitted,
5278
+ onEscape: onEscape,
5279
+ emitReset: emitReset,
5280
+ haltEvent: haltEvent,
5281
+ consumeAction: consumeAction,
5282
+ deprecateRenamedEvent: deprecateRenamedEvent,
5283
+ boot: boot
5284
+ };
5285
+ })(jQuery);
5286
+
5287
+ up.on = up.bus.on;
5288
+
5289
+ up.off = up.bus.off;
5290
+
5291
+ up.emit = up.bus.emit;
5292
+
5293
+ up.reset = up.bus.emitReset;
5294
+
5295
+ up.boot = up.bus.boot;
5296
+
5297
+ }).call(this);
5298
+
5299
+ /***
5300
+ Request parameters
5301
+ ==================
5302
+
5303
+ Methods like [`up.replace()`](/up.replace) accept request parameters (or form data values) as a `{ params }` option.
5304
+
5305
+ This module offers a consistent API to read and manipulate request parameters independent of their type.
5306
+
5307
+
5308
+ \#\#\# Supported parameter types
5309
+
5310
+ The following types of parameters are supported:
5311
+
5312
+ 1. an object like `{ email: 'foo@bar.com' }`
5313
+ 2. a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
5314
+ 3. a query string like `email=foo%40bar.com`
5315
+ 4. an array of `{ name, value }` objects like `[{ name: 'email', value: 'foo@bar.com' }]`
5316
+
5317
+ @class up.params
5318
+ */
5319
+
5320
+ (function() {
5321
+ up.params = (function($) {
5322
+ var NATURE_ARRAY, NATURE_FORM_DATA, NATURE_MISSING, NATURE_OBJECT, NATURE_QUERY, add, arrayEntryToQuery, assign, buildArrayFromFormData, buildArrayFromObject, buildArrayFromQuery, buildFormDataFromArray, buildObjectFromArray, buildQueryFromArray, buildURL, fromField, fromForm, fromURL, get, isPrimitiveValue, merge, natureOf, safeGet, safeSet, submittingButton, toArray, toFormData, toObject, toQuery, u;
5323
+ u = up.util;
5324
+ NATURE_MISSING = 0;
5325
+ NATURE_ARRAY = 1;
5326
+ NATURE_QUERY = 2;
5327
+ NATURE_FORM_DATA = 3;
5328
+ NATURE_OBJECT = 4;
5329
+ natureOf = function(params) {
5330
+ if (u.isMissing(params)) {
5331
+ return NATURE_MISSING;
5332
+ } else if (u.isArray(params)) {
5333
+ return NATURE_ARRAY;
5334
+ } else if (u.isString(params)) {
5335
+ return NATURE_QUERY;
5336
+ } else if (u.isFormData(params)) {
5337
+ return NATURE_FORM_DATA;
5338
+ } else if (u.isObject(params)) {
5339
+ return NATURE_OBJECT;
5340
+ } else {
5341
+ return up.fail("Unsupport params type: %o", params);
5342
+ }
5343
+ };
5344
+
5345
+ /***
5346
+ Returns an array representation of the given `params`.
5347
+
5348
+ The given params value may be of any [supported type](/up.params).
5349
+
5350
+ Each element in the returned array is an object with `{ name }` and `{ value }` properties.
5351
+
5352
+ \#\#\# Example
5353
+
5354
+ var array = up.params.toArray('foo=bar&baz=bam')
5355
+
5356
+ // array is now: [
5357
+ // { name: 'foo', value: 'bar' },
5358
+ // { name: 'baz', value: 'bam' },
5359
+ // ]
5360
+
5361
+ @function up.params.toArray
5362
+ @param {Object|FormData|string|Array|undefined} params
5363
+ the params to convert
5364
+ @return {Array}
5365
+ an array representation of the given params
5366
+ @experimental
5367
+ */
5368
+ toArray = function(params) {
5369
+ switch (natureOf(params)) {
5370
+ case NATURE_MISSING:
5371
+ return [];
5372
+ case NATURE_ARRAY:
5373
+ return params;
5374
+ case NATURE_QUERY:
5375
+ return buildArrayFromQuery(params);
5376
+ case NATURE_FORM_DATA:
5377
+ return buildArrayFromFormData(params);
5378
+ case NATURE_OBJECT:
5379
+ return buildArrayFromObject(params);
5380
+ }
5381
+ };
5382
+
5383
+ /***
5384
+ Returns an object representation of the given `params`.
5385
+
5386
+ The given params value may be of any [supported type](/up.params).
5387
+
5388
+ The returned value is a simple JavaScript object whose properties correspond
5389
+ to the key/values in the given `params`.
5390
+
5391
+ \#\#\# Example
5392
+
5393
+ var object = up.params.toObject('foo=bar&baz=bam')
5394
+
5395
+ // object is now: {
5396
+ // foo: 'bar',
5397
+ // baz: 'bam'
5398
+ // ]
5399
+
5400
+ @function up.params.toObject
5401
+ @param {Object|FormData|string|Array|undefined} params
5402
+ the params to convert
5403
+ @return {Array}
5404
+ an object representation of the given params
5405
+ @experimental
5406
+ */
5407
+ toObject = function(params) {
5408
+ switch (natureOf(params)) {
5409
+ case NATURE_MISSING:
5410
+ return {};
5411
+ case NATURE_ARRAY:
5412
+ case NATURE_QUERY:
5413
+ case NATURE_FORM_DATA:
5414
+ return buildObjectFromArray(toArray(params));
5415
+ case NATURE_OBJECT:
5416
+ return params;
5417
+ }
5418
+ };
5419
+
5420
+ /***
5421
+ Returns [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) representation of the given `params`.
5422
+
5423
+ The given params value may be of any [supported type](/up.params).
5424
+
5425
+ \#\#\# Example
5426
+
5427
+ var formData = up.params.toFormData('foo=bar&baz=bam')
5428
+
5429
+ formData.get('foo') // 'bar'
5430
+ formData.get('baz') // 'bam'
5431
+
5432
+ @function up.params.toFormData
5433
+ @param {Object|FormData|string|Array|undefined} params
5434
+ the params to convert
5435
+ @return {FormData}
5436
+ a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) representation of the given params
5437
+ @experimental
5438
+ */
5439
+ toFormData = function(params) {
5440
+ switch (natureOf(params)) {
5441
+ case NATURE_MISSING:
5442
+ return new FormData();
5443
+ case NATURE_ARRAY:
5444
+ case NATURE_QUERY:
5445
+ case NATURE_OBJECT:
5446
+ return buildFormDataFromArray(toArray(params));
5447
+ case NATURE_FORM_DATA:
5448
+ return params;
5449
+ }
5450
+ };
5451
+
5452
+ /***
5453
+ Returns an query string for the given `params`.
5454
+
5455
+ The given params value may be of any [supported type](/up.params).
5456
+
5457
+ The keys and values in the returned query string will be [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
5458
+ Non-primitive values (like [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) will be omitted from
5459
+ the retuned query string.
5460
+
5461
+ \#\#\# Example
5462
+
5463
+ var query = up.params.toQuery({ foo: 'bar', baz: 'bam' })
5464
+
5465
+ // query is now: 'foo=bar&baz=bam'
5466
+
5467
+ @function up.params.toQuery
5468
+ @param {Object|FormData|string|Array|undefined} params
5469
+ the params to convert
5470
+ @return {string}
5471
+ a query string built from the given params
5472
+ @experimental
5473
+ */
5474
+ toQuery = function(params) {
5475
+ switch (natureOf(params)) {
5476
+ case NATURE_MISSING:
5477
+ return '';
5478
+ case NATURE_QUERY:
5479
+ return params;
5480
+ case NATURE_ARRAY:
5481
+ case NATURE_FORM_DATA:
5482
+ case NATURE_OBJECT:
5483
+ return buildQueryFromArray(toArray(params));
5484
+ }
5485
+ };
5486
+ arrayEntryToQuery = function(entry) {
5487
+ var query, value;
5488
+ value = entry.value;
5489
+ if (!isPrimitiveValue(value)) {
5490
+ return void 0;
5491
+ }
5492
+ query = encodeURIComponent(entry.name);
5493
+ if (u.isGiven(value)) {
5494
+ query += "=";
5495
+ query += encodeURIComponent(value);
5496
+ }
5497
+ return query;
5498
+ };
5499
+
5500
+ /***
5501
+ Returns whether the given value can be encoded into a query string.
5502
+
5503
+ We will have `File` values in our params when we serialize a form with a file input.
5504
+ These entries will be filtered out when converting to a query string.
5505
+ */
5506
+ isPrimitiveValue = function(value) {
5507
+ return u.isMissing(value) || u.isString(value) || u.isNumber(value) || u.isBoolean(value);
5508
+ };
5509
+ safeSet = function(obj, k, value) {
5510
+ if (!u.isBasicObjectProperty(k)) {
5511
+ return obj[k] = value;
5512
+ }
5513
+ };
5514
+ safeGet = function(obj, k) {
5515
+ if (!u.isBasicObjectProperty(k)) {
5516
+ return obj[k];
5517
+ }
5518
+ };
5519
+ buildQueryFromArray = function(array) {
5520
+ var parts;
5521
+ parts = u.map(array, arrayEntryToQuery);
5522
+ parts = u.compact(parts);
5523
+ return parts.join('&');
5524
+ };
5525
+ buildArrayFromQuery = function(query) {
5526
+ var array, i, len, name, part, ref, ref1, value;
5527
+ array = [];
5528
+ ref = query.split('&');
5529
+ for (i = 0, len = ref.length; i < len; i++) {
5530
+ part = ref[i];
5531
+ if (part) {
5532
+ ref1 = part.split('='), name = ref1[0], value = ref1[1];
5533
+ name = decodeURIComponent(name);
5534
+ if (u.isGiven(value)) {
5535
+ value = decodeURIComponent(value);
5536
+ } else {
5537
+ value = null;
5538
+ }
5539
+ array.push({
5540
+ name: name,
5541
+ value: value
5542
+ });
5543
+ }
5544
+ }
5545
+ return array;
5546
+ };
5547
+ buildArrayFromObject = function(object) {
5548
+ var array, k, v;
5549
+ array = [];
5550
+ for (k in object) {
5551
+ v = object[k];
5552
+ array.push({
5553
+ name: k,
5554
+ value: v
5555
+ });
5556
+ }
5557
+ return array;
5558
+ };
5559
+ buildObjectFromArray = function(array) {
5560
+ var entry, i, len, obj;
5561
+ obj = {};
5562
+ for (i = 0, len = array.length; i < len; i++) {
5563
+ entry = array[i];
5564
+ safeSet(obj, entry.name, entry.value);
5565
+ }
5566
+ return obj;
5567
+ };
5568
+ buildArrayFromFormData = function(formData) {
5569
+ var array;
5570
+ array = [];
5571
+ u.eachIterator(formData.entries(), function(value) {
5572
+ var name, ref;
5573
+ ref = value, name = ref[0], value = ref[1];
5574
+ return array.push({
5575
+ name: name,
5576
+ value: value
5577
+ });
5578
+ });
5579
+ return array;
5580
+ };
5581
+ buildFormDataFromArray = function(array) {
5582
+ var entry, formData, i, len;
5583
+ formData = new FormData();
5584
+ for (i = 0, len = array.length; i < len; i++) {
5585
+ entry = array[i];
5586
+ formData.append(entry.name, entry.value);
5587
+ }
5588
+ return formData;
5589
+ };
5590
+ buildURL = function(base, params) {
5591
+ var parts, separator;
5592
+ parts = [base, toQuery(params)];
5593
+ parts = u.select(parts, u.isPresent);
5594
+ separator = u.contains(base, '?') ? '&' : '?';
5595
+ return parts.join(separator);
5596
+ };
5597
+
5598
+ /***
5599
+ Adds to the given `params` a new entry with the given `name` and `value`.
5600
+
5601
+ The given params value may be of any [supported type](/up.params).
5602
+
5603
+ The given `params` value is changed in-place, if possible. Some types, such as query strings,
5604
+ cannot be changed in-place. The return value is always a params value that includes the new entry.
5605
+
5606
+ \#\#\# Example
5607
+
5608
+ var obj = { foo: 'bar' }
5609
+ up.params.add(obj, 'baz', 'bam')
5610
+ // obj is now: { foo: 'bar', baz: 'bam' }
5611
+
5612
+ @function up.params.add
5613
+ @param {string|object|FormData|Array|undefined} params
5614
+ @param {string} name
5615
+ @param {any} value
5616
+ @return {string|object|FormData|Array}
5617
+ @experimental
5618
+ */
5619
+ add = function(params, name, value) {
5620
+ var newEntry;
5621
+ newEntry = [
5622
+ {
5623
+ name: name,
5624
+ value: value
5625
+ }
5626
+ ];
5627
+ return assign(params, newEntry);
5628
+ };
5629
+
5630
+ /***
5631
+ Returns a new params value that contains entries from both `params` and `otherParams`.
5632
+
5633
+ The given params value may be of any [supported type](/up.params).
5634
+
5635
+ This function creates a new params value. The given `params` argument is not changed.
5636
+
5637
+ @function up.params.merge
5638
+ @param {string|object|FormData|Array|undefined} params
5639
+ @param {string|object|FormData|Array|undefined} otherParams
5640
+ @return {string|object|FormData|Array}
5641
+ @experimental
5642
+ */
5643
+ merge = function(params, otherParams) {
5644
+ var formData, otherArray, otherQuery, parts;
5645
+ switch (natureOf(params)) {
5646
+ case NATURE_MISSING:
5647
+ return merge({}, otherParams);
5648
+ case NATURE_ARRAY:
5649
+ otherArray = toArray(otherParams);
5650
+ return params.concat(otherArray);
5651
+ case NATURE_FORM_DATA:
5652
+ formData = new FormData();
5653
+ assign(formData, params);
5654
+ assign(formData, otherParams);
5655
+ return formData;
5656
+ case NATURE_QUERY:
5657
+ otherQuery = toQuery(otherParams);
5658
+ parts = u.select([params, otherQuery], u.isPresent);
5659
+ return parts.join('&');
5660
+ case NATURE_OBJECT:
5661
+ return u.merge(params, toObject(otherParams));
5662
+ }
5663
+ };
5664
+
5665
+ /***
5666
+ Returns the first param value with the given `name` from the given `params`.
5667
+
5668
+ The given params value may be of any [supported type](/up.params).
5669
+
5670
+ \#\#\# Example
5671
+
5672
+ var array = [
5673
+ { name: 'foo', value: 'bar' },
5674
+ { name: 'baz', value: 'bam' }
5675
+ }
5676
+
5677
+ value = up.params.get(array, 'baz')
5678
+ // value is now: 'bam'
5679
+
5680
+ @function up.params.get
5681
+ @experimental
5682
+ */
5683
+ get = function(params, name) {
5684
+ var entry, value;
5685
+ switch (natureOf(params)) {
5686
+ case NATURE_MISSING:
5687
+ return void 0;
5688
+ case NATURE_ARRAY:
5689
+ entry = u.detect(params, function(entry) {
5690
+ return entry.name === name;
5691
+ });
5692
+ return entry != null ? entry.value : void 0;
5693
+ case NATURE_FORM_DATA:
5694
+ value = params.get(name);
5695
+ if (u.isNull(value)) {
5696
+ value = void 0;
5697
+ }
5698
+ return value;
5699
+ case NATURE_QUERY:
5700
+ return safeGet(toObject(params), name);
5701
+ case NATURE_OBJECT:
5702
+ return safeGet(params, name);
5703
+ }
5704
+ };
5705
+
5706
+ /***
5707
+ Extends the given `params` with entries from the given `otherParams`.
5708
+
5709
+ The given params value may be of any [supported type](/up.params).
5710
+
5711
+ The given `params` is changed in-place, if possible. Some types, such as query strings,
5712
+ cannot be changed in-place. The return value is always a params value that includes the new entries.
5713
+
5714
+ @function up.params.assign
5715
+ @param {string|object|FormData|Array|undefined} params
5716
+ @param {string|object|FormData|Array|undefined} otherParams
5717
+ @return {string|object|FormData|Array}
5718
+ @experimental
5719
+ */
5720
+ assign = function(params, otherParams) {
5721
+ var entry, i, len, otherArray;
5722
+ switch (natureOf(params)) {
5723
+ case NATURE_ARRAY:
5724
+ otherArray = toArray(otherParams);
5725
+ params.push.apply(params, otherArray);
5726
+ return params;
5727
+ case NATURE_FORM_DATA:
5728
+ otherArray = toArray(otherParams);
5729
+ for (i = 0, len = otherArray.length; i < len; i++) {
5730
+ entry = otherArray[i];
5731
+ params.append(entry.name, entry.value);
5732
+ }
5733
+ return params;
5734
+ case NATURE_OBJECT:
5735
+ return u.assign(params, toObject(otherParams));
5736
+ case NATURE_QUERY:
5737
+ case NATURE_MISSING:
5738
+ return merge(params, otherParams);
5739
+ }
5740
+ };
5741
+ submittingButton = function(form) {
5742
+ var $activeElement, $form, submitButtonSelector;
5743
+ $form = $(form);
5744
+ submitButtonSelector = up.form.submitButtonSelector();
5745
+ $activeElement = $(document.activeElement);
5746
+ if ($activeElement.is(submitButtonSelector) && $form.has($activeElement)) {
5747
+ return $activeElement[0];
5748
+ } else {
5749
+ return $form.find(submitButtonSelector)[0];
5750
+ }
5751
+ };
5752
+
5753
+ /***
5754
+ Serializes request params from the given `<form>`.
5755
+
5756
+ The returned params may be passed as `{ params }` option to
5757
+ [`up.request()`](/up.request) or [`up.replace()`](/up.replace).
5758
+
5759
+ The serialized params will include the form's submit button, if that
5760
+ button as a `name` attribute.
5761
+
5762
+ \#\#\#\# Example
5763
+
5764
+ Given this HTML form:
5765
+
5766
+ <form>
5767
+ <input type="text" name="name" value="Foo Bar">
5768
+ <input type="text" name="email" value="foo@bar.com">
5769
+ </form>
5770
+
5771
+ This would serialize the form into an array representation:
5772
+
5773
+ var params = up.params.fromForm('input[name=email]')
5774
+
5775
+ // params is now: [
5776
+ // { name: 'name', value: 'Foo Bar' },
5777
+ // { name: 'email', value: 'foo@bar.com' }
5778
+ // ]
5779
+
5780
+ @function up.params.fromForm
5781
+ @param {Element|jQuery|string} form
5782
+ @return {Array}
5783
+ @experimental
5784
+ */
5785
+ fromForm = function(form) {
5786
+ var button, fields;
5787
+ if (form = u.element(form)) {
5788
+ fields = form.querySelectorAll(up.form.fieldSelector());
5789
+ if (button = submittingButton(form)) {
5790
+ fields = u.toArray(fields);
5791
+ fields.push(button);
5792
+ }
5793
+ return u.flatMap(fields, fromField);
5794
+ }
5795
+ };
5796
+
5797
+ /***
5798
+ Serializes request params from a single [input field](/up.form.config#config.fields).
5799
+ To serialize an entire form, use [`up.params.fromForm()`](/up.params.fromForm).
5800
+
5801
+ Note that some fields may produce multiple params, such as `<select multiple>`.
5802
+
5803
+ \#\#\#\# Example
5804
+
5805
+ Given this HTML form:
5806
+
5807
+ <form>
5808
+ <input type="text" name="email" value="foo@bar.com">
5809
+ <input type="password" name="password">
5810
+ </form>
5811
+
5812
+ This would retrieve request parameters from the `email` field:
5813
+
5814
+ var params = up.params.fromField('input[name=email]')
5815
+
5816
+ // params is now: [{ name: 'email', value: 'foo@bar.com' }]
5817
+
5818
+ @function up.params.fromField
5819
+ @param {Element|jQuery|string} form
5820
+ @return {Array}
5821
+ an array of `{ name, value }` objects
5822
+ @experimental
5823
+ */
5824
+ fromField = function(field) {
5825
+ var file, i, j, len, len1, name, option, params, ref, ref1, tagName, type;
5826
+ params = [];
5827
+ if ((field = u.element(field)) && (name = field.name) && (!field.disabled)) {
5828
+ tagName = field.tagName;
5829
+ type = field.type;
5830
+ if (tagName === 'SELECT') {
5831
+ ref = field.querySelectorAll('option');
5832
+ for (i = 0, len = ref.length; i < len; i++) {
5833
+ option = ref[i];
5834
+ if (option.selected) {
5835
+ params.push({
5836
+ name: name,
5837
+ value: option.value
5838
+ });
5839
+ }
5840
+ }
5841
+ } else if (type === 'checkbox' || type === 'radio') {
5842
+ if (field.checked) {
5843
+ params.push({
5844
+ name: name,
5845
+ value: field.value
5846
+ });
5847
+ }
5848
+ } else if (type === 'file') {
5849
+ ref1 = field.files;
5850
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
5851
+ file = ref1[j];
5852
+ params.push({
5853
+ name: name,
5854
+ value: file
5855
+ });
5856
+ }
5857
+ } else {
5858
+ params.push({
5859
+ name: name,
5860
+ value: field.value
5861
+ });
5862
+ }
5863
+ }
5864
+ return params;
5865
+ };
5866
+
5867
+ /***
5868
+ Returns the [query string](https://en.wikipedia.org/wiki/Query_string) from the given URL.
5869
+
5870
+ The query string is returned **without** a leading question mark (`?`).
5871
+ Returns `undefined` if the given URL has no query component.
5872
+
5873
+ You can access individual values from the returned query string using functions like
5874
+ [`up.params.get()`](/up.params.get) or [`up.params.toObject()`](/up.params.toObject).
5875
+
5876
+ \#\#\# Example
5877
+
5878
+ var query = up.params.fromURL('http://foo.com?bar=baz')
5879
+
5880
+ // query is now: 'bar=baz'
5881
+
5882
+ @function up.params.fromURL
5883
+ @param {string} url
5884
+ The URL from which to extract the query string.
5885
+ @return {string|undefined}
5886
+ The given URL's query string, or `undefined` if the URL has no query component.
5887
+ @experimental
5888
+ */
5889
+ fromURL = function(url) {
5890
+ var query, urlParts;
5891
+ urlParts = u.parseUrl(url);
5892
+ if (query = urlParts.search) {
5893
+ query = query.replace(/^\?/, '');
5894
+ return query;
5895
+ }
5896
+ };
5101
5897
  return {
5102
- on: live,
5103
- off: unbind,
5104
- emit: emit,
5105
- nobodyPrevents: nobodyPrevents,
5106
- whenEmitted: whenEmitted,
5107
- onEscape: onEscape,
5108
- emitReset: emitReset,
5109
- haltEvent: haltEvent,
5110
- consumeAction: consumeAction,
5111
- renamedEvent: renamedEvent,
5112
- boot: boot
5898
+ toArray: toArray,
5899
+ toObject: toObject,
5900
+ toQuery: toQuery,
5901
+ toFormData: toFormData,
5902
+ buildURL: buildURL,
5903
+ get: get,
5904
+ add: add,
5905
+ assign: assign,
5906
+ merge: merge,
5907
+ fromForm: fromForm,
5908
+ fromURL: fromURL
5113
5909
  };
5114
5910
  })(jQuery);
5115
5911
 
5116
- up.on = up.bus.on;
5117
-
5118
- up.off = up.bus.off;
5119
-
5120
- up.emit = up.bus.emit;
5121
-
5122
- up.reset = up.bus.emitReset;
5912
+ }).call(this);
5913
+ (function() {
5123
5914
 
5124
- up.boot = up.bus.boot;
5125
5915
 
5126
5916
  }).call(this);
5127
5917
 
@@ -5455,10 +6245,10 @@ The output can be configured using the [`up.log.config`](/up.log.config) propert
5455
6245
  var slice = [].slice;
5456
6246
 
5457
6247
  up.log = (function($) {
5458
- var SESSION_KEY_ENABLED, b, config, debug, disable, enable, error, group, prefix, printBanner, puts, reset, setEnabled, u, warn;
6248
+ var b, config, debug, disable, enable, error, group, prefix, printBanner, puts, reset, sessionStore, setEnabled, u, warn;
5459
6249
  u = up.util;
5460
6250
  b = up.browser;
5461
- SESSION_KEY_ENABLED = 'up.log.enabled';
6251
+ sessionStore = new up.store.Session('up.log');
5462
6252
 
5463
6253
  /***
5464
6254
  Configures the logging output on the developer console.
@@ -5481,7 +6271,7 @@ The output can be configured using the [`up.log.config`](/up.log.config) propert
5481
6271
  */
5482
6272
  config = u.config({
5483
6273
  prefix: '[UP] ',
5484
- enabled: b.sessionStorage().getItem(SESSION_KEY_ENABLED) === 'true',
6274
+ enabled: sessionStore.get('enabled'),
5485
6275
  collapse: false
5486
6276
  });
5487
6277
  reset = function() {
@@ -5524,7 +6314,7 @@ The output can be configured using the [`up.log.config`](/up.log.config) propert
5524
6314
  };
5525
6315
 
5526
6316
  /***
5527
- @function up.log.warn
6317
+ @function up.warn
5528
6318
  @internal
5529
6319
  */
5530
6320
  warn = function() {
@@ -5585,7 +6375,7 @@ The output can be configured using the [`up.log.config`](/up.log.config) propert
5585
6375
  up.on('up:framework:boot', printBanner);
5586
6376
  up.on('up:framework:reset', reset);
5587
6377
  setEnabled = function(value) {
5588
- b.sessionStorage().setItem(SESSION_KEY_ENABLED, value.toString());
6378
+ sessionStore.set('enabled', value);
5589
6379
  return config.enabled = value;
5590
6380
  };
5591
6381
 
@@ -5627,6 +6417,8 @@ The output can be configured using the [`up.log.config`](/up.log.config) propert
5627
6417
 
5628
6418
  up.puts = up.log.puts;
5629
6419
 
6420
+ up.warn = up.log.warn;
6421
+
5630
6422
  }).call(this);
5631
6423
 
5632
6424
  /***
@@ -5731,10 +6523,8 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
5731
6523
  var slice = [].slice;
5732
6524
 
5733
6525
  up.syntax = (function($) {
5734
- var DESTRUCTIBLE_CLASS, DESTRUCTORS_KEY, SYSTEM_MACRO_PRIORITIES, addDestructor, applyCompiler, buildCompiler, clean, compile, compiler, compilers, data, detectSystemMacroPriority, insertCompiler, isBooting, macro, macros, normalizeDestructor, removeDestructors, reset, u;
6526
+ var SYSTEM_MACRO_PRIORITIES, buildCompiler, clean, compile, compilers, detectSystemMacroPriority, insertCompiler, isBooting, macros, readData, registerCompiler, registerDestructor, registerMacro, reset, u;
5735
6527
  u = up.util;
5736
- DESTRUCTIBLE_CLASS = 'up-destructible';
5737
- DESTRUCTORS_KEY = 'up-destructors';
5738
6528
  SYSTEM_MACRO_PRIORITIES = {
5739
6529
  '[up-back]': -100,
5740
6530
  '[up-drawer]': -200,
@@ -5921,19 +6711,13 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
5921
6711
  such as timeouts and event handlers bound to the document.
5922
6712
  The destructor is *not* expected to remove the element from the DOM, which
5923
6713
  is already handled by [`up.destroy()`](/up.destroy).
5924
-
5925
- The function may also return an array of destructor functions.
5926
6714
  @stable
5927
6715
  */
5928
- compiler = function() {
5929
- var args, callback, options, selector;
5930
- selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
5931
- if (!up.browser.isSupported()) {
5932
- return;
5933
- }
5934
- callback = args.pop();
5935
- options = u.options(args[0]);
5936
- return insertCompiler(compilers, selector, options, callback);
6716
+ registerCompiler = function() {
6717
+ var args, compiler;
6718
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
6719
+ compiler = buildCompiler.apply(null, args);
6720
+ return insertCompiler(compilers, compiler);
5937
6721
  };
5938
6722
 
5939
6723
  /***
@@ -5977,90 +6761,46 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
5977
6761
  See [`up.compiler()`](/up.compiler) for details.
5978
6762
  @stable
5979
6763
  */
5980
- macro = function() {
5981
- var args, callback, options, selector;
5982
- selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
5983
- if (!up.browser.isSupported()) {
5984
- return;
5985
- }
5986
- callback = args.pop();
5987
- options = u.options(args[0]);
6764
+ registerMacro = function() {
6765
+ var args, macro;
6766
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
6767
+ macro = buildCompiler.apply(null, args);
5988
6768
  if (isBooting) {
5989
- options.priority = detectSystemMacroPriority(selector) || up.fail('Unregistered priority for system macro %o', selector);
6769
+ macro.priority = detectSystemMacroPriority(macro.selector) || up.fail('Unregistered priority for system macro %o', macro.selector);
5990
6770
  }
5991
- return insertCompiler(macros, selector, options, callback);
6771
+ return insertCompiler(macros, macro);
5992
6772
  };
5993
- detectSystemMacroPriority = function(fullMacroSelector) {
6773
+ detectSystemMacroPriority = function(macroSelector) {
5994
6774
  var priority, substr;
5995
6775
  for (substr in SYSTEM_MACRO_PRIORITIES) {
5996
6776
  priority = SYSTEM_MACRO_PRIORITIES[substr];
5997
- if (fullMacroSelector.indexOf(substr) >= 0) {
6777
+ if (macroSelector.indexOf(substr) >= 0) {
5998
6778
  return priority;
5999
6779
  }
6000
6780
  }
6001
6781
  };
6002
- buildCompiler = function(selector, options, callback) {
6003
- return {
6782
+ buildCompiler = function() {
6783
+ var args, callback, options, selector;
6784
+ selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
6785
+ callback = args.pop();
6786
+ options = u.extractOptions(args);
6787
+ options = u.options(options, {
6004
6788
  selector: selector,
6005
- callback: callback,
6006
6789
  isSystem: isBooting,
6007
- priority: options.priority || 0,
6008
- batch: options.batch,
6009
- keep: options.keep
6010
- };
6790
+ priority: 0,
6791
+ batch: false,
6792
+ keep: false
6793
+ });
6794
+ return u.assign(callback, options);
6011
6795
  };
6012
- insertCompiler = function(queue, selector, options, callback) {
6013
- var index, newCompiler, oldCompiler;
6014
- if (!up.browser.isSupported()) {
6015
- return;
6016
- }
6017
- newCompiler = buildCompiler(selector, options, callback);
6796
+ insertCompiler = function(queue, newCompiler) {
6797
+ var existingCompiler, index;
6018
6798
  index = 0;
6019
- while ((oldCompiler = queue[index]) && (oldCompiler.priority >= newCompiler.priority)) {
6799
+ while ((existingCompiler = queue[index]) && (existingCompiler.priority >= newCompiler.priority)) {
6020
6800
  index += 1;
6021
6801
  }
6022
6802
  return queue.splice(index, 0, newCompiler);
6023
6803
  };
6024
- applyCompiler = function(compiler, $jqueryElement, nativeElement) {
6025
- var returnValue, value;
6026
- up.puts((!compiler.isSystem ? "Compiling '%s' on %o" : void 0), compiler.selector, nativeElement);
6027
- if (compiler.keep) {
6028
- value = u.isString(compiler.keep) ? compiler.keep : '';
6029
- $jqueryElement.attr('up-keep', value);
6030
- }
6031
- returnValue = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)]);
6032
- return addDestructor($jqueryElement, returnValue);
6033
- };
6034
-
6035
- /***
6036
- Tries to find a list of destructors in a compiler's return value.
6037
-
6038
- @param {Object} returnValue
6039
- @return {Function|undefined}
6040
- @internal
6041
- */
6042
- normalizeDestructor = function(returnValue) {
6043
- if (u.isFunction(returnValue)) {
6044
- return returnValue;
6045
- } else if (u.isArray(returnValue) && u.all(returnValue, u.isFunction)) {
6046
- return u.sequence.apply(u, returnValue);
6047
- }
6048
- };
6049
- addDestructor = function($element, newDestructor) {
6050
- var elementDestructor;
6051
- if (newDestructor = normalizeDestructor(newDestructor)) {
6052
- $element.addClass(DESTRUCTIBLE_CLASS);
6053
- elementDestructor = $element.data(DESTRUCTORS_KEY) || function() {
6054
- return removeDestructors($element);
6055
- };
6056
- elementDestructor = u.sequence(elementDestructor, newDestructor);
6057
- return $element.data(DESTRUCTORS_KEY, elementDestructor);
6058
- }
6059
- };
6060
- removeDestructors = function($element) {
6061
- $element.removeData(DESTRUCTORS_KEY);
6062
- return $element.removeClass(DESTRUCTIBLE_CLASS);
6063
- };
6064
6804
 
6065
6805
  /***
6066
6806
  Applies all compilers on the given element and its descendants.
@@ -6072,49 +6812,25 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6072
6812
  @internal
6073
6813
  */
6074
6814
  compile = function($fragment, options) {
6075
- var $skipSubtrees;
6076
- options = u.options(options);
6077
- $skipSubtrees = $(options.skip);
6078
- return up.log.group("Compiling fragment %o", $fragment.get(0), function() {
6079
- var $matches, i, len, queue, ref, results;
6080
- ref = [macros, compilers];
6081
- results = [];
6082
- for (i = 0, len = ref.length; i < len; i++) {
6083
- queue = ref[i];
6084
- results.push((function() {
6085
- var j, len1, results1;
6086
- results1 = [];
6087
- for (j = 0, len1 = queue.length; j < len1; j++) {
6088
- compiler = queue[j];
6089
- $matches = u.selectInSubtree($fragment, compiler.selector);
6090
- if ($skipSubtrees.length) {
6091
- $matches = $matches.filter(function() {
6092
- var $match;
6093
- $match = $(this);
6094
- return u.all($skipSubtrees, function(skipSubtree) {
6095
- return $match.closest(skipSubtree).length === 0;
6096
- });
6097
- });
6098
- }
6099
- if ($matches.length) {
6100
- results1.push(up.log.group((!compiler.isSystem ? "Compiling '%s' on %d element(s)" : void 0), compiler.selector, $matches.length, function() {
6101
- if (compiler.batch) {
6102
- return applyCompiler(compiler, $matches, $matches.get());
6103
- } else {
6104
- return $matches.each(function() {
6105
- return applyCompiler(compiler, $(this), this);
6106
- });
6107
- }
6108
- }));
6109
- } else {
6110
- results1.push(void 0);
6111
- }
6112
- }
6113
- return results1;
6114
- })());
6115
- }
6116
- return results;
6117
- });
6815
+ var compileRun, orderedCompilers;
6816
+ orderedCompilers = macros.concat(compilers);
6817
+ compileRun = new up.CompilePass($fragment, orderedCompilers, options);
6818
+ return compileRun.compile();
6819
+ };
6820
+
6821
+ /***
6822
+ @function up.syntax.destructor
6823
+ @internal
6824
+ */
6825
+ registerDestructor = function(element, destructor) {
6826
+ var destructors;
6827
+ element = u.element(element);
6828
+ if (!(destructors = element.upDestructors)) {
6829
+ destructors = [];
6830
+ element.upDestructors = destructors;
6831
+ element.classList.add('up-can-clean');
6832
+ }
6833
+ return destructors.push(destructor);
6118
6834
  };
6119
6835
 
6120
6836
  /***
@@ -6126,12 +6842,17 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6126
6842
  @internal
6127
6843
  */
6128
6844
  clean = function($fragment) {
6129
- var $destructibles;
6130
- $destructibles = u.selectInSubtree($fragment, "." + DESTRUCTIBLE_CLASS);
6131
- return u.each($destructibles, function(destructible) {
6132
- var destructor;
6133
- if (destructor = $(destructible).data(DESTRUCTORS_KEY)) {
6134
- return destructor();
6845
+ var cleanables;
6846
+ cleanables = u.selectInSubtree($fragment, '.up-can-clean');
6847
+ return u.each(cleanables, function(cleanable) {
6848
+ var destructor, destructors, i, len, results;
6849
+ if (destructors = cleanable.upDestructors) {
6850
+ results = [];
6851
+ for (i = 0, len = destructors.length; i < len; i++) {
6852
+ destructor = destructors[i];
6853
+ results.push(destructor());
6854
+ }
6855
+ return results;
6135
6856
  }
6136
6857
  });
6137
6858
  };
@@ -6140,7 +6861,7 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6140
6861
  Checks if the given element has an [`up-data`](/up-data) attribute.
6141
6862
  If yes, parses the attribute value as JSON and returns the parsed object.
6142
6863
 
6143
- Returns an empty object if the element has no `up-data` attribute.
6864
+ Returns `undefined` if the element has no `up-data` attribute.
6144
6865
 
6145
6866
  \#\#\# Example
6146
6867
 
@@ -6157,8 +6878,8 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6157
6878
  @return
6158
6879
  The JSON-decoded value of the `up-data` attribute.
6159
6880
 
6160
- Returns an empty object (`{}`) if the element has no (or an empty) `up-data` attribute.
6161
- @stable
6881
+ Returns `undefined` if the element has no (or an empty) `up-data` attribute.
6882
+ @experimental
6162
6883
  */
6163
6884
 
6164
6885
  /***
@@ -6204,14 +6925,10 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6204
6925
  A serialized JSON string
6205
6926
  @stable
6206
6927
  */
6207
- data = function(elementOrSelector) {
6208
- var $element, json;
6209
- $element = $(elementOrSelector);
6210
- json = $element.attr('up-data');
6211
- if (u.isString(json) && u.trim(json) !== '') {
6212
- return JSON.parse(json);
6213
- } else {
6214
- return {};
6928
+ readData = function(elementOrSelector) {
6929
+ var element;
6930
+ if (element = u.element(elementOrSelector)) {
6931
+ return u.jsonAttr(element, 'up-data') || {};
6215
6932
  }
6216
6933
  };
6217
6934
 
@@ -6222,28 +6939,27 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6222
6939
  @internal
6223
6940
  */
6224
6941
  reset = function() {
6225
- var isSystem;
6226
- isSystem = function(compiler) {
6227
- return compiler.isSystem;
6228
- };
6229
- compilers = u.select(compilers, isSystem);
6230
- return macros = u.select(macros, isSystem);
6942
+ compilers = u.select(compilers, 'isSystem');
6943
+ return macros = u.select(macros, 'isSystem');
6231
6944
  };
6232
6945
  up.on('up:framework:booted', function() {
6233
6946
  return isBooting = false;
6234
6947
  });
6235
6948
  up.on('up:framework:reset', reset);
6236
6949
  return {
6237
- compiler: compiler,
6238
- macro: macro,
6950
+ compiler: registerCompiler,
6951
+ macro: registerMacro,
6952
+ destructor: registerDestructor,
6239
6953
  compile: compile,
6240
6954
  clean: clean,
6241
- data: data
6955
+ data: readData
6242
6956
  };
6243
6957
  })(jQuery);
6244
6958
 
6245
6959
  up.compiler = up.syntax.compiler;
6246
6960
 
6961
+ up.destructor = up.syntax.destructor;
6962
+
6247
6963
  up.macro = up.syntax.macro;
6248
6964
 
6249
6965
  }).call(this);
@@ -6251,7 +6967,7 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
6251
6967
  /***
6252
6968
  History
6253
6969
  ========
6254
-
6970
+
6255
6971
  In an Unpoly app, every page has an URL.
6256
6972
 
6257
6973
  [Fragment updates](/up.link) automatically update the URL.
@@ -6587,7 +7303,7 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6587
7303
  var slice = [].slice;
6588
7304
 
6589
7305
  up.layout = (function($) {
6590
- var absolutize, anchoredRight, config, finishScrolling, firstHashTarget, fixedChildren, lastScrollTops, measureObstruction, reset, restoreScroll, reveal, revealHash, revealOrRestoreScroll, revealSelector, saveScroll, scroll, scrollAbruptlyNow, scrollTopKey, scrollTops, scrollWithAnimateNow, scrollableElementForViewport, scrollingTracker, u, viewportOf, viewportSelector, viewports, viewportsWithin;
7306
+ var absolutize, anchoredRight, config, finishScrolling, firstHashTarget, fixedChildren, lastScrollTops, measureObstruction, pureHash, reset, restoreScroll, reveal, revealHash, saveScroll, scroll, scrollAbruptlyNow, scrollAfterInsertFragment, scrollTopKey, scrollTops, scrollWithAnimateNow, scrollingTracker, u, viewportOf, viewportSelector, viewports, viewportsWithin;
6591
7307
  u = up.util;
6592
7308
 
6593
7309
  /***
@@ -6683,28 +7399,19 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6683
7399
  @experimental
6684
7400
  */
6685
7401
  scroll = function(viewport, scrollTop, options) {
6686
- var $scrollable;
6687
- $scrollable = scrollableElementForViewport(viewport);
7402
+ var $viewport;
7403
+ $viewport = $(viewport);
6688
7404
  options = u.options(options);
6689
7405
  options.duration = u.option(options.duration, config.duration);
6690
7406
  options.easing = u.option(options.easing, config.easing);
6691
- return finishScrolling($scrollable).then(function() {
7407
+ return finishScrolling($viewport).then(function() {
6692
7408
  if (up.motion.isEnabled() && options.duration > 0) {
6693
- return scrollWithAnimateNow($scrollable, scrollTop, options);
7409
+ return scrollWithAnimateNow($viewport, scrollTop, options);
6694
7410
  } else {
6695
- return scrollAbruptlyNow($scrollable, scrollTop);
7411
+ return scrollAbruptlyNow($viewport, scrollTop);
6696
7412
  }
6697
7413
  });
6698
7414
  };
6699
- scrollableElementForViewport = function(viewport) {
6700
- var $viewport;
6701
- $viewport = $(viewport);
6702
- if ($viewport.get(0) === document) {
6703
- return $('html, body');
6704
- } else {
6705
- return $viewport;
6706
- }
6707
- };
6708
7415
  scrollWithAnimateNow = function($scrollable, scrollTop, animateOptions) {
6709
7416
  var start;
6710
7417
  start = function() {
@@ -6741,7 +7448,7 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6741
7448
  if (!up.motion.isEnabled()) {
6742
7449
  return Promise.resolve();
6743
7450
  }
6744
- $scrollable = scrollableElementForViewport(element);
7451
+ $scrollable = viewportOf(element);
6745
7452
  return scrollingTracker.finish($scrollable);
6746
7453
  };
6747
7454
 
@@ -6760,40 +7467,28 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6760
7467
  @return {Object}
6761
7468
  @internal
6762
7469
  */
6763
- measureObstruction = function() {
6764
- var fixedBottomTops, fixedTopBottoms, measurePosition, obstructor;
6765
- measurePosition = function(obstructor, cssAttr) {
6766
- var $obstructor, anchorPosition;
6767
- $obstructor = $(obstructor);
6768
- anchorPosition = u.readComputedStyleNumber($obstructor, cssAttr);
6769
- if (!u.isPresent(anchorPosition)) {
6770
- up.fail("Fixed element %o must have a CSS attribute %s", $obstructor.get(0), cssAttr);
6771
- }
6772
- return anchorPosition + $obstructor.height();
7470
+ measureObstruction = function(viewportHeight) {
7471
+ var $bottomObstructors, $topObstructors, bottomObstructions, composeHeight, measureBottomObstructor, measureTopObstructor, topObstructions;
7472
+ composeHeight = function(obstructor, distanceFromEdgeProps) {
7473
+ var distanceFromEdge;
7474
+ distanceFromEdge = u.sum(distanceFromEdgeProps, function(prop) {
7475
+ return u.readComputedStyleNumber(obstructor, prop);
7476
+ }) || 0;
7477
+ return distanceFromEdge + obstructor.offsetHeight;
6773
7478
  };
6774
- fixedTopBottoms = (function() {
6775
- var i, len, ref, results;
6776
- ref = $(config.fixedTop.join(', '));
6777
- results = [];
6778
- for (i = 0, len = ref.length; i < len; i++) {
6779
- obstructor = ref[i];
6780
- results.push(measurePosition(obstructor, 'top'));
6781
- }
6782
- return results;
6783
- })();
6784
- fixedBottomTops = (function() {
6785
- var i, len, ref, results;
6786
- ref = $(config.fixedBottom.join(', '));
6787
- results = [];
6788
- for (i = 0, len = ref.length; i < len; i++) {
6789
- obstructor = ref[i];
6790
- results.push(measurePosition(obstructor, 'bottom'));
6791
- }
6792
- return results;
6793
- })();
7479
+ measureTopObstructor = function(obstructor) {
7480
+ return composeHeight(obstructor, ['top', 'margin-top']);
7481
+ };
7482
+ measureBottomObstructor = function(obstructor) {
7483
+ return composeHeight(obstructor, ['bottom', 'margin-bottom']);
7484
+ };
7485
+ $topObstructors = $(config.fixedTop.join(', '));
7486
+ $bottomObstructors = $(config.fixedBottom.join(', '));
7487
+ topObstructions = u.map($topObstructors, measureTopObstructor);
7488
+ bottomObstructions = u.map($bottomObstructors, measureBottomObstructor);
6794
7489
  return {
6795
- top: Math.max.apply(Math, [0].concat(slice.call(fixedTopBottoms))),
6796
- bottom: Math.max.apply(Math, [0].concat(slice.call(fixedBottomTops)))
7490
+ top: Math.max.apply(Math, [0].concat(slice.call(topObstructions))),
7491
+ bottom: Math.max.apply(Math, [0].concat(slice.call(bottomObstructions)))
6797
7492
  };
6798
7493
  };
6799
7494
 
@@ -6849,14 +7544,14 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6849
7544
  var $viewport, elementDims, firstElementRow, lastElementRow, newScrollPos, obstruction, offsetShift, originalScrollPos, predictFirstVisibleRow, predictLastVisibleRow, snap, viewportHeight, viewportIsDocument;
6850
7545
  $viewport = options.viewport ? $(options.viewport) : viewportOf($element);
6851
7546
  snap = u.option(options.snap, config.snap);
6852
- viewportIsDocument = $viewport.is(document);
7547
+ viewportIsDocument = $viewport.is(up.browser.documentViewportSelector());
6853
7548
  viewportHeight = viewportIsDocument ? u.clientSize().height : $viewport.outerHeight();
6854
7549
  originalScrollPos = $viewport.scrollTop();
6855
7550
  newScrollPos = originalScrollPos;
6856
7551
  offsetShift = void 0;
6857
7552
  obstruction = void 0;
6858
7553
  if (viewportIsDocument) {
6859
- obstruction = measureObstruction();
7554
+ obstruction = measureObstruction(viewportHeight);
6860
7555
  offsetShift = 0;
6861
7556
  } else {
6862
7557
  obstruction = {
@@ -6869,14 +7564,14 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6869
7564
  return newScrollPos + obstruction.top;
6870
7565
  };
6871
7566
  predictLastVisibleRow = function() {
6872
- return newScrollPos + viewportHeight - obstruction.bottom - 1;
7567
+ return newScrollPos + viewportHeight - obstruction.bottom;
6873
7568
  };
6874
7569
  elementDims = u.measure($element, {
6875
7570
  relative: $viewport,
6876
7571
  includeMargin: true
6877
7572
  });
6878
7573
  firstElementRow = elementDims.top + offsetShift;
6879
- lastElementRow = firstElementRow + Math.min(elementDims.height, config.substance) - 1;
7574
+ lastElementRow = firstElementRow + Math.min(elementDims.height, config.substance);
6880
7575
  if (lastElementRow > predictLastVisibleRow()) {
6881
7576
  newScrollPos += lastElementRow - predictLastVisibleRow();
6882
7577
  }
@@ -6895,35 +7590,83 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6895
7590
  };
6896
7591
 
6897
7592
  /***
6898
- [Reveals](/up.reveal) an element matching the `#hash` in the current URL.
7593
+ @function up.layout.scrollAfterInsertFragment
7594
+ @param {boolean|object} [options.restoreScroll]
7595
+ @param {boolean|string|jQuery|Element} [options.reveal]
7596
+ @param {boolean|string} [options.reveal]
7597
+ @return {Promise}
7598
+ A promise that is fulfilled when the scrolling has finished.
7599
+ @internal
7600
+ */
7601
+ scrollAfterInsertFragment = function(selectorOrElement, options) {
7602
+ var $element, durationOptions, givenTops, hashOpt, restoreScrollOpt, revealOpt, selector;
7603
+ options = u.options(options);
7604
+ $element = $(selectorOrElement);
7605
+ hashOpt = options.hash;
7606
+ revealOpt = options.reveal;
7607
+ restoreScrollOpt = options.restoreScroll;
7608
+ durationOptions = u.only(options, 'duration');
7609
+ if (restoreScrollOpt) {
7610
+ givenTops = u.presence(restoreScrollOpt, u.isObject);
7611
+ return restoreScroll({
7612
+ around: $element,
7613
+ scrollTops: givenTops
7614
+ });
7615
+ } else if (hashOpt && revealOpt === true) {
7616
+ return revealHash(hashOpt, durationOptions);
7617
+ } else if (revealOpt) {
7618
+ if (u.isElement(revealOpt) || u.isJQuery(revealOpt)) {
7619
+ $element = $(revealOpt);
7620
+ } else if (u.isString(revealOpt)) {
7621
+ selector = up.dom.resolveSelector(revealOpt, options.origin);
7622
+ $element = up.first(selector);
7623
+ } else {
7624
+
7625
+ }
7626
+ if ($element.length) {
7627
+ return reveal($element, durationOptions);
7628
+ }
7629
+ } else {
7630
+ return Promise.resolve();
7631
+ }
7632
+ };
7633
+
7634
+ /***
7635
+ [Reveals](/up.reveal) an element matching the given `#hash` anchor.
6899
7636
 
6900
7637
  Other than the default behavior found in browsers, `up.revealHash` works with
6901
7638
  [multiple viewports](/up-viewport) and honors [fixed elements](/up-fixed-top) obstructing the user's
6902
7639
  view of the viewport.
6903
7640
 
6904
- This is called automatically when the page loads initially.
7641
+ When the page loads initially, this function is automatically called with the hash from
7642
+ the current URL.
7643
+
7644
+ If no element matches the given `#hash` anchor, a resolved promise is returned.
6905
7645
 
6906
7646
  @function up.layout.revealHash
6907
7647
  @return {Promise}
6908
7648
  A promise that is fulfilled when scroll position has changed to match the location hash.
6909
7649
  @experimental
6910
7650
  */
6911
- revealHash = function() {
6912
- var $match, hash;
6913
- if ((hash = up.browser.hash()) && ($match = firstHashTarget(hash))) {
6914
- return reveal($match);
7651
+ revealHash = function(hash) {
7652
+ var $match;
7653
+ if (hash && ($match = firstHashTarget(hash))) {
7654
+ return reveal($match, {
7655
+ top: true
7656
+ });
6915
7657
  } else {
6916
7658
  return Promise.resolve();
6917
7659
  }
6918
7660
  };
6919
7661
  viewportSelector = function() {
6920
- return config.viewports.join(',');
7662
+ return [up.browser.documentViewportSelector()].concat(slice.call(config.viewports)).join(',');
6921
7663
  };
6922
7664
 
6923
7665
  /***
6924
7666
  Returns the viewport for the given element.
6925
7667
 
6926
- Returns `$(document)` if no better viewpoint could be found.
7668
+ Returns the [document's scrolling element](https://developer.mozilla.org/en-US/docs/Web/API/Document/scrollingElement)
7669
+ if no closer viewpoint exists.
6927
7670
 
6928
7671
  @function up.layout.viewportOf
6929
7672
  @param {string|Element|jQuery} selectorOrElement
@@ -6931,16 +7674,12 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6931
7674
  @internal
6932
7675
  */
6933
7676
  viewportOf = function(selectorOrElement, options) {
6934
- var $element, $viewport;
7677
+ var $element;
6935
7678
  if (options == null) {
6936
7679
  options = {};
6937
7680
  }
6938
7681
  $element = $(selectorOrElement);
6939
- $viewport = $element.closest(viewportSelector());
6940
- if ($viewport.length === 0) {
6941
- $viewport = $(document);
6942
- }
6943
- return $viewport;
7682
+ return $element.closest(viewportSelector());
6944
7683
  };
6945
7684
 
6946
7685
  /***
@@ -6965,16 +7704,10 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
6965
7704
  @internal
6966
7705
  */
6967
7706
  viewports = function() {
6968
- return $(document).add(viewportSelector());
7707
+ return $(viewportSelector());
6969
7708
  };
6970
7709
  scrollTopKey = function(viewport) {
6971
- var $viewport;
6972
- $viewport = $(viewport);
6973
- if ($viewport.is(document)) {
6974
- return 'document';
6975
- } else {
6976
- return u.selectorForElement($viewport);
6977
- }
7710
+ return u.selectorForElement(viewport);
6978
7711
  };
6979
7712
 
6980
7713
  /***
@@ -7082,7 +7815,7 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
7082
7815
  } else {
7083
7816
  $viewports = viewports();
7084
7817
  }
7085
- scrollTopsForUrl = lastScrollTops.get(url) || {};
7818
+ scrollTopsForUrl = options.scrollTops || lastScrollTops.get(url) || {};
7086
7819
  return up.log.group('Restoring scroll positions for URL %s to %o', url, scrollTopsForUrl, function() {
7087
7820
  var allScrollPromises;
7088
7821
  allScrollPromises = u.map($viewports, function(viewport) {
@@ -7097,51 +7830,6 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
7097
7830
  });
7098
7831
  };
7099
7832
 
7100
- /***
7101
- @function up.layout.revealOrRestoreScroll
7102
- @param {boolean} [options.restoreScroll]
7103
- @param {boolean|string} [options.reveal]
7104
- @return {Promise}
7105
- A promise that is fulfilled when the element is revealed or
7106
- the scroll position is restored.
7107
- @internal
7108
- */
7109
- revealOrRestoreScroll = function(selectorOrElement, options) {
7110
- var $element, revealOptions, selector;
7111
- options = u.options(options);
7112
- $element = $(selectorOrElement);
7113
- if (options.restoreScroll) {
7114
- return restoreScroll({
7115
- around: $element
7116
- });
7117
- }
7118
- if (options.reveal) {
7119
- revealOptions = {
7120
- duration: options.duration
7121
- };
7122
- if (u.isString(options.reveal)) {
7123
- selector = revealSelector(options.reveal, options);
7124
- $element = up.first(selector) || $element;
7125
- revealOptions.top = true;
7126
- }
7127
- if ($element.length) {
7128
- return reveal($element, revealOptions);
7129
- }
7130
- }
7131
- return Promise.resolve();
7132
- };
7133
-
7134
- /***
7135
- @internal
7136
- */
7137
- revealSelector = function(selector, options) {
7138
- selector = up.dom.resolveSelector(selector, options.origin);
7139
- if (selector[0] === '#') {
7140
- selector += ", a[name='" + selector + "']";
7141
- }
7142
- return selector;
7143
- };
7144
-
7145
7833
  /***
7146
7834
  @internal
7147
7835
  */
@@ -7328,11 +8016,31 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
7328
8016
  @internal
7329
8017
  */
7330
8018
  firstHashTarget = function(hash) {
7331
- if (hash = up.browser.hash(hash)) {
7332
- return up.first("[id='" + hash + "'], a[name='" + hash + "']");
8019
+ var byID, byName;
8020
+ if (hash = pureHash(hash)) {
8021
+ byID = u.attributeSelector('id', hash);
8022
+ byName = 'a' + u.attributeSelector('name', hash);
8023
+ return up.first(byID + "," + byName);
8024
+ }
8025
+ };
8026
+
8027
+ /***
8028
+ Returns `'foo'` if the hash is `'#foo'`.
8029
+
8030
+ Returns undefined if the hash is `'#'`, `''` or `undefined`.
8031
+
8032
+ @function up.browser.hash
8033
+ @internal
8034
+ */
8035
+ pureHash = function(value) {
8036
+ if (value && value[0] === '#') {
8037
+ value = value.substr(1);
7333
8038
  }
8039
+ return u.presence(value);
7334
8040
  };
7335
- up.on('up:app:booted', revealHash);
8041
+ up.on('up:app:booted', function() {
8042
+ return revealHash(location.hash);
8043
+ });
7336
8044
  up.on('up:framework:reset', reset);
7337
8045
  return {
7338
8046
  reveal: reveal,
@@ -7346,7 +8054,7 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
7346
8054
  scrollTops: scrollTops,
7347
8055
  saveScroll: saveScroll,
7348
8056
  restoreScroll: restoreScroll,
7349
- revealOrRestoreScroll: revealOrRestoreScroll,
8057
+ scrollAfterInsertFragment: scrollAfterInsertFragment,
7350
8058
  anchoredRight: anchoredRight,
7351
8059
  fixedChildren: fixedChildren,
7352
8060
  absolutize: absolutize
@@ -7377,7 +8085,7 @@ is built from these functions. You can use them to extend Unpoly from your
7377
8085
 
7378
8086
  (function() {
7379
8087
  up.dom = (function($) {
7380
- var bestMatchingSteps, bestPreflightSelector, config, destroy, emitFragmentDestroy, emitFragmentDestroyed, emitFragmentInserted, emitFragmentKept, extract, findKeepPlan, first, firstInLayer, firstInPriority, hello, isRealElement, layerOf, markElementAsDestroying, matchesLayer, processResponse, reload, replace, reset, resolveSelector, setSource, shouldExtractTitle, shouldLogDestruction, source, swapElements, transferKeepableElements, u, updateHistoryAndTitle;
8088
+ var all, bestMatchingSteps, bestPreflightSelector, config, destroy, emitFragmentDestroy, emitFragmentDestroyed, emitFragmentInserted, emitFragmentKept, extract, findKeepPlan, first, firstInLayer, firstInPriority, hello, isRealElement, layerOf, markElementAsDestroying, matchesLayer, processResponse, reload, replace, reset, resolveSelector, setSource, shouldExtractTitle, shouldLogDestruction, source, swapElements, transferKeepableElements, u, updateHistoryAndTitle;
7381
8089
  u = up.util;
7382
8090
 
7383
8091
  /***
@@ -7442,6 +8150,7 @@ is built from these functions. You can use them to extend Unpoly from your
7442
8150
  @param {string|Element|jQuery} origin
7443
8151
  The element that this selector resolution is relative to.
7444
8152
  That element's selector will be substituted for `&` ([like in Sass](https://sass-lang.com/documentation/file.SASS_REFERENCE.html#parent-selector)).
8153
+ @return {string}
7445
8154
  @internal
7446
8155
  */
7447
8156
  resolveSelector = function(selectorOrElement, origin) {
@@ -7556,13 +8265,8 @@ is built from these functions. You can use them to extend Unpoly from your
7556
8265
  You can also pass `false` to explicitly prevent the title from being updated.
7557
8266
  @param {string} [options.method='get']
7558
8267
  The HTTP method to use for the request.
7559
- @param {Object|Array|FormData} [options.data]
7560
- Parameters that should be sent as the request's payload.
7561
-
7562
- Parameters can either be passed as an object (where the property names become
7563
- the param names and the property values become the param values) or as
7564
- an array of `{ name: 'param-name', value: 'param-value' }` objects
7565
-
8268
+ @param {Object|FormData|string|Array} [options.params]
8269
+ [Parameters](/up.params) that should be sent as the request's payload.
7566
8270
  @param {string} [options.transition='none']
7567
8271
  @param {string|boolean} [options.history=true]
7568
8272
  If a string is given, it is used as the URL the browser's location bar and history.
@@ -7588,18 +8292,15 @@ is built from these functions. You can use them to extend Unpoly from your
7588
8292
  @param {Object} [options.headers={}]
7589
8293
  An object of additional header key/value pairs to send along
7590
8294
  with the request.
7591
- @param {boolean} [options.requireMatch=true]
7592
- Whether to raise an error if the given selector is missing in
7593
- either the current page or in the response.
7594
8295
  @param {Element|jQuery} [options.origin]
7595
8296
  The element that triggered the replacement.
7596
8297
 
7597
8298
  The element's selector will be substituted for the `&` shorthand in the target selector ([like in Sass](https://sass-lang.com/documentation/file.SASS_REFERENCE.html#parent-selector)).
7598
8299
  @param {string} [options.layer='auto']
7599
8300
  The name of the layer that ought to be updated. Valid values are
7600
- `auto`, `page`, `modal` and `popup`.
8301
+ `'auto'`, `'page'`, `'modal'` and `'popup'`.
7601
8302
 
7602
- If set to `auto` (default), Unpoly will try to find a match in the
8303
+ If set to `'auto'` (default), Unpoly will try to find a match in the
7603
8304
  same layer as the element that triggered the replacement (see `options.origin`).
7604
8305
  If that element is not known, or no match was found in that layer,
7605
8306
  Unpoly will search in other layers, starting from the topmost layer.
@@ -7618,7 +8319,7 @@ is built from these functions. You can use them to extend Unpoly from your
7618
8319
  var e, failureOptions, fullLoad, improvedFailTarget, improvedTarget, onFailure, onSuccess, promise, request, successOptions;
7619
8320
  options = u.options(options);
7620
8321
  options.inspectResponse = fullLoad = function() {
7621
- return up.browser.navigate(url, u.only(options, 'method', 'data'));
8322
+ return up.browser.navigate(url, u.only(options, 'method', 'params'));
7622
8323
  };
7623
8324
  if (!up.browser.canPushState() && options.history !== false) {
7624
8325
  if (!options.preload) {
@@ -7644,19 +8345,20 @@ is built from these functions. You can use them to extend Unpoly from your
7644
8345
  e = error;
7645
8346
  return Promise.reject(e);
7646
8347
  }
7647
- request = {
8348
+ request = new up.Request({
7648
8349
  url: url,
7649
8350
  method: options.method,
7650
8351
  data: options.data,
8352
+ params: options.params,
7651
8353
  target: improvedTarget,
7652
8354
  failTarget: improvedFailTarget,
7653
8355
  cache: options.cache,
7654
8356
  preload: options.preload,
7655
8357
  headers: options.headers,
7656
8358
  timeout: options.timeout
7657
- };
8359
+ });
7658
8360
  onSuccess = function(response) {
7659
- return processResponse(true, improvedTarget, response, successOptions);
8361
+ return processResponse(true, improvedTarget, request, response, successOptions);
7660
8362
  };
7661
8363
  onFailure = function(response) {
7662
8364
  var promise, rejection;
@@ -7666,7 +8368,7 @@ is built from these functions. You can use them to extend Unpoly from your
7666
8368
  if (response.isFatalError()) {
7667
8369
  return rejection();
7668
8370
  } else {
7669
- promise = processResponse(false, improvedFailTarget, response, failureOptions);
8371
+ promise = processResponse(false, improvedFailTarget, request, response, failureOptions);
7670
8372
  return u.always(promise, rejection);
7671
8373
  }
7672
8374
  };
@@ -7680,14 +8382,12 @@ is built from these functions. You can use them to extend Unpoly from your
7680
8382
  /***
7681
8383
  @internal
7682
8384
  */
7683
- processResponse = function(isSuccess, selector, response, options) {
7684
- var hash, historyUrl, isReloadable, request, sourceUrl;
7685
- request = response.request;
8385
+ processResponse = function(isSuccess, selector, request, response, options) {
8386
+ var hash, historyUrl, isReloadable, sourceUrl;
7686
8387
  sourceUrl = response.url;
7687
8388
  historyUrl = sourceUrl;
7688
- hash = request.hash;
7689
- if (options.reveal === true && hash) {
7690
- options.reveal = hash;
8389
+ if (hash = request.hash) {
8390
+ options.hash = hash;
7691
8391
  historyUrl += hash;
7692
8392
  }
7693
8393
  isReloadable = response.method === 'GET';
@@ -7771,7 +8471,6 @@ is built from these functions. You can use them to extend Unpoly from your
7771
8471
  return up.log.group('Extracting %s from %d bytes of HTML', selectorOrElement, html != null ? html.length : void 0, function() {
7772
8472
  options = u.options(options, {
7773
8473
  historyMethod: 'push',
7774
- requireMatch: true,
7775
8474
  keep: true,
7776
8475
  layer: 'auto'
7777
8476
  });
@@ -7845,7 +8544,7 @@ is built from these functions. You can use them to extend Unpoly from your
7845
8544
  $old.append($wrapper);
7846
8545
  }
7847
8546
  hello($wrapper.children(), options);
7848
- promise = up.layout.revealOrRestoreScroll($wrapper, options);
8547
+ promise = up.layout.scrollAfterInsertFragment($wrapper, options);
7849
8548
  promise = promise.then(function() {
7850
8549
  return up.animate($wrapper, transition, options);
7851
8550
  });
@@ -8058,7 +8757,7 @@ is built from these functions. You can use them to extend Unpoly from your
8058
8757
  for (i = 0, len = ref.length; i < len; i++) {
8059
8758
  plan = ref[i];
8060
8759
  emitFragmentKept(plan);
8061
- keptElements.push(plan.$element);
8760
+ keptElements.push(plan.$element[0]);
8062
8761
  }
8063
8762
  up.syntax.compile($element, {
8064
8763
  skip: keptElements
@@ -8140,9 +8839,10 @@ is built from these functions. You can use them to extend Unpoly from your
8140
8839
 
8141
8840
  @function up.first
8142
8841
  @param {string|Element|jQuery|Array<Element>} selectorOrElement
8143
- @param {string} options.layer
8144
- The name of the layer in which to find the element. Valid values are
8145
- `auto`, `page`, `modal` and `popup`.
8842
+ @param {string} [options.layer='auto']
8843
+ The name of the layer in which to find the element.
8844
+
8845
+ Valid values are `'auto'`, `'page'`, `'modal'` and `'popup'`.
8146
8846
  @param {string|Element|jQuery} [options.origin]
8147
8847
  An second element or selector that can be referenced as `&` in the first selector:
8148
8848
 
@@ -8196,19 +8896,56 @@ is built from these functions. You can use them to extend Unpoly from your
8196
8896
  }
8197
8897
  return $match;
8198
8898
  };
8899
+
8900
+ /***
8901
+ @function up.dom.layerOf
8902
+ @internal
8903
+ */
8199
8904
  layerOf = function(selectorOrElement) {
8200
8905
  var $element;
8201
8906
  $element = $(selectorOrElement);
8202
- if (up.popup.contains($element)) {
8203
- return 'popup';
8204
- } else if (up.modal.contains($element)) {
8205
- return 'modal';
8206
- } else {
8207
- return 'page';
8907
+ if ($element.length) {
8908
+ if (up.popup.contains($element)) {
8909
+ return 'popup';
8910
+ } else if (up.modal.contains($element)) {
8911
+ return 'modal';
8912
+ } else {
8913
+ return 'page';
8914
+ }
8208
8915
  }
8209
8916
  };
8210
8917
  matchesLayer = function(selectorOrElement, layer) {
8211
- return layerOf(selectorOrElement) === layer;
8918
+ return !layer || layerOf(selectorOrElement) === layer;
8919
+ };
8920
+
8921
+ /***
8922
+ Returns all elements matching the given selector, but
8923
+ ignores elements that are being [destroyed](/up.destroy) or [transitioned](/up.morph).
8924
+
8925
+ If the given argument is already a jQuery collection (or an array
8926
+ of DOM elements), returns the subset of the given list that is matching these conditions.
8927
+
8928
+ @function up.all
8929
+ @param {string|jQuery|Array<Element>} selectorOrElements
8930
+ @param {string|Element|jQuery} [options.origin]
8931
+ An second element or selector that can be referenced as `&` in the first selector.
8932
+ @param {string} [options.layer]
8933
+ The name of the layer in which to find the element. Valid values are
8934
+ `'page'`, `'modal'` and `'popup'`.
8935
+ @return {jQuery}
8936
+ A jQuery collection of matching elements.
8937
+ @experimental
8938
+ */
8939
+ all = function(selectorOrElements, options) {
8940
+ var $root, resolved;
8941
+ options = u.options(options);
8942
+ resolved = resolveSelector(selectorOrElements, options.origin);
8943
+ $root = $(u.option(options.root, document));
8944
+ return $root.find(resolved).filter(function(index, element) {
8945
+ var $element;
8946
+ $element = $(element);
8947
+ return isRealElement($element) && matchesLayer($element, options.layer);
8948
+ });
8212
8949
  };
8213
8950
 
8214
8951
  /***
@@ -8357,10 +9094,12 @@ is built from these functions. You can use them to extend Unpoly from your
8357
9094
  destroy: destroy,
8358
9095
  extract: extract,
8359
9096
  first: first,
9097
+ all: all,
8360
9098
  source: source,
8361
9099
  resolveSelector: resolveSelector,
8362
9100
  hello: hello,
8363
- config: config
9101
+ config: config,
9102
+ layerOf: layerOf
8364
9103
  };
8365
9104
  })(jQuery);
8366
9105
 
@@ -8374,9 +9113,11 @@ is built from these functions. You can use them to extend Unpoly from your
8374
9113
 
8375
9114
  up.first = up.dom.first;
8376
9115
 
9116
+ up.all = up.dom.all;
9117
+
8377
9118
  up.hello = up.dom.hello;
8378
9119
 
8379
- up.renamedModule('flow', 'dom');
9120
+ up.deprecateRenamedModule('flow', 'dom');
8380
9121
 
8381
9122
  }).call(this);
8382
9123
 
@@ -8418,7 +9159,7 @@ You can define custom animations using [`up.transition()`](/up.transition) and
8418
9159
  var slice = [].slice;
8419
9160
 
8420
9161
  up.motion = (function($) {
8421
- var animCount, animate, animateNow, animateOptions, composeTransitionFn, config, defaultNamedAnimations, defaultNamedTransitions, findAnimationFn, findNamedAnimation, findTransitionFn, finish, isEnabled, isNone, isSingletonElement, morph, motionTracker, namedAnimations, namedTransitions, registerAnimation, registerTransition, reset, skipAnimate, snapshot, swapElementsDirectly, translateCss, u, willAnimate;
9162
+ var animCount, animate, animateNow, animateOptions, composeTransitionFn, config, defaultNamedAnimations, defaultNamedTransitions, findAnimationFn, findNamedAnimation, findTransitionFn, finish, isEnabled, isNone, morph, motionTracker, namedAnimations, namedTransitions, registerAnimation, registerTransition, reset, skipAnimate, snapshot, swapElementsDirectly, translateCss, u, willAnimate;
8422
9163
  u = up.util;
8423
9164
  namedAnimations = {};
8424
9165
  defaultNamedAnimations = {};
@@ -8564,10 +9305,7 @@ You can define custom animations using [`up.transition()`](/up.transition) and
8564
9305
  };
8565
9306
  willAnimate = function($elements, animationOrTransition, options) {
8566
9307
  options = animateOptions(options);
8567
- return isEnabled() && !isNone(animationOrTransition) && options.duration > 0 && !isSingletonElement($elements);
8568
- };
8569
- isSingletonElement = function($element) {
8570
- return $element.is('body');
9308
+ return isEnabled() && !isNone(animationOrTransition) && options.duration > 0 && !u.isSingletonElement($elements);
8571
9309
  };
8572
9310
  skipAnimate = function($element, animation) {
8573
9311
  if (u.isOptions(animation)) {
@@ -8743,7 +9481,7 @@ You can define custom animations using [`up.transition()`](/up.transition) and
8743
9481
  scrollOptions = u.merge(options, {
8744
9482
  duration: 0
8745
9483
  });
8746
- return up.layout.revealOrRestoreScroll($new, scrollOptions);
9484
+ return up.layout.scrollAfterInsertFragment($new, scrollOptions);
8747
9485
  };
8748
9486
  if (willMorph) {
8749
9487
  if (motionTracker.isActive($old) && options.trackMotion === false) {
@@ -9220,7 +9958,7 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9220
9958
 
9221
9959
  \#\#\# Example
9222
9960
 
9223
- up.request('/search', data: { query: 'sunshine' }).then(function(response) {
9961
+ up.request('/search', params: { query: 'sunshine' }).then(function(response) {
9224
9962
  console.log('The response text is %o', response.text);
9225
9963
  }).catch(function() {
9226
9964
  console.error('The request failed');
@@ -9255,14 +9993,8 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9255
9993
  requests, if available. If set to `false` a network connection will always be attempted.
9256
9994
  @param {Object} [options.headers={}]
9257
9995
  An object of additional HTTP headers.
9258
- @param {Object|Array|FormData} [options.data={}]
9259
- Parameters that should be sent as the request's payload.
9260
-
9261
- Parameters may be passed as one of the following forms:
9262
-
9263
- 1. An object where keys are param names and the values are param values
9264
- 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
9265
- 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
9996
+ @param {Object|FormData|string|Array} [options.params={}]
9997
+ [Parameters](/up.params) that should be sent as the request's payload.
9266
9998
  @param {string} [options.timeout]
9267
9999
  A timeout in milliseconds.
9268
10000
 
@@ -9277,17 +10009,20 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9277
10009
  @stable
9278
10010
  */
9279
10011
  makeRequest = function() {
9280
- var args, ignoreCache, options, promise, request;
10012
+ var args, ignoreCache, promise, request, requestOrOptions, url;
9281
10013
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
9282
- options = u.extractOptions(args);
9283
- if (u.isGiven(args[0])) {
9284
- options.url = args[0];
10014
+ if (u.isString(args[0])) {
10015
+ url = args.shift();
10016
+ }
10017
+ requestOrOptions = args.shift() || {};
10018
+ if (url) {
10019
+ requestOrOptions.url = url;
9285
10020
  }
9286
- ignoreCache = options.cache === false;
9287
- request = up.Request.wrap(options);
10021
+ request = up.Request.wrap(requestOrOptions);
9288
10022
  if (!request.isSafe()) {
9289
10023
  clear();
9290
10024
  }
10025
+ ignoreCache = request.cache === false;
9291
10026
  if (!ignoreCache && (promise = get(request))) {
9292
10027
  up.puts('Re-using cached response for %s %s', request.method, request.url);
9293
10028
  } else {
@@ -9297,7 +10032,7 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9297
10032
  return remove(request);
9298
10033
  });
9299
10034
  }
9300
- if (!options.preload) {
10035
+ if (!request.preload) {
9301
10036
  loadStarted();
9302
10037
  u.always(promise, loadEnded);
9303
10038
  }
@@ -9311,7 +10046,7 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9311
10046
 
9312
10047
  \#\#\# Example
9313
10048
 
9314
- up.request('/search', data: { query: 'sunshine' }).then(function(text) {
10049
+ up.request('/search', params: { query: 'sunshine' }).then(function(text) {
9315
10050
  console.log('The response text is %o', text);
9316
10051
  }).catch(function() {
9317
10052
  console.error('The request failed');
@@ -9333,14 +10068,8 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9333
10068
  @param {Object} [request.headers={}]
9334
10069
  An object of additional header key/value pairs to send along
9335
10070
  with the request.
9336
- @param {Object|Array|FormData} [options.data]
9337
- Parameters that should be sent as the request's payload.
9338
-
9339
- Parameters may be passed as one of the following forms:
9340
-
9341
- 1. An object where keys are param names and the values are param values
9342
- 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
9343
- 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
10071
+ @param {Object|FormData|string|Array} [options.params]
10072
+ [Parameters](/up.params) that should be sent as the request's payload.
9344
10073
  @param {string} [request.timeout]
9345
10074
  A timeout in milliseconds for the request.
9346
10075
 
@@ -9354,7 +10083,7 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9354
10083
  ajax = function() {
9355
10084
  var args;
9356
10085
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
9357
- up.log.warn('up.ajax() has been deprecated. Use up.request() instead.');
10086
+ up.warn('up.ajax() has been deprecated. Use up.request() instead.');
9358
10087
  return new Promise(function(resolve, reject) {
9359
10088
  var pickResponseText;
9360
10089
  pickResponseText = function(response) {
@@ -9639,7 +10368,7 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9639
10368
  @stable
9640
10369
  */
9641
10370
  clear = cache.clear;
9642
- up.bus.renamedEvent('up:proxy:received', 'up:proxy:loaded');
10371
+ up.bus.deprecateRenamedEvent('up:proxy:received', 'up:proxy:loaded');
9643
10372
  preloadAfterDelay = function($link) {
9644
10373
  var curriedPreload, delay;
9645
10374
  delay = parseInt(u.presentAttr($link, 'up-delay')) || config.preloadDelay;
@@ -9672,24 +10401,42 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9672
10401
  @function up.proxy.preload
9673
10402
  @param {string|Element|jQuery}
9674
10403
  The element whose destination should be preloaded.
10404
+ @param {object} options
10405
+ Options that will be passed to the function making the HTTP requests.
9675
10406
  @return
9676
10407
  A promise that will be fulfilled when the request was loaded and cached
9677
10408
  @experimental
9678
10409
  */
9679
- preload = function(linkOrSelector) {
9680
- var $link;
10410
+ preload = function(linkOrSelector, options) {
10411
+ var $link, preloadEventAttrs;
9681
10412
  $link = $(linkOrSelector);
9682
10413
  if (up.link.isSafe($link)) {
9683
- return up.log.group("Preloading link %o", $link.get(0), function() {
10414
+ preloadEventAttrs = {
10415
+ message: ['Preloading link %o', $link.get(0)],
10416
+ $element: $link,
10417
+ $link: $link
10418
+ };
10419
+ return up.bus.whenEmitted('up:link:preload', preloadEventAttrs).then(function() {
9684
10420
  var variant;
9685
10421
  variant = up.link.followVariantForLink($link);
9686
- return variant.preloadLink($link);
10422
+ return variant.preloadLink($link, options);
9687
10423
  });
9688
10424
  } else {
9689
10425
  return Promise.reject(new Error("Won't preload unsafe link"));
9690
10426
  }
9691
10427
  };
9692
10428
 
10429
+ /***
10430
+ This event is [emitted](/up.emit) before a link is [preloaded](/up.preload).
10431
+
10432
+ @event up:link:preload
10433
+ @param {jQuery} event.$link
10434
+ The link element that will be preloaded.
10435
+ @param event.preventDefault()
10436
+ Event listeners may call this method to prevent the link from being preloaded.
10437
+ @stable
10438
+ */
10439
+
9693
10440
  /***
9694
10441
  @internal
9695
10442
  */
@@ -9700,12 +10447,12 @@ Other Unpoly modules contain even more tricks to outsmart network latency:
9700
10447
  /***
9701
10448
  @internal
9702
10449
  */
9703
- wrapMethod = function(method, data, appendOpts) {
10450
+ wrapMethod = function(method, params) {
9704
10451
  if (u.contains(config.wrapMethods, method)) {
9705
- data = u.appendRequestData(data, up.protocol.config.methodParam, method, appendOpts);
10452
+ params = up.params.add(params, up.protocol.config.methodParam, method);
9706
10453
  method = 'POST';
9707
10454
  }
9708
- return [method, data];
10455
+ return [method, params];
9709
10456
  };
9710
10457
 
9711
10458
  /***
@@ -9885,8 +10632,13 @@ new page is loading.
9885
10632
  An element or selector which is either an `<a>` tag or any element with an `up-href` attribute.
9886
10633
  @param {string} [options.target]
9887
10634
  The selector to replace.
9888
- Defaults to the `[up-target]`, `[up-modal]` or `[up-popup]` attribute on `link`.
10635
+
10636
+ Defaults to the link's `[up-target]`, `[up-modal]` or `[up-popup]` attribute.
9889
10637
  If no target is given, the `<body>` element will be replaced.
10638
+ @param {String} [options.url]
10639
+ The URL to navigate to.
10640
+
10641
+ Defaults to the link's `[up-href]` or `[href]` attribute.
9890
10642
  @param {boolean|string} [options.reveal=true]
9891
10643
  Whether to [reveal](/up.reveal) the target fragment after it was replaced.
9892
10644
 
@@ -9910,8 +10662,10 @@ new page is loading.
9910
10662
  /***
9911
10663
  This event is [emitted](/up.emit) when a link is [followed](/up.follow) through Unpoly.
9912
10664
 
10665
+ The event is emitted on the `<a>` element that is being followed.
10666
+
9913
10667
  @event up:link:follow
9914
- @param {jQuery} event.$element
10668
+ @param {jQuery} event.$link
9915
10669
  The link element that will be followed.
9916
10670
  @param event.preventDefault()
9917
10671
  Event listeners may call this method to prevent the link from being followed.
@@ -9926,7 +10680,7 @@ new page is loading.
9926
10680
  var $link, target, url;
9927
10681
  $link = $(linkOrSelector);
9928
10682
  options = u.options(options);
9929
- url = u.option($link.attr('up-href'), $link.attr('href'));
10683
+ url = u.option(options.url, $link.attr('up-href'), $link.attr('href'));
9930
10684
  target = u.option(options.target, $link.attr('up-target'));
9931
10685
  options.failTarget = u.option(options.failTarget, $link.attr('up-fail-target'));
9932
10686
  options.fallback = u.option(options.fallback, $link.attr('up-fallback'));
@@ -10181,9 +10935,9 @@ new page is loading.
10181
10935
  or make an educated guess (default).
10182
10936
  @param {string} [up-layer='auto']
10183
10937
  The name of the layer that ought to be updated. Valid values are
10184
- `auto`, `page`, `modal` and `popup`.
10938
+ `'auto'`, `'page'`, `'modal'` and `'popup'`.
10185
10939
 
10186
- If set to `auto` (default), Unpoly will try to find a match in the link's layer.
10940
+ If set to `'auto'` (default), Unpoly will try to find a match in the link's layer.
10187
10941
  If no match was found in that layer,
10188
10942
  Unpoly will search in other layers, starting from the topmost layer.
10189
10943
  @param {string} [up-fail-layer='auto']
@@ -10419,7 +11173,7 @@ open dialogs with sub-forms, etc. all without losing form state.
10419
11173
  var slice = [].slice;
10420
11174
 
10421
11175
  up.form = (function($) {
10422
- var autosubmit, config, fieldSelector, findSwitcherForTarget, observe, observeField, reset, resolveValidateTarget, submit, switchTarget, switchTargets, switcherValues, u, validate;
11176
+ var autosubmit, config, fieldSelector, findSwitcherForTarget, observe, observeField, reset, resolveValidateTarget, submit, submitButtonSelector, switchTarget, switchTargets, switcherValues, u, validate;
10423
11177
  u = up.util;
10424
11178
 
10425
11179
  /***
@@ -10438,13 +11192,16 @@ open dialogs with sub-forms, etc. all without losing form state.
10438
11192
  By default this looks for a `<fieldset>`, `<label>` or `<form>`
10439
11193
  around the validating input field, or any element with an
10440
11194
  `up-fieldset` attribute.
10441
- @param {string} [config.fields=[':input']]
11195
+ @param {string} [config.fields]
10442
11196
  An array of CSS selectors that represent form fields, such as `input` or `select`.
11197
+ @param {string} [config.submitButtons]
11198
+ An array of CSS selectors that represent submit buttons, such as `input[type=submit]`.
10443
11199
  @stable
10444
11200
  */
10445
11201
  config = u.config({
10446
11202
  validateTargets: ['[up-fieldset]:has(&)', 'fieldset:has(&)', 'label:has(&)', 'form:has(&)'],
10447
- fields: [':input'],
11203
+ fields: ['select', 'input:not([type=submit]):not([type=image])', 'button[type]:not([type=submit])', 'textarea'],
11204
+ submitButtons: ['input[type=submit]', 'input[type=image]', 'button[type=submit]', 'button:not([type])'],
10448
11205
  observeDelay: 0
10449
11206
  });
10450
11207
  reset = function() {
@@ -10459,6 +11216,14 @@ open dialogs with sub-forms, etc. all without losing form state.
10459
11216
  return config.fields.join(',');
10460
11217
  };
10461
11218
 
11219
+ /***
11220
+ @function up.form.submitButtonSelector
11221
+ @internal
11222
+ */
11223
+ submitButtonSelector = function() {
11224
+ return config.submitButtons.join(',');
11225
+ };
11226
+
10462
11227
  /***
10463
11228
  Submits a form via AJAX and updates a page fragment with the response.
10464
11229
 
@@ -10541,9 +11306,9 @@ open dialogs with sub-forms, etc. all without losing form state.
10541
11306
  with the request.
10542
11307
  @param {string} [options.layer='auto']
10543
11308
  The name of the layer that ought to be updated. Valid values are
10544
- `auto`, `page`, `modal` and `popup`.
11309
+ `'auto'`, `'page'`, `'modal'` and `'popup'`.
10545
11310
 
10546
- If set to `auto` (default), Unpoly will try to find a match in the form's layer.
11311
+ If set to `'auto'` (default), Unpoly will try to find a match in the form's layer.
10547
11312
  @param {string} [options.failLayer='auto']
10548
11313
  The name of the layer that ought to be updated if the server sends a non-200 status code.
10549
11314
  @return {Promise}
@@ -10570,7 +11335,7 @@ open dialogs with sub-forms, etc. all without losing form state.
10570
11335
  options.origin = u.option(options.origin, $form);
10571
11336
  options.layer = u.option(options.layer, $form.attr('up-layer'), 'auto');
10572
11337
  options.failLayer = u.option(options.failLayer, $form.attr('up-fail-layer'), 'auto');
10573
- options.data = u.requestDataFromForm($form);
11338
+ options.params = up.params.fromForm($form);
10574
11339
  options = u.merge(options, up.motion.animateOptions(options, $form));
10575
11340
  if (options.validate) {
10576
11341
  options.headers || (options.headers = {});
@@ -10579,6 +11344,8 @@ open dialogs with sub-forms, etc. all without losing form state.
10579
11344
  options.headers[up.protocol.config.validateHeader] = options.validate;
10580
11345
  }
10581
11346
  return up.bus.whenEmitted('up:form:submit', {
11347
+ message: 'Submitting form',
11348
+ $form: $form,
10582
11349
  $element: $form
10583
11350
  }).then(function() {
10584
11351
  var promise;
@@ -10599,7 +11366,7 @@ open dialogs with sub-forms, etc. all without losing form state.
10599
11366
  This event is [emitted](/up.emit) when a form is [submitted](/up.submit) through Unpoly.
10600
11367
 
10601
11368
  @event up:form:submit
10602
- @param {jQuery} event.$element
11369
+ @param {jQuery} event.$form
10603
11370
  The `<form>` element that will be submitted.
10604
11371
  @param event.preventDefault()
10605
11372
  Event listeners may call this method to prevent the form from being submitted.
@@ -10868,11 +11635,11 @@ open dialogs with sub-forms, etc. all without losing form state.
10868
11635
  $target = $(target);
10869
11636
  fieldValues || (fieldValues = switcherValues(findSwitcherForTarget($target)));
10870
11637
  if (hideValues = $target.attr('up-hide-for')) {
10871
- hideValues = hideValues.split(' ');
11638
+ hideValues = u.splitValues(hideValues);
10872
11639
  show = u.intersect(fieldValues, hideValues).length === 0;
10873
11640
  } else {
10874
11641
  if (showValues = $target.attr('up-show-for')) {
10875
- showValues = showValues.split(' ');
11642
+ showValues = u.splitValues(showValues);
10876
11643
  } else {
10877
11644
  showValues = [':present', ':checked'];
10878
11645
  }
@@ -10915,7 +11682,7 @@ open dialogs with sub-forms, etc. all without losing form state.
10915
11682
 
10916
11683
  \#\#\# Failed submission
10917
11684
 
10918
- When the server was unable to save the form due to invalid data,
11685
+ When the server was unable to save the form due to invalid params,
10919
11686
  it will usually re-render an updated copy of the form with
10920
11687
  validation messages.
10921
11688
 
@@ -10998,9 +11765,9 @@ open dialogs with sub-forms, etc. all without losing form state.
10998
11765
  or `method` (vanilla HTML) for the same purpose.
10999
11766
  @param {string} [up-layer='auto']
11000
11767
  The name of the layer that ought to be updated. Valid values are
11001
- `auto`, `page`, `modal` and `popup`.
11768
+ `'auto'`, `'page'`, `'modal'` and `'popup'`.
11002
11769
 
11003
- If set to `auto` (default), Unpoly will try to find a match in the form's layer.
11770
+ If set to `'auto'` (default), Unpoly will try to find a match in the form's layer.
11004
11771
  If no match was found in that layer,
11005
11772
  Unpoly will search in other layers, starting from the topmost layer.
11006
11773
  @param {string} [up-fail-layer='auto']
@@ -11428,7 +12195,8 @@ open dialogs with sub-forms, etc. all without losing form state.
11428
12195
  validate: validate,
11429
12196
  switchTargets: switchTargets,
11430
12197
  autosubmit: autosubmit,
11431
- fieldSelector: fieldSelector
12198
+ fieldSelector: fieldSelector,
12199
+ submitButtonSelector: submitButtonSelector
11432
12200
  };
11433
12201
  })(jQuery);
11434
12202
 
@@ -11502,7 +12270,7 @@ The HTML of a popup element is simply this:
11502
12270
  @param {string} [config.position='bottom-right']
11503
12271
  Defines where the popup is attached to the opening element.
11504
12272
 
11505
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
12273
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
11506
12274
  @param {string} [config.history=false]
11507
12275
  Whether opening a popup will add a browser history entry.
11508
12276
  @param {string} [config.openAnimation='fade-in']
@@ -11650,7 +12418,7 @@ The HTML of a popup element is simply this:
11650
12418
  @param {string} [options.position='bottom-right']
11651
12419
  Defines where the popup is attached to the opening element.
11652
12420
 
11653
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
12421
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
11654
12422
  @param {string} [options.html]
11655
12423
  A string of HTML from which to extract the popup contents. No network request will be made.
11656
12424
  @param {string} [options.confirm]
@@ -11887,7 +12655,7 @@ The HTML of a popup element is simply this:
11887
12655
  @param [up-position]
11888
12656
  Defines where the popup is attached to the opening element.
11889
12657
 
11890
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
12658
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
11891
12659
  @param {string} [up-confirm]
11892
12660
  A message that will be displayed in a cancelable confirmation dialog
11893
12661
  before the popup is opened.
@@ -12041,7 +12809,7 @@ or function.
12041
12809
 
12042
12810
  (function() {
12043
12811
  up.modal = (function($) {
12044
- var animate, autoclose, chain, closeAsap, closeNow, config, contains, createHiddenFrame, discardHistory, extractAsap, flavor, flavorDefault, flavorOverrides, flavors, followAsap, isOpen, markAsAnimating, openAsap, openNow, preloadNow, reset, shiftElements, state, templateHtml, u, unshiftElements, unveilFrame, visitAsap;
12812
+ var animate, autoclose, chain, closeAsap, closeNow, config, contains, createHiddenFrame, discardHistory, extractAsap, flavor, flavorDefault, flavorOverrides, flavors, followAsap, isOpen, markAsAnimating, openAsap, openNow, preloadNow, reset, shiftElements, state, templateHtml, u, unshiftElements, unveilFrame, validateTarget, visitAsap;
12045
12813
  u = up.util;
12046
12814
 
12047
12815
  /***
@@ -12430,7 +13198,8 @@ or function.
12430
13198
  $link = u.option(u.pluckKey(options, '$link'), u.nullJQuery());
12431
13199
  url = u.option(u.pluckKey(options, 'url'), $link.attr('up-href'), $link.attr('href'));
12432
13200
  html = u.option(u.pluckKey(options, 'html'));
12433
- target = u.option(u.pluckKey(options, 'target'), $link.attr('up-modal'), 'body');
13201
+ target = u.option(u.pluckKey(options, 'target'), $link.attr('up-modal'));
13202
+ validateTarget(target);
12434
13203
  options.flavor = u.option(options.flavor, $link.attr('up-flavor'), config.flavor);
12435
13204
  options.position = u.option(options.position, $link.attr('up-position'), flavorDefault('position', options.flavor));
12436
13205
  options.position = u.evalOption(options.position, {
@@ -12506,6 +13275,13 @@ or function.
12506
13275
  });
12507
13276
  });
12508
13277
  };
13278
+ validateTarget = function(target) {
13279
+ if (u.isBlank(target)) {
13280
+ return up.fail('Cannot open a modal without a target selector');
13281
+ } else if (target === 'body') {
13282
+ return up.fail('Cannot open the <body> in a modal');
13283
+ }
13284
+ };
12509
13285
 
12510
13286
  /***
12511
13287
  This event is [emitted](/up.emit) when a modal dialog is starting to open.
@@ -12656,7 +13432,7 @@ or function.
12656
13432
  if (overrideConfig == null) {
12657
13433
  overrideConfig = {};
12658
13434
  }
12659
- up.log.warn('up.modal.flavor() is deprecated. Use the up.modal.flavors property instead.');
13435
+ up.warn('up.modal.flavor() is deprecated. Use the up.modal.flavors property instead.');
12660
13436
  return u.assign(flavorOverrides(name), overrideConfig);
12661
13437
  };
12662
13438
 
@@ -13283,7 +14059,7 @@ Once the response is received the URL will change to `/bar` and the `up-active`
13283
14059
  for (i = 0, len = ref.length; i < len; i++) {
13284
14060
  attr = ref[i];
13285
14061
  if (value = u.presentAttr($section, attr)) {
13286
- ref1 = value.split(/\s+/);
14062
+ ref1 = u.splitValues(value);
13287
14063
  for (j = 0, len1 = ref1.length; j < len1; j++) {
13288
14064
  url = ref1[j];
13289
14065
  if (url !== '#') {
@@ -13580,7 +14356,7 @@ Once the response is received the URL will change to `/bar` and the `up-active`
13580
14356
  };
13581
14357
  })(jQuery);
13582
14358
 
13583
- up.renamedModule('navigation', 'feedback');
14359
+ up.deprecateRenamedModule('navigation', 'feedback');
13584
14360
 
13585
14361
  }).call(this);
13586
14362