upjs-rails 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d1b52fa522fbde804ecf3db0de54efa03a5efa5
4
- data.tar.gz: 2f8c0c4b0589ffb165a1237caa492a41922a3157
3
+ metadata.gz: 77dcd1a1f7e464a6f108d1fb3fafcc34c1c6c06e
4
+ data.tar.gz: 63001606276d007e811562e6c1e26d078a45fa63
5
5
  SHA512:
6
- metadata.gz: 688084094aeea1ae88a73f96d5243e632e82b089376e95d3ee0cf37e77b57ef5b10259bdc0ae3f1ddf25b3d005b7bd07ee43d1530f012b9f8c6dd51bcd5660ec
7
- data.tar.gz: af2131873f4c54165dc49780fb724b96bb4ab6b5ae0c830abb18d7469e3b01b9da6cf689066579089c0dd5325c33cd61a5b9f8f1d5b847b2629f6c0e61aef412
6
+ metadata.gz: 43e602670030b82cb89e564dfa223bfa3a602872ff55225f09915b066c3e0588e620affd0fbe670c6eb4f9a9648b0adc37c8f4186c244842e3ac37f7b9b8b175
7
+ data.tar.gz: 00c7bfc420e1f6397f18c9f7bb703a406e6ef3bfbf7d5881a72104514220e323cf47b0c6307de7c91123174eed84fc75516c4d5a12334c143cac914b559612c8
data/CHANGELOG.md CHANGED
@@ -10,9 +10,22 @@ Unreleased
10
10
 
11
11
  ### Compatible changes
12
12
 
13
+ ### Breaking changes
14
+
15
+
16
+ 0.19.0
17
+ ------
18
+
19
+ ### Compatible changes
20
+
21
+ - Elements can now be persisted during page updates using the [`up-keep`](/up-keep) attribute.
22
+ - `up.proxy.ajax` is now available as [`up.ajax`](/up.ajax).
23
+ - `up.ajax` can now handle nested objects as `{ data }` option (used to pass form parameters).
13
24
 
14
25
  ### Breaking changes
15
26
 
27
+ - `up.implant` has been renamed to [`up.extract`](/up.extract).
28
+
16
29
 
17
30
  0.18.1
18
31
  ------
data/dist/up.js CHANGED
@@ -27,7 +27,7 @@ that might save you from loading something like [Underscore.js](http://underscor
27
27
  @function up.util.memoize
28
28
  @internal
29
29
  */
30
- var $createElementFromSelector, ANIMATION_PROMISE_KEY, CONSOLE_PLACEHOLDERS, any, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, cssAnimate, debug, detect, each, error, escapePressed, evalConsoleTemplate, except, extend, findWithSelf, finishCssAnimate, fixedToAbsolute, forceCompositing, intersect, isArray, isBlank, isDeferred, isDefined, isElement, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, multiSelector, nextFrame, nonUpClasses, normalizeMethod, normalizeUrl, nullJQuery, offsetParent, once, only, option, options, parseUrl, presence, presentAttr, reject, remove, requestDataAsArray, requestDataAsQueryString, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, selectorForElement, setMissingAttrs, temporaryCss, times, titleFromXhr, toArray, trim, unJQuery, uniq, unresolvableDeferred, unresolvablePromise, unwrapElement, warn;
30
+ var $createElementFromSelector, $createPlaceholder, ANIMATION_PROMISE_KEY, all, any, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, cssAnimate, detect, each, error, escapePressed, except, extend, findWithSelf, finishCssAnimate, fixedToAbsolute, forceCompositing, intersect, isArray, isBlank, isDeferred, isDefined, isElement, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, multiSelector, nextFrame, nonUpClasses, normalizeMethod, normalizeUrl, nullJQuery, offsetParent, once, only, option, options, parseUrl, presence, presentAttr, reject, remove, requestDataAsArray, requestDataAsQuery, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, selectorForElement, setMissingAttrs, temporaryCss, times, titleFromXhr, toArray, trim, unJQuery, uniq, unresolvableDeferred, unresolvablePromise, unwrapElement;
31
31
  memoize = function(func) {
32
32
  var cache, cached;
33
33
  cache = void 0;
@@ -140,17 +140,17 @@ that might save you from loading something like [Underscore.js](http://underscor
140
140
  @internal
141
141
  */
142
142
  $createElementFromSelector = function(selector) {
143
- var $element, $parent, $root, classes, conjunction, depthSelector, expression, html, id, iteration, j, k, len, len1, path, tag;
143
+ var $element, $parent, $root, classes, conjunction, depthSelector, expression, html, i, id, iteration, j, len, len1, path, tag;
144
144
  path = selector.split(/[ >]/);
145
145
  $root = null;
146
- for (iteration = j = 0, len = path.length; j < len; iteration = ++j) {
146
+ for (iteration = i = 0, len = path.length; i < len; iteration = ++i) {
147
147
  depthSelector = path[iteration];
148
148
  conjunction = depthSelector.match(/(^|\.|\#)[A-Za-z0-9\-_]+/g);
149
149
  tag = "div";
150
150
  classes = [];
151
151
  id = null;
152
- for (k = 0, len1 = conjunction.length; k < len1; k++) {
153
- expression = conjunction[k];
152
+ for (j = 0, len1 = conjunction.length; j < len1; j++) {
153
+ expression = conjunction[j];
154
154
  switch (expression[0]) {
155
155
  case ".":
156
156
  classes.push(expression.substr(1));
@@ -191,90 +191,17 @@ that might save you from loading something like [Underscore.js](http://underscor
191
191
  };
192
192
 
193
193
  /**
194
- Prints a debugging message to the browser console.
195
-
196
- @function up.debug
197
- @param {String} message
198
- @param {Array} args...
199
- @internal
200
- */
201
- debug = function() {
202
- var args, message, ref;
203
- message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
204
- message = "[UP] " + message;
205
- return (ref = up.browser).puts.apply(ref, ['debug', message].concat(slice.call(args)));
206
- };
207
-
208
- /**
209
- @function up.warn
210
- @internal
211
- */
212
- warn = function() {
213
- var args, message, ref;
214
- message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
215
- message = "[UP] " + message;
216
- return (ref = up.browser).puts.apply(ref, ['warn', message].concat(slice.call(args)));
217
- };
218
-
219
- /**
220
- Throws a fatal error with the given message.
221
-
222
- - The error will be printed to the [error console](https://developer.mozilla.org/en-US/docs/Web/API/Console/error)
223
- - An [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) (exception) will be thrown, unwinding the current call stack
224
- - The error message will be printed in a corner of the screen
225
-
226
- \#\#\#\# Examples
227
-
228
- up.error('Division by zero')
229
- up.error('Unexpected result %o', result)
230
-
231
- @function up.error
232
- @internal
194
+ @function $create
233
195
  */
234
- error = function() {
235
- var $error, args, asString, ref;
236
- args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
237
- args[0] = "[UP] " + args[0];
238
- (ref = up.browser).puts.apply(ref, ['error'].concat(slice.call(args)));
239
- asString = evalConsoleTemplate.apply(null, args);
240
- $error = presence($('.up-error')) || $('<div class="up-error"></div>').prependTo('body');
241
- $error.addClass('up-error');
242
- $error.text(asString);
243
- throw new Error(asString);
244
- };
245
- CONSOLE_PLACEHOLDERS = /\%[odisf]/g;
246
- evalConsoleTemplate = function() {
247
- var args, i, maxLength, message;
248
- args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
249
- message = args[0];
250
- i = 0;
251
- maxLength = 80;
252
- return message.replace(CONSOLE_PLACEHOLDERS, function() {
253
- var arg, argType;
254
- i += 1;
255
- arg = args[i];
256
- argType = typeof arg;
257
- if (argType === 'string') {
258
- arg = arg.replace(/\s+/g, ' ');
259
- if (arg.length > maxLength) {
260
- arg = (arg.substr(0, maxLength)) + "…";
261
- }
262
- arg = "\"" + arg + "\"";
263
- } else if (argType === 'undefined') {
264
- arg = 'undefined';
265
- } else if (argType === 'number' || argType === 'function') {
266
- arg = arg.toString();
267
- } else {
268
- arg = JSON.stringify(arg);
269
- }
270
- if (arg.length > maxLength) {
271
- arg = (arg.substr(0, maxLength)) + " …";
272
- if (argType === 'object' || argType === 'function') {
273
- arg += " }";
274
- }
275
- }
276
- return arg;
277
- });
196
+ $createPlaceholder = function(selector, container) {
197
+ var $placeholder;
198
+ if (container == null) {
199
+ container = document.body;
200
+ }
201
+ $placeholder = $createElementFromSelector(selector);
202
+ $placeholder.addClass('up-placeholder');
203
+ $placeholder.appendTo(container);
204
+ return $placeholder;
278
205
  };
279
206
 
280
207
  /**
@@ -294,10 +221,10 @@ that might save you from loading something like [Underscore.js](http://underscor
294
221
  @experimental
295
222
  */
296
223
  selectorForElement = function(element) {
297
- var $element, classes, id, j, klass, len, name, selector, upId;
224
+ var $element, classes, i, id, klass, len, name, selector, upId;
298
225
  $element = $(element);
299
226
  selector = void 0;
300
- debug("Creating selector from element %o", $element.get(0));
227
+ up.puts("Creating selector from element %o", $element.get(0));
301
228
  if (upId = presence($element.attr("up-id"))) {
302
229
  selector = "[up-id='" + upId + "']";
303
230
  } else if (id = presence($element.attr("id"))) {
@@ -305,10 +232,9 @@ that might save you from loading something like [Underscore.js](http://underscor
305
232
  } else if (name = presence($element.attr("name"))) {
306
233
  selector = "[name='" + name + "']";
307
234
  } else if (classes = presence(nonUpClasses($element))) {
308
- console.log("using klass!", classes);
309
235
  selector = '';
310
- for (j = 0, len = classes.length; j < len; j++) {
311
- klass = classes[j];
236
+ for (i = 0, len = classes.length; i < len; i++) {
237
+ klass = classes[i];
312
238
  selector += "." + klass;
313
239
  }
314
240
  } else {
@@ -387,9 +313,9 @@ that might save you from loading something like [Underscore.js](http://underscor
387
313
  @stable
388
314
  */
389
315
  each = function(array, block) {
390
- var index, item, j, len, results;
316
+ var i, index, item, len, results;
391
317
  results = [];
392
- for (index = j = 0, len = array.length; j < len; index = ++j) {
318
+ for (index = i = 0, len = array.length; i < len; index = ++i) {
393
319
  item = array[index];
394
320
  results.push(block(item, index));
395
321
  }
@@ -418,9 +344,9 @@ that might save you from loading something like [Underscore.js](http://underscor
418
344
  @stable
419
345
  */
420
346
  times = function(count, block) {
421
- var iteration, j, ref, results;
347
+ var i, iteration, ref, results;
422
348
  results = [];
423
- for (iteration = j = 0, ref = count - 1; 0 <= ref ? j <= ref : j >= ref; iteration = 0 <= ref ? ++j : --j) {
349
+ for (iteration = i = 0, ref = count - 1; 0 <= ref ? i <= ref : i >= ref; iteration = 0 <= ref ? ++i : --i) {
424
350
  results.push(block(iteration));
425
351
  }
426
352
  return results;
@@ -797,10 +723,10 @@ that might save you from loading something like [Underscore.js](http://underscor
797
723
  @stable
798
724
  */
799
725
  detect = function(array, tester) {
800
- var element, j, len, match;
726
+ var element, i, len, match;
801
727
  match = void 0;
802
- for (j = 0, len = array.length; j < len; j++) {
803
- element = array[j];
728
+ for (i = 0, len = array.length; i < len; i++) {
729
+ element = array[i];
804
730
  if (tester(element)) {
805
731
  match = element;
806
732
  break;
@@ -820,10 +746,10 @@ that might save you from loading something like [Underscore.js](http://underscor
820
746
  @experimental
821
747
  */
822
748
  any = function(array, tester) {
823
- var element, j, len, match;
749
+ var element, i, len, match;
824
750
  match = false;
825
- for (j = 0, len = array.length; j < len; j++) {
826
- element = array[j];
751
+ for (i = 0, len = array.length; i < len; i++) {
752
+ element = array[i];
827
753
  if (tester(element)) {
828
754
  match = true;
829
755
  break;
@@ -832,6 +758,29 @@ that might save you from loading something like [Underscore.js](http://underscor
832
758
  return match;
833
759
  };
834
760
 
761
+ /**
762
+ Returns whether the given function returns a truthy value
763
+ for all elements in the given array.
764
+
765
+ @function up.util.all
766
+ @param {Array<T>} array
767
+ @param {Function<T>} tester
768
+ @return {Boolean}
769
+ @experimental
770
+ */
771
+ all = function(array, tester) {
772
+ var element, i, len, match;
773
+ match = true;
774
+ for (i = 0, len = array.length; i < len; i++) {
775
+ element = array[i];
776
+ if (!tester(element)) {
777
+ match = false;
778
+ break;
779
+ }
780
+ }
781
+ return match;
782
+ };
783
+
835
784
  /**
836
785
  Returns all elements from the given array that are
837
786
  neither `null` or `undefined`.
@@ -925,10 +874,10 @@ that might save you from loading something like [Underscore.js](http://underscor
925
874
  var $element, attrName, attrNames, values;
926
875
  $element = arguments[0], attrNames = 2 <= arguments.length ? slice.call(arguments, 1) : [];
927
876
  values = (function() {
928
- var j, len, results;
877
+ var i, len, results;
929
878
  results = [];
930
- for (j = 0, len = attrNames.length; j < len; j++) {
931
- attrName = attrNames[j];
879
+ for (i = 0, len = attrNames.length; i < len; i++) {
880
+ attrName = attrNames[i];
932
881
  results.push($element.attr(attrName));
933
882
  }
934
883
  return results;
@@ -1212,11 +1161,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1212
1161
  @internal
1213
1162
  */
1214
1163
  copyAttributes = function($source, $target) {
1215
- var attr, j, len, ref, results;
1164
+ var attr, i, len, ref, results;
1216
1165
  ref = $source.get(0).attributes;
1217
1166
  results = [];
1218
- for (j = 0, len = ref.length; j < len; j++) {
1219
- attr = ref[j];
1167
+ for (i = 0, len = ref.length; i < len; i++) {
1168
+ attr = ref[i];
1220
1169
  if (attr.specified) {
1221
1170
  results.push($target.attr(attr.name, attr.value));
1222
1171
  } else {
@@ -1311,11 +1260,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1311
1260
  @stable
1312
1261
  */
1313
1262
  only = function() {
1314
- var filtered, j, len, object, properties, property;
1263
+ var filtered, i, len, object, properties, property;
1315
1264
  object = arguments[0], properties = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1316
1265
  filtered = {};
1317
- for (j = 0, len = properties.length; j < len; j++) {
1318
- property = properties[j];
1266
+ for (i = 0, len = properties.length; i < len; i++) {
1267
+ property = properties[i];
1319
1268
  if (object.hasOwnProperty(property)) {
1320
1269
  filtered[property] = object[property];
1321
1270
  }
@@ -1333,11 +1282,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1333
1282
  @stable
1334
1283
  */
1335
1284
  except = function() {
1336
- var filtered, j, len, object, properties, property;
1285
+ var filtered, i, len, object, properties, property;
1337
1286
  object = arguments[0], properties = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1338
1287
  filtered = copy(object);
1339
- for (j = 0, len = properties.length; j < len; j++) {
1340
- property = properties[j];
1288
+ for (i = 0, len = properties.length; i < len; i++) {
1289
+ property = properties[i];
1341
1290
  delete filtered[property];
1342
1291
  }
1343
1292
  return filtered;
@@ -1484,12 +1433,12 @@ that might save you from loading something like [Underscore.js](http://underscor
1484
1433
  @internal
1485
1434
  */
1486
1435
  multiSelector = function(parts) {
1487
- var combinedSelector, elements, j, len, obj, part, selectors;
1436
+ var combinedSelector, elements, i, len, obj, part, selectors;
1488
1437
  obj = {};
1489
1438
  selectors = [];
1490
1439
  elements = [];
1491
- for (j = 0, len = parts.length; j < len; j++) {
1492
- part = parts[j];
1440
+ for (i = 0, len = parts.length; i < len; i++) {
1441
+ part = parts[i];
1493
1442
  if (isString(part)) {
1494
1443
  selectors.push(part);
1495
1444
  } else {
@@ -1505,11 +1454,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1505
1454
  return obj.find(void 0);
1506
1455
  };
1507
1456
  obj.find = function($root) {
1508
- var $matches, $result, k, len1, ref, selector;
1457
+ var $matches, $result, j, len1, ref, selector;
1509
1458
  $result = nullJQuery();
1510
1459
  ref = obj.parsed;
1511
- for (k = 0, len1 = ref.length; k < len1; k++) {
1512
- selector = ref[k];
1460
+ for (j = 0, len1 = ref.length; j < len1; j++) {
1461
+ selector = ref[j];
1513
1462
  $matches = $root ? $root.find(selector) : $(selector);
1514
1463
  $result = $result.add($matches);
1515
1464
  }
@@ -1577,7 +1526,7 @@ that might save you from loading something like [Underscore.js](http://underscor
1577
1526
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
1578
1527
  if (config.log) {
1579
1528
  args[0] = "[" + config.log + "] " + args[0];
1580
- return debug.apply(null, args);
1529
+ return up.puts.apply(up, args);
1581
1530
  }
1582
1531
  };
1583
1532
  keys = function() {
@@ -1635,7 +1584,9 @@ that might save you from loading something like [Underscore.js](http://underscor
1635
1584
  };
1636
1585
  alias = function(oldKey, newKey) {
1637
1586
  var value;
1638
- value = get(oldKey);
1587
+ value = get(oldKey, {
1588
+ silent: true
1589
+ });
1639
1590
  if (isDefined(value)) {
1640
1591
  return set(newKey, value);
1641
1592
  }
@@ -1666,24 +1617,30 @@ that might save you from loading something like [Underscore.js](http://underscor
1666
1617
  return true;
1667
1618
  }
1668
1619
  };
1669
- get = function(key, fallback) {
1620
+ get = function(key, options) {
1670
1621
  var entry, storeKey;
1671
- if (fallback == null) {
1672
- fallback = void 0;
1622
+ if (options == null) {
1623
+ options = {};
1673
1624
  }
1674
1625
  storeKey = normalizeStoreKey(key);
1675
1626
  if (entry = store[storeKey]) {
1676
1627
  if (isFresh(entry)) {
1677
- log("Cache hit for %o", key);
1628
+ if (!options.silent) {
1629
+ log("Cache hit for '%s'", key);
1630
+ }
1678
1631
  return entry.value;
1679
1632
  } else {
1680
- log("Discarding stale cache entry for %o", key);
1633
+ if (!options.silent) {
1634
+ log("Discarding stale cache entry for '%s'", key);
1635
+ }
1681
1636
  remove(key);
1682
- return fallback;
1637
+ return void 0;
1683
1638
  }
1684
1639
  } else {
1685
- log("Cache miss for %o", key);
1686
- return fallback;
1640
+ if (!options.silent) {
1641
+ log("Cache miss for '%s'", key);
1642
+ }
1643
+ return void 0;
1687
1644
  }
1688
1645
  };
1689
1646
  return {
@@ -1774,51 +1731,68 @@ that might save you from loading something like [Underscore.js](http://underscor
1774
1731
  @internal
1775
1732
  */
1776
1733
  requestDataAsArray = function(data) {
1777
- var name, results, value;
1778
- if (isMissing(data)) {
1779
- return [];
1780
- } else if (isArray(data)) {
1781
- return data;
1782
- } else if (isObject(data)) {
1783
- results = [];
1784
- for (name in data) {
1785
- value = data[name];
1786
- results.push({
1787
- name: name,
1788
- value: value
1734
+ var array, i, len, pair, part, query, ref;
1735
+ query = requestDataAsQuery(data);
1736
+ array = [];
1737
+ ref = query.split('&');
1738
+ for (i = 0, len = ref.length; i < len; i++) {
1739
+ part = ref[i];
1740
+ if (isPresent(part)) {
1741
+ pair = part.split('=');
1742
+ array.push({
1743
+ name: decodeURIComponent(pair[0]),
1744
+ value: decodeURIComponent(pair[1])
1789
1745
  });
1790
1746
  }
1791
- return results;
1792
- } else {
1793
- return error('Unknown options.data type for %o', data);
1794
1747
  }
1748
+ return array;
1795
1749
  };
1796
1750
 
1797
1751
  /**
1798
1752
  Returns an URL-encoded query string for the given params object.
1799
1753
 
1800
- @function up.util.requestDataAsQueryString
1754
+ @function up.util.requestDataAsQuery
1801
1755
  @param {Object|Array|Undefined|Null} data
1802
1756
  @internal
1803
1757
  */
1804
- requestDataAsQueryString = function(data) {
1805
- var array, query;
1806
- array = requestDataAsArray(data);
1807
- query = '';
1808
- if (isPresent(array)) {
1809
- query += '?';
1810
- each(array, function(field, index) {
1811
- if (index !== 0) {
1812
- query += '&';
1813
- }
1814
- return query += encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value);
1815
- });
1758
+ requestDataAsQuery = function(data) {
1759
+ var query;
1760
+ if (data) {
1761
+ query = $.param(data);
1762
+ query = query.replace(/\+/g, '%20');
1763
+ return query;
1764
+ } else {
1765
+ return "";
1816
1766
  }
1817
- return query;
1767
+ };
1768
+
1769
+ /**
1770
+ Throws a fatal error with the given message.
1771
+
1772
+ - The error will be printed to the [error console](https://developer.mozilla.org/en-US/docs/Web/API/Console/error)
1773
+ - An [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) (exception) will be thrown, unwinding the current call stack
1774
+ - The error message will be printed in a corner of the screen
1775
+
1776
+ \#\#\#\# Examples
1777
+
1778
+ up.error('Division by zero')
1779
+ up.error('Unexpected result %o', result)
1780
+
1781
+ @experimental
1782
+ */
1783
+ error = function() {
1784
+ var $error, args, asString, ref, ref1;
1785
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
1786
+ (ref = up.log).error.apply(ref, args);
1787
+ asString = (ref1 = up.browser).sprintf.apply(ref1, args);
1788
+ $error = presence($('.up-error')) || $('<div class="up-error"></div>').prependTo('body');
1789
+ $error.addClass('up-error');
1790
+ $error.text(asString);
1791
+ throw new Error(asString);
1818
1792
  };
1819
1793
  return {
1820
1794
  requestDataAsArray: requestDataAsArray,
1821
- requestDataAsQueryString: requestDataAsQueryString,
1795
+ requestDataAsQuery: requestDataAsQuery,
1822
1796
  offsetParent: offsetParent,
1823
1797
  fixedToAbsolute: fixedToAbsolute,
1824
1798
  presentAttr: presentAttr,
@@ -1828,6 +1802,7 @@ that might save you from loading something like [Underscore.js](http://underscor
1828
1802
  normalizeMethod: normalizeMethod,
1829
1803
  createElementFromHtml: createElementFromHtml,
1830
1804
  $createElementFromSelector: $createElementFromSelector,
1805
+ $createPlaceholder: $createPlaceholder,
1831
1806
  selectorForElement: selectorForElement,
1832
1807
  extend: extend,
1833
1808
  copy: copy,
@@ -1835,12 +1810,11 @@ that might save you from loading something like [Underscore.js](http://underscor
1835
1810
  options: options,
1836
1811
  option: option,
1837
1812
  error: error,
1838
- debug: debug,
1839
- warn: warn,
1840
1813
  each: each,
1841
1814
  map: map,
1842
1815
  times: times,
1843
1816
  any: any,
1817
+ all: all,
1844
1818
  detect: detect,
1845
1819
  select: select,
1846
1820
  reject: reject,
@@ -1901,15 +1875,112 @@ that might save you from loading something like [Underscore.js](http://underscor
1901
1875
  cache: cache,
1902
1876
  unwrapElement: unwrapElement,
1903
1877
  multiSelector: multiSelector,
1904
- evalConsoleTemplate: evalConsoleTemplate
1878
+ error: error
1905
1879
  };
1906
1880
  })($);
1907
1881
 
1908
1882
  up.error = up.util.error;
1909
1883
 
1910
- up.warn = up.util.warn;
1884
+ }).call(this);
1885
+ (function() {
1886
+ var slice = [].slice;
1887
+
1888
+ up.log = (function($) {
1889
+ var debug, error, group, prefix, puts, warn;
1890
+ prefix = function(message) {
1891
+ return "ᴜᴘ " + message;
1892
+ };
1893
+
1894
+ /**
1895
+ Prints a debugging message to the browser console.
1896
+
1897
+ @function up.debug
1898
+ @param {String} message
1899
+ @param {Array} args...
1900
+ @internal
1901
+ */
1902
+ debug = function() {
1903
+ var args, message, ref;
1904
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1905
+ if (message) {
1906
+ return (ref = up.browser).puts.apply(ref, ['debug', prefix(message)].concat(slice.call(args)));
1907
+ }
1908
+ };
1909
+
1910
+ /**
1911
+ Prints a logging message to the browser console.
1912
+
1913
+ @function up.puts
1914
+ @param {String} message
1915
+ @param {Array} args...
1916
+ @internal
1917
+ */
1918
+ puts = function() {
1919
+ var args, message, ref;
1920
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1921
+ if (message) {
1922
+ return (ref = up.browser).puts.apply(ref, ['log', prefix(message)].concat(slice.call(args)));
1923
+ }
1924
+ };
1911
1925
 
1912
- up.debug = up.util.debug;
1926
+ /**
1927
+ @function up.log.warn
1928
+ @internal
1929
+ */
1930
+ warn = function() {
1931
+ var args, message, ref;
1932
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1933
+ if (message) {
1934
+ return (ref = up.browser).puts.apply(ref, ['warn', prefix(message)].concat(slice.call(args)));
1935
+ }
1936
+ };
1937
+
1938
+ /**
1939
+ - Makes sure the group always closes
1940
+ - Does not make a group if the message is nil
1941
+
1942
+ @function up.log.group
1943
+ @internal
1944
+ */
1945
+ group = function() {
1946
+ var args, block, message, ref;
1947
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1948
+ block = args.pop();
1949
+ if (message) {
1950
+ (ref = up.browser).puts.apply(ref, ['groupCollapsed', prefix(message)].concat(slice.call(args)));
1951
+ try {
1952
+ return block();
1953
+ } finally {
1954
+ if (message) {
1955
+ console.groupEnd();
1956
+ }
1957
+ }
1958
+ } else {
1959
+ return block();
1960
+ }
1961
+ };
1962
+
1963
+ /**
1964
+ @function up.log.error
1965
+ @internal
1966
+ */
1967
+ error = function() {
1968
+ var args, message, ref;
1969
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
1970
+ if (message) {
1971
+ return (ref = up.browser).puts.apply(ref, ['error', prefix(message)].concat(slice.call(args)));
1972
+ }
1973
+ };
1974
+ return {
1975
+ puts: puts,
1976
+ debug: debug,
1977
+ error: error,
1978
+ warn: warn,
1979
+ group: group
1980
+ };
1981
+ })(jQuery);
1982
+
1983
+ up.puts = up.log.puts;
1913
1984
 
1914
1985
  }).call(this);
1915
1986
 
@@ -1927,7 +1998,7 @@ we can't currently get rid off.
1927
1998
  var slice = [].slice;
1928
1999
 
1929
2000
  up.browser = (function($) {
1930
- var canCssTransition, canInputEvent, canLogSubstitution, canPushState, confirm, initialRequestMethod, isIE8OrWorse, isIE9OrWorse, isRecentJQuery, isSupported, loadPage, popCookie, puts, u, url;
2001
+ var CONSOLE_PLACEHOLDERS, canCssTransition, canInputEvent, canLogSubstitution, canPushState, confirm, initialRequestMethod, installPolyfills, isIE8OrWorse, isIE9OrWorse, isRecentJQuery, isSupported, loadPage, popCookie, puts, sprintf, u, url;
1931
2002
  u = up.util;
1932
2003
 
1933
2004
  /**
@@ -1938,13 +2009,17 @@ we can't currently get rid off.
1938
2009
  @internal
1939
2010
  */
1940
2011
  loadPage = function(url, options) {
1941
- var $form, addField, csrfField, method;
2012
+ var $form, addField, csrfField, method, query;
1942
2013
  if (options == null) {
1943
2014
  options = {};
1944
2015
  }
1945
2016
  method = u.option(options.method, 'get').toLowerCase();
1946
2017
  if (method === 'get') {
1947
- return location.href = url + u.requestDataAsQueryString(options.data);
2018
+ query = u.requestDataAsQuery(options.data);
2019
+ if (query) {
2020
+ url = url + "?" + query;
2021
+ }
2022
+ return location.href = url;
1948
2023
  } else {
1949
2024
  $form = $("<form method='post' action='" + url + "'></form>");
1950
2025
  addField = function(field) {
@@ -1988,10 +2063,50 @@ we can't currently get rid off.
1988
2063
  if (canLogSubstitution()) {
1989
2064
  return console[stream].apply(console, args);
1990
2065
  } else {
1991
- message = u.evalConsoleTemplate.apply(u, args);
2066
+ message = sprintf.apply(null, args);
1992
2067
  return console[stream](message);
1993
2068
  }
1994
2069
  };
2070
+ CONSOLE_PLACEHOLDERS = /\%[odisf]/g;
2071
+
2072
+ /**
2073
+ See https://developer.mozilla.org/en-US/docs/Web/API/Console#Using_string_substitutions
2074
+
2075
+ @function up.browser.sprintf
2076
+ @internal
2077
+ */
2078
+ sprintf = function() {
2079
+ var args, i, maxLength, message;
2080
+ message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
2081
+ i = 0;
2082
+ maxLength = 80;
2083
+ return message.replace(CONSOLE_PLACEHOLDERS, function() {
2084
+ var arg, argType;
2085
+ arg = args[i];
2086
+ argType = typeof arg;
2087
+ if (argType === 'string') {
2088
+ arg = arg.replace(/\s+/g, ' ');
2089
+ if (arg.length > maxLength) {
2090
+ arg = (arg.substr(0, maxLength)) + "…";
2091
+ }
2092
+ arg = "\"" + arg + "\"";
2093
+ } else if (argType === 'undefined') {
2094
+ arg = 'undefined';
2095
+ } else if (argType === 'number' || argType === 'function') {
2096
+ arg = arg.toString();
2097
+ } else {
2098
+ arg = JSON.stringify(arg);
2099
+ }
2100
+ if (arg.length > maxLength) {
2101
+ arg = (arg.substr(0, maxLength)) + " …";
2102
+ if (argType === 'object' || argType === 'function') {
2103
+ arg += " }";
2104
+ }
2105
+ }
2106
+ i += 1;
2107
+ return arg;
2108
+ });
2109
+ };
1995
2110
  url = function() {
1996
2111
  return location.href;
1997
2112
  };
@@ -2113,6 +2228,27 @@ we can't currently get rid off.
2113
2228
  isSupported = function() {
2114
2229
  return (!isIE8OrWorse()) && isRecentJQuery();
2115
2230
  };
2231
+
2232
+ /**
2233
+ @internal
2234
+ */
2235
+ installPolyfills = function() {
2236
+ console.group || (console.group = function() {
2237
+ var args;
2238
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
2239
+ return puts.apply(null, ['group'].concat(slice.call(args)));
2240
+ });
2241
+ console.groupCollapsed || (console.groupCollapsed = function() {
2242
+ var args;
2243
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
2244
+ return puts.apply(null, ['groupCollapsed'].concat(slice.call(args)));
2245
+ });
2246
+ return console.groupEnd || (console.groupEnd = function() {
2247
+ var args;
2248
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
2249
+ return puts.apply(null, ['groupEnd'].concat(slice.call(args)));
2250
+ });
2251
+ };
2116
2252
  return {
2117
2253
  url: url,
2118
2254
  loadPage: loadPage,
@@ -2122,7 +2258,9 @@ we can't currently get rid off.
2122
2258
  canInputEvent: canInputEvent,
2123
2259
  canLogSubstitution: canLogSubstitution,
2124
2260
  isSupported: isSupported,
2125
- puts: puts
2261
+ installPolyfills: installPolyfills,
2262
+ puts: puts,
2263
+ sprintf: sprintf
2126
2264
  };
2127
2265
  })(jQuery);
2128
2266
 
@@ -2173,7 +2311,7 @@ and call `preventDefault()` on the `event` object:
2173
2311
  var slice = [].slice;
2174
2312
 
2175
2313
  up.bus = (function($) {
2176
- var boot, emit, emitReset, forgetUpDescription, live, liveUpDescriptions, nextUpDescriptionNumber, nobodyPrevents, onEscape, rememberUpDescription, restoreSnapshot, snapshot, u, unbind, upDescriptionNumber, upDescriptionToJqueryDescription, upListenerToJqueryListener;
2314
+ var boot, emit, emitReset, forgetUpDescription, live, liveUpDescriptions, logEmission, nextUpDescriptionNumber, nobodyPrevents, onEscape, rememberUpDescription, restoreSnapshot, snapshot, u, unbind, upDescriptionNumber, upDescriptionToJqueryDescription, upListenerToJqueryListener;
2177
2315
  u = up.util;
2178
2316
  liveUpDescriptions = {};
2179
2317
  nextUpDescriptionNumber = 0;
@@ -2372,6 +2510,10 @@ and call `preventDefault()` on the `event` object:
2372
2510
  or `stopPropagation()`.
2373
2511
  @param {jQuery} [eventProps.$element=$(document)]
2374
2512
  The element on which the event is triggered.
2513
+ @param {String|Array} [eventProps.message]
2514
+ A message to print to the console when the event is emitted.
2515
+ If omitted, a default message is printed.
2516
+ Set this to `false` to prevent any console output.
2375
2517
  @experimental
2376
2518
  */
2377
2519
  emit = function(eventName, eventProps) {
@@ -2380,11 +2522,40 @@ and call `preventDefault()` on the `event` object:
2380
2522
  eventProps = {};
2381
2523
  }
2382
2524
  event = $.Event(eventName, eventProps);
2383
- $target = eventProps.$element || $(document);
2384
- u.debug("Emitting %o on %o with props %o", eventName, $target, eventProps);
2525
+ if ($target = eventProps.$element) {
2526
+ delete eventProps.$element;
2527
+ } else {
2528
+ $target = $(document);
2529
+ }
2530
+ logEmission(eventName, eventProps);
2385
2531
  $target.trigger(event);
2386
2532
  return event;
2387
2533
  };
2534
+ logEmission = function(eventName, eventProps) {
2535
+ var niceMessage, niceMessageArgs, ref;
2536
+ if (eventProps.hasOwnProperty('message')) {
2537
+ niceMessage = eventProps.message;
2538
+ delete eventProps.message;
2539
+ if (u.isArray(niceMessage)) {
2540
+ ref = niceMessage, niceMessage = ref[0], niceMessageArgs = 2 <= ref.length ? slice.call(ref, 1) : [];
2541
+ } else {
2542
+ niceMessageArgs = [];
2543
+ }
2544
+ if (niceMessage) {
2545
+ if (u.isPresent(eventProps)) {
2546
+ return up.puts.apply(up, [niceMessage + " (%s (%o))"].concat(slice.call(niceMessageArgs), [eventName], [eventProps]));
2547
+ } else {
2548
+ return up.puts.apply(up, [niceMessage + " (%s)"].concat(slice.call(niceMessageArgs), [eventName]));
2549
+ }
2550
+ }
2551
+ } else {
2552
+ if (u.isPresent(eventProps)) {
2553
+ return up.puts('Emitted event %s (%o)', eventName, eventProps);
2554
+ } else {
2555
+ return up.puts('Emitted event %s', eventName);
2556
+ }
2557
+ }
2558
+ };
2388
2559
 
2389
2560
  /**
2390
2561
  [Emits an event](/up.emit) and returns whether any listener
@@ -2393,13 +2564,19 @@ and call `preventDefault()` on the `event` object:
2393
2564
  @function up.bus.nobodyPrevents
2394
2565
  @param {String} eventName
2395
2566
  @param {Object} eventProps
2567
+ @param {String|Array} [eventProps.message]
2396
2568
  @experimental
2397
2569
  */
2398
2570
  nobodyPrevents = function() {
2399
2571
  var args, event;
2400
2572
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
2401
2573
  event = emit.apply(null, args);
2402
- return !event.isDefaultPrevented();
2574
+ if (event.isDefaultPrevented()) {
2575
+ up.puts("An observer prevented the event %s", args[0]);
2576
+ return false;
2577
+ } else {
2578
+ return true;
2579
+ }
2403
2580
  };
2404
2581
 
2405
2582
  /**
@@ -2432,7 +2609,7 @@ and call `preventDefault()` on the `event` object:
2432
2609
  results = [];
2433
2610
  for (i = 0, len = liveUpDescriptions.length; i < len; i++) {
2434
2611
  description = liveUpDescriptions[i];
2435
- results.push(description._isDefault = true);
2612
+ results.push(description.isDefault = true);
2436
2613
  }
2437
2614
  return results;
2438
2615
  };
@@ -2446,7 +2623,7 @@ and call `preventDefault()` on the `event` object:
2446
2623
  restoreSnapshot = function() {
2447
2624
  var description, doomedDescriptions, i, len, results;
2448
2625
  doomedDescriptions = u.reject(liveUpDescriptions, function(description) {
2449
- return description._isDefault;
2626
+ return description.isDefault;
2450
2627
  });
2451
2628
  results = [];
2452
2629
  for (i = 0, len = doomedDescriptions.length; i < len; i++) {
@@ -2468,7 +2645,9 @@ and call `preventDefault()` on the `event` object:
2468
2645
  @experimental
2469
2646
  */
2470
2647
  emitReset = function() {
2471
- return up.emit('up:framework:reset');
2648
+ return up.emit('up:framework:reset', {
2649
+ message: 'Resetting framework'
2650
+ });
2472
2651
  };
2473
2652
 
2474
2653
  /**
@@ -2493,7 +2672,10 @@ and call `preventDefault()` on the `event` object:
2493
2672
  */
2494
2673
  boot = function() {
2495
2674
  if (up.browser.isSupported()) {
2496
- return up.emit('up:framework:boot');
2675
+ up.browser.installPolyfills();
2676
+ return up.emit('up:framework:boot', {
2677
+ message: 'Booting framework'
2678
+ });
2497
2679
  }
2498
2680
  };
2499
2681
 
@@ -2568,7 +2750,7 @@ later.
2568
2750
  var slice = [].slice;
2569
2751
 
2570
2752
  up.syntax = (function($) {
2571
- var DESTROYABLE_CLASS, DESTROYER_KEY, applyCompiler, compile, compiler, compilers, data, defaultCompilers, hello, reset, runDestroyers, snapshot, u;
2753
+ var DESTROYABLE_CLASS, DESTROYER_KEY, applyCompiler, clean, compile, compiler, compilers, data, reset, snapshot, u;
2572
2754
  u = up.util;
2573
2755
  DESTROYABLE_CLASS = 'up-destroyable';
2574
2756
  DESTROYER_KEY = 'up-destroyer';
@@ -2714,6 +2896,11 @@ later.
2714
2896
  If set to `true` and a fragment insertion contains multiple
2715
2897
  elements matching the selector, `compiler` is only called once
2716
2898
  with a jQuery collection containing all matching elements.
2899
+ @param {Boolean} [options.keep=false]
2900
+ If set to `true` compiled fragment will be [persisted](/up-keep) during
2901
+ [page updates](/a-up-target).
2902
+
2903
+ This has the same effect as setting an `up-keep` attribute on the element.
2717
2904
  @param {Function($element, data)} compiler
2718
2905
  The function to call when a matching element is inserted.
2719
2906
  The function takes the new element as the first argument (as a jQuery object).
@@ -2728,7 +2915,6 @@ later.
2728
2915
  @stable
2729
2916
  */
2730
2917
  compilers = [];
2731
- defaultCompilers = null;
2732
2918
  compiler = function() {
2733
2919
  var args, options, selector;
2734
2920
  selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
@@ -2736,46 +2922,81 @@ later.
2736
2922
  return;
2737
2923
  }
2738
2924
  compiler = args.pop();
2739
- options = u.options(args[0], {
2740
- batch: false
2741
- });
2925
+ options = u.options(args[0]);
2742
2926
  return compilers.push({
2743
2927
  selector: selector,
2744
2928
  callback: compiler,
2745
- batch: options.batch
2929
+ batch: options.batch,
2930
+ keep: options.keep
2746
2931
  });
2747
2932
  };
2748
2933
  applyCompiler = function(compiler, $jqueryElement, nativeElement) {
2749
- var destroyer;
2750
- u.debug("Applying compiler %o on %o", compiler.selector, nativeElement);
2934
+ var destroyer, value;
2935
+ up.puts((!compiler.isDefault ? "Compiling '%s' on %o" : void 0), compiler.selector, nativeElement);
2936
+ if (compiler.keep) {
2937
+ value = u.isString(compiler.keep) ? compiler.keep : '';
2938
+ $jqueryElement.attr('up-keep', value);
2939
+ }
2751
2940
  destroyer = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)]);
2752
2941
  if (u.isFunction(destroyer)) {
2753
2942
  $jqueryElement.addClass(DESTROYABLE_CLASS);
2754
2943
  return $jqueryElement.data(DESTROYER_KEY, destroyer);
2755
- }
2756
- };
2757
- compile = function($fragment) {
2758
- var $matches, i, len, results;
2759
- u.debug("Compiling fragment %o", $fragment);
2760
- results = [];
2761
- for (i = 0, len = compilers.length; i < len; i++) {
2762
- compiler = compilers[i];
2763
- $matches = u.findWithSelf($fragment, compiler.selector);
2764
- if ($matches.length) {
2765
- if (compiler.batch) {
2766
- results.push(applyCompiler(compiler, $matches, $matches.get()));
2767
- } else {
2768
- results.push($matches.each(function() {
2769
- return applyCompiler(compiler, $(this), this);
2944
+ }
2945
+ };
2946
+
2947
+ /**
2948
+ Applies all compilers on the given element and its descendants.
2949
+ Unlike [`up.hello`](/up.hello), this doesn't emit any events.
2950
+
2951
+ @function up.syntax.compile
2952
+ @param {Array<Element>} [options.skip]
2953
+ A list of elements whose subtrees should not be compiled.
2954
+ @internal
2955
+ */
2956
+ compile = function($fragment, options) {
2957
+ var $skipSubtrees;
2958
+ options = u.options(options);
2959
+ $skipSubtrees = $(options.skip);
2960
+ return up.log.group("Compiling fragment %o", $fragment.get(0), function() {
2961
+ var $matches, i, len, results;
2962
+ results = [];
2963
+ for (i = 0, len = compilers.length; i < len; i++) {
2964
+ compiler = compilers[i];
2965
+ $matches = u.findWithSelf($fragment, compiler.selector);
2966
+ $matches = $matches.filter(function() {
2967
+ var $match;
2968
+ $match = $(this);
2969
+ return u.all($skipSubtrees, function(element) {
2970
+ return $match.closest(element).length === 0;
2971
+ });
2972
+ });
2973
+ if ($matches.length) {
2974
+ results.push(up.log.group((!compiler.isDefault ? "Compiling '%s' on %d element(s)" : void 0), compiler.selector, $matches.length, function() {
2975
+ if (compiler.batch) {
2976
+ return applyCompiler(compiler, $matches, $matches.get());
2977
+ } else {
2978
+ return $matches.each(function() {
2979
+ return applyCompiler(compiler, $(this), this);
2980
+ });
2981
+ }
2770
2982
  }));
2983
+ } else {
2984
+ results.push(void 0);
2771
2985
  }
2772
- } else {
2773
- results.push(void 0);
2774
2986
  }
2775
- }
2776
- return results;
2987
+ return results;
2988
+ });
2777
2989
  };
2778
- runDestroyers = function($fragment) {
2990
+
2991
+ /**
2992
+ Runs any destroyers on the given fragment and its descendants.
2993
+ Unlike [`up.destroy`](/up.destroy), this doesn't emit any events
2994
+ and does not remove the element from the DOM.
2995
+
2996
+ @function up.syntax.clean
2997
+ @internal
2998
+ */
2999
+ clean = function($fragment) {
2779
3000
  return u.findWithSelf($fragment, "." + DESTROYABLE_CLASS).each(function() {
2780
3001
  var $element, destroyer;
2781
3002
  $element = $(this);
@@ -2831,7 +3052,13 @@ later.
2831
3052
  @internal
2832
3053
  */
2833
3054
  snapshot = function() {
2834
- return defaultCompilers = u.copy(compilers);
3055
+ var i, len, results;
3056
+ results = [];
3057
+ for (i = 0, len = compilers.length; i < len; i++) {
3058
+ compiler = compilers[i];
3059
+ results.push(compiler.isDefault = true);
3060
+ }
3061
+ return results;
2835
3062
  };
2836
3063
 
2837
3064
  /**
@@ -2841,80 +3068,22 @@ later.
2841
3068
  @internal
2842
3069
  */
2843
3070
  reset = function() {
2844
- return compilers = u.copy(defaultCompilers);
2845
- };
2846
-
2847
- /**
2848
- Compiles a page fragment that has been inserted into the DOM
2849
- without Up.js.
2850
-
2851
- **As long as you manipulate the DOM using Up.js, you will never
2852
- need to call this method.** You only need to use `up.hello` if the
2853
- DOM is manipulated without Up.js' involvement, e.g. by setting
2854
- the `innerHTML` property or calling jQuery methods like
2855
- `html`, `insertAfter` or `appendTo`:
2856
-
2857
- $element = $('.element');
2858
- $element.html('<div>...</div>');
2859
- up.hello($element);
2860
-
2861
- This function emits the [`up:fragment:inserted`](/up:fragment:inserted)
2862
- event.
2863
-
2864
- @function up.hello
2865
- @param {String|Element|jQuery} selectorOrElement
2866
- @param {String|Element|jQuery} [options.origin]
2867
- @return {jQuery}
2868
- The compiled element
2869
- @stable
2870
- */
2871
- hello = function(selectorOrElement, options) {
2872
- var $element, eventAttrs;
2873
- $element = $(selectorOrElement);
2874
- eventAttrs = u.options(options, {
2875
- $element: $element
3071
+ return compilers = u.select(compilers, function(compiler) {
3072
+ return compiler.isDefault;
2876
3073
  });
2877
- up.emit('up:fragment:inserted', eventAttrs);
2878
- return $element;
2879
3074
  };
2880
-
2881
- /**
2882
- When a page fragment has been [inserted or updated](/up.replace),
2883
- this event is [emitted](/up.emit) on the fragment.
2884
-
2885
- \#\#\#\# Example
2886
-
2887
- up.on('up:fragment:inserted', function(event, $fragment) {
2888
- console.log("Looks like we have a new %o!", $fragment);
2889
- });
2890
-
2891
- @event up:fragment:inserted
2892
- @param {jQuery} event.$element
2893
- The fragment that has been inserted or updated.
2894
- @stable
2895
- */
2896
- up.on('ready', (function() {
2897
- return hello(document.body);
2898
- }));
2899
- up.on('up:fragment:inserted', function(event, $element) {
2900
- return compile($element);
2901
- });
2902
- up.on('up:fragment:destroy', function(event, $element) {
2903
- return runDestroyers($element);
2904
- });
2905
3075
  up.on('up:framework:boot', snapshot);
2906
3076
  up.on('up:framework:reset', reset);
2907
3077
  return {
2908
3078
  compiler: compiler,
2909
- hello: hello,
3079
+ compile: compile,
3080
+ clean: clean,
2910
3081
  data: data
2911
3082
  };
2912
3083
  })(jQuery);
2913
3084
 
2914
3085
  up.compiler = up.syntax.compiler;
2915
3086
 
2916
- up.hello = up.syntax.hello;
2917
-
2918
3087
  up.ready = function() {
2919
3088
  return up.util.error('up.ready no longer exists. Please use up.hello instead.');
2920
3089
  };
@@ -3041,6 +3210,7 @@ We need to work on this page:
3041
3210
  @experimental
3042
3211
  */
3043
3212
  push = function(url, options) {
3213
+ up.puts("Current location is now %s", url);
3044
3214
  return manipulate('push', url, options);
3045
3215
  };
3046
3216
  manipulate = function(method, url, options) {
@@ -3052,7 +3222,6 @@ We need to work on this page:
3052
3222
  if (up.browser.canPushState()) {
3053
3223
  fullMethod = method + "State";
3054
3224
  state = buildState();
3055
- u.debug("Changing history to URL %o (%o)", url, method);
3056
3225
  window.history[fullMethod](state, '', url);
3057
3226
  return observeNewUrl(currentUrl());
3058
3227
  } else {
@@ -3066,32 +3235,35 @@ We need to work on this page:
3066
3235
  };
3067
3236
  };
3068
3237
  restoreStateOnPop = function(state) {
3069
- var popSelector, url;
3070
- url = currentUrl();
3071
- u.debug("Restoring state %o (now on " + url + ")", state);
3072
- popSelector = config.popTargets.join(', ');
3073
- return up.replace(popSelector, url, {
3074
- history: false,
3075
- reveal: false,
3076
- transition: 'none',
3077
- saveScroll: false,
3078
- restoreScroll: config.restoreScroll
3079
- });
3080
- };
3081
- pop = function(event) {
3082
- var state;
3083
- u.debug("History state popped to URL %o", currentUrl());
3084
- observeNewUrl(currentUrl());
3085
- up.layout.saveScroll({
3086
- url: previousUrl
3087
- });
3088
- state = event.originalEvent.state;
3238
+ var url;
3089
3239
  if (state != null ? state.fromUp : void 0) {
3090
- return restoreStateOnPop(state);
3240
+ url = currentUrl();
3241
+ return up.log.group("Restoring URL %s", url, function() {
3242
+ var popSelector;
3243
+ popSelector = config.popTargets.join(', ');
3244
+ return up.replace(popSelector, url, {
3245
+ history: false,
3246
+ reveal: false,
3247
+ transition: 'none',
3248
+ saveScroll: false,
3249
+ restoreScroll: config.restoreScroll
3250
+ });
3251
+ });
3091
3252
  } else {
3092
- return u.debug('Discarding unknown state %o', state);
3253
+ return up.puts('Ignoring a state not pushed by Up.js (%o)', state);
3093
3254
  }
3094
3255
  };
3256
+ pop = function(event) {
3257
+ return up.log.group("History state popped to URL %s", currentUrl(), function() {
3258
+ var state;
3259
+ observeNewUrl(currentUrl());
3260
+ up.layout.saveScroll({
3261
+ url: previousUrl
3262
+ });
3263
+ state = event.originalEvent.state;
3264
+ return restoreStateOnPop(state);
3265
+ });
3266
+ };
3095
3267
  if (up.browser.canPushState()) {
3096
3268
  register = function() {
3097
3269
  $(window).on("popstate", pop);
@@ -3324,7 +3496,7 @@ This modules contains functions to scroll the viewport and reveal contained elem
3324
3496
  $obstructor = $(obstructor);
3325
3497
  anchorPosition = $obstructor.css(cssAttr);
3326
3498
  if (!u.isPresent(anchorPosition)) {
3327
- u.error("Fixed element %o must have a CSS attribute %o", $obstructor, cssAttr);
3499
+ u.error("Fixed element %o must have a CSS attribute %s", $obstructor.get(0), cssAttr);
3328
3500
  }
3329
3501
  return parseInt(anchorPosition) + $obstructor.height();
3330
3502
  };
@@ -3399,9 +3571,9 @@ This modules contains functions to scroll the viewport and reveal contained elem
3399
3571
  */
3400
3572
  reveal = function(elementOrSelector, options) {
3401
3573
  var $element, $viewport, elementDims, firstElementRow, lastElementRow, newScrollPos, obstruction, offsetShift, originalScrollPos, predictFirstVisibleRow, predictLastVisibleRow, snap, viewportHeight, viewportIsDocument;
3402
- u.debug('Revealing %o', elementOrSelector);
3403
- options = u.options(options);
3404
3574
  $element = $(elementOrSelector);
3575
+ up.puts('Revealing fragment %o', elementOrSelector.get(0));
3576
+ options = u.options(options);
3405
3577
  $viewport = options.viewport ? $(options.viewport) : viewportOf($element);
3406
3578
  snap = u.option(options.snap, config.snap);
3407
3579
  viewportIsDocument = $viewport.is(document);
@@ -3566,7 +3738,7 @@ This modules contains functions to scroll the viewport and reveal contained elem
3566
3738
  }
3567
3739
  url = u.option(options.url, up.history.url());
3568
3740
  tops = u.option(options.tops, scrollTops());
3569
- u.debug('Saving scroll positions for URL %o: %o', url, tops);
3741
+ up.puts('Saving scroll positions for URL %s (%o)', url, tops);
3570
3742
  return lastScrollTops.set(url, tops);
3571
3743
  };
3572
3744
 
@@ -3584,7 +3756,7 @@ This modules contains functions to scroll the viewport and reveal contained elem
3584
3756
  @experimental
3585
3757
  */
3586
3758
  restoreScroll = function(options) {
3587
- var $ancestorViewports, $descendantViewports, $matchingViewport, $viewports, key, right, scrollTop, tops, url;
3759
+ var $ancestorViewports, $descendantViewports, $viewports, tops, url;
3588
3760
  if (options == null) {
3589
3761
  options = {};
3590
3762
  }
@@ -3598,16 +3770,18 @@ This modules contains functions to scroll the viewport and reveal contained elem
3598
3770
  $viewports = viewports();
3599
3771
  }
3600
3772
  tops = lastScrollTops.get(url);
3601
- u.debug('Restoring scroll positions for URL %o (viewports are %o, saved tops are %o)', url, $viewports, tops);
3602
- for (key in tops) {
3603
- scrollTop = tops[key];
3604
- right = key === 'document' ? document : key;
3605
- $matchingViewport = $viewports.filter(right);
3606
- scroll($matchingViewport, scrollTop, {
3607
- duration: 0
3608
- });
3609
- }
3610
- return u.resolvedDeferred();
3773
+ return up.log.group('Restoring scroll positions for URL %s to %o', url, tops, function() {
3774
+ var $matchingViewport, key, right, scrollTop;
3775
+ for (key in tops) {
3776
+ scrollTop = tops[key];
3777
+ right = key === 'document' ? document : key;
3778
+ $matchingViewport = $viewports.filter(right);
3779
+ scroll($matchingViewport, scrollTop, {
3780
+ duration: 0
3781
+ });
3782
+ }
3783
+ return u.resolvedDeferred();
3784
+ });
3611
3785
  };
3612
3786
 
3613
3787
  /**
@@ -3765,7 +3939,7 @@ are based on this module.
3765
3939
 
3766
3940
  (function() {
3767
3941
  up.flow = (function($) {
3768
- var autofocus, destroy, elementsInserted, findOldFragment, first, implant, isRealElement, oldFragmentNotFound, parseImplantSteps, parseResponse, processResponse, reload, replace, resolveSelector, setSource, source, swapElements, u;
3942
+ var autofocus, destroy, emitFragmentInserted, emitFragmentKept, extract, findKeepPlan, findOldFragment, first, hello, isRealElement, oldFragmentNotFound, parseImplantSteps, parseResponse, processResponse, reload, replace, resolveSelector, setSource, source, swapElements, transferKeepableElements, u, updateHistory;
3769
3943
  u = up.util;
3770
3944
  setSource = function(element, sourceUrl) {
3771
3945
  var $element;
@@ -3809,7 +3983,7 @@ are based on this module.
3809
3983
  originSelector = u.selectorForElement(origin);
3810
3984
  selector = selector.replace(/\&/, originSelector);
3811
3985
  } else {
3812
- u.error("Found origin reference %o in selector %o, but options.origin is missing", '&', selector);
3986
+ u.error("Found origin reference (%s) in selector %s, but options.origin is missing", '&', selector);
3813
3987
  }
3814
3988
  }
3815
3989
  } else {
@@ -3822,7 +3996,9 @@ are based on this module.
3822
3996
  Replaces elements on the current page with corresponding elements
3823
3997
  from a new page fetched from the server.
3824
3998
 
3825
- The current and new elements must have the same CSS selector.
3999
+ The current and new elements must both match the given CSS selector.
4000
+
4001
+ The UJS variant of this is the [`a[up-target]`](/a-up-target) selector.
3826
4002
 
3827
4003
  \#\#\#\# Example
3828
4004
 
@@ -3840,7 +4016,7 @@ are based on this module.
3840
4016
  <div class="one">new one</div>
3841
4017
  <div class="two">new two</div>
3842
4018
 
3843
- Up.js looks for the selector `.two` in the response and [implants](/up.implant) it into
4019
+ Up.js looks for the selector `.two` in the response and [implants](/up.extract) it into
3844
4020
  the current page. The current page now looks like this:
3845
4021
 
3846
4022
  <div class="one">old one</div>
@@ -3942,7 +4118,7 @@ are based on this module.
3942
4118
  */
3943
4119
  replace = function(selectorOrElement, url, options) {
3944
4120
  var failTarget, promise, request, target;
3945
- u.debug("Replace %o with %o (options %o)", selectorOrElement, url, options);
4121
+ up.puts("Replacing %s from %s (%o)", selectorOrElement, url, options);
3946
4122
  options = u.options(options);
3947
4123
  target = resolveSelector(selectorOrElement, options.origin);
3948
4124
  failTarget = u.option(options.failTarget, 'body');
@@ -3963,7 +4139,7 @@ are based on this module.
3963
4139
  preload: options.preload,
3964
4140
  headers: options.headers
3965
4141
  };
3966
- promise = up.proxy.ajax(request);
4142
+ promise = up.ajax(request);
3967
4143
  promise.done(function(html, textStatus, xhr) {
3968
4144
  return processResponse(true, target, url, request, xhr, options);
3969
4145
  });
@@ -3977,7 +4153,7 @@ are based on this module.
3977
4153
  @internal
3978
4154
  */
3979
4155
  processResponse = function(isSuccess, selector, url, request, xhr, options) {
3980
- var isReloadable, newRequest, urlFromServer;
4156
+ var isReloadable, newRequest, query, urlFromServer;
3981
4157
  options.method = u.normalizeMethod(u.option(u.methodFromXhr(xhr), options.method));
3982
4158
  options.title = u.option(u.titleFromXhr(xhr), options.title);
3983
4159
  isReloadable = options.method === 'GET';
@@ -3992,7 +4168,9 @@ are based on this module.
3992
4168
  up.proxy.alias(request, newRequest);
3993
4169
  }
3994
4170
  } else if (isReloadable) {
3995
- url = url + u.requestDataAsQueryString(options.data);
4171
+ if (query = u.requestDataAsQuery(options.data)) {
4172
+ url = url + "?" + query;
4173
+ }
3996
4174
  }
3997
4175
  if (isSuccess) {
3998
4176
  if (isReloadable) {
@@ -4028,7 +4206,7 @@ are based on this module.
4028
4206
  if (options.preload) {
4029
4207
  return u.resolvedPromise();
4030
4208
  } else {
4031
- return implant(selector, xhr.responseText, options);
4209
+ return extract(selector, xhr.responseText, options);
4032
4210
  }
4033
4211
  };
4034
4212
 
@@ -4049,7 +4227,7 @@ are based on this module.
4049
4227
  html = '<div class="one">new one</div>' +
4050
4228
  '<div class="two">new two</div>';
4051
4229
 
4052
- up.flow.implant('.two', html);
4230
+ up.extract('.two', html);
4053
4231
 
4054
4232
  Up.js looks for the selector `.two` in the strings and updates its
4055
4233
  contents in the current page. The current page now looks like this:
@@ -4060,7 +4238,7 @@ are based on this module.
4060
4238
  Note how only `.two` has changed. The update for `.one` was
4061
4239
  discarded, since it didn't match the selector.
4062
4240
 
4063
- @function up.implant
4241
+ @function up.extract
4064
4242
  @param {String|Element|jQuery} selectorOrElement
4065
4243
  @param {String} html
4066
4244
  @param {Object} [options]
@@ -4070,36 +4248,43 @@ are based on this module.
4070
4248
  and all animation has finished.
4071
4249
  @experimental
4072
4250
  */
4073
- implant = function(selectorOrElement, html, options) {
4074
- var $new, $old, deferred, deferreds, j, len, ref, ref1, ref2, response, selector, step;
4075
- options = u.options(options, {
4076
- historyMethod: 'push',
4077
- requireMatch: true
4078
- });
4079
- selector = resolveSelector(selectorOrElement, options.origin);
4080
- response = parseResponse(html, options);
4081
- options.title || (options.title = response.title());
4082
- if (options.saveScroll !== false) {
4083
- up.layout.saveScroll();
4084
- }
4085
- if (typeof options.beforeSwap === "function") {
4086
- options.beforeSwap($old, $new);
4087
- }
4088
- deferreds = [];
4089
- ref = parseImplantSteps(selector, options);
4090
- for (j = 0, len = ref.length; j < len; j++) {
4091
- step = ref[j];
4092
- $old = findOldFragment(step.selector, options);
4093
- $new = (ref1 = response.find(step.selector)) != null ? ref1.first() : void 0;
4094
- if ($old && $new) {
4095
- deferred = swapElements($old, $new, step.pseudoClass, step.transition, options);
4096
- deferreds.push(deferred);
4251
+ extract = function(selectorOrElement, html, options) {
4252
+ return up.log.group('Extracting %s from %d bytes of HTML', selectorOrElement, html != null ? html.length : void 0, function() {
4253
+ var deferreds, j, len, ref, ref1, response, selector, step;
4254
+ options = u.options(options, {
4255
+ historyMethod: 'push',
4256
+ requireMatch: true,
4257
+ keep: true
4258
+ });
4259
+ selector = resolveSelector(selectorOrElement, options.origin);
4260
+ response = parseResponse(html, options);
4261
+ options.title || (options.title = response.title());
4262
+ if (options.saveScroll !== false) {
4263
+ up.layout.saveScroll();
4097
4264
  }
4098
- }
4099
- if (typeof options.afterSwap === "function") {
4100
- options.afterSwap($old, $new);
4101
- }
4102
- return (ref2 = up.motion).when.apply(ref2, deferreds);
4265
+ if (typeof options.beforeSwap === "function") {
4266
+ options.beforeSwap();
4267
+ }
4268
+ deferreds = [];
4269
+ updateHistory(options);
4270
+ ref = parseImplantSteps(selector, options);
4271
+ for (j = 0, len = ref.length; j < len; j++) {
4272
+ step = ref[j];
4273
+ up.log.group('Updating %s', step.selector, function() {
4274
+ var $new, $old, deferred, ref1;
4275
+ $old = findOldFragment(step.selector, options);
4276
+ $new = (ref1 = response.find(step.selector)) != null ? ref1.first() : void 0;
4277
+ if ($old && $new) {
4278
+ deferred = swapElements($old, $new, step.pseudoClass, step.transition, options);
4279
+ return deferreds.push(deferred);
4280
+ }
4281
+ });
4282
+ }
4283
+ if (typeof options.afterSwap === "function") {
4284
+ options.afterSwap();
4285
+ }
4286
+ return (ref1 = up.motion).when.apply(ref1, deferreds);
4287
+ });
4103
4288
  };
4104
4289
  findOldFragment = function(selector, options) {
4105
4290
  return first(".up-popup " + selector) || first(".up-modal " + selector) || first(selector) || oldFragmentNotFound(selector, options);
@@ -4107,7 +4292,7 @@ are based on this module.
4107
4292
  oldFragmentNotFound = function(selector, options) {
4108
4293
  var message;
4109
4294
  if (options.requireMatch) {
4110
- message = 'Could not find selector %o in current body HTML';
4295
+ message = 'Could not find selector %s in current body HTML';
4111
4296
  if (message[0] === '#') {
4112
4297
  message += ' (avoid using IDs)';
4113
4298
  }
@@ -4127,28 +4312,21 @@ are based on this module.
4127
4312
  if (child = $.find(selector, htmlElement)[0]) {
4128
4313
  return $(child);
4129
4314
  } else if (options.requireMatch) {
4130
- return u.error("Could not find selector %o in response %o", selector, html);
4315
+ return u.error("Could not find selector %s in response %o", selector, html);
4131
4316
  }
4132
4317
  }
4133
4318
  };
4134
4319
  };
4135
- elementsInserted = function($new, options) {
4320
+ updateHistory = function(options) {
4136
4321
  if (options.history) {
4137
4322
  if (options.title) {
4138
4323
  document.title = options.title;
4139
4324
  }
4140
- up.history[options.historyMethod](options.history);
4325
+ return up.history[options.historyMethod](options.history);
4141
4326
  }
4142
- if (options.source !== false) {
4143
- setSource($new, options.source);
4144
- }
4145
- autofocus($new);
4146
- return up.hello($new, {
4147
- origin: options.origin
4148
- });
4149
4327
  };
4150
4328
  swapElements = function($old, $new, pseudoClass, transition, options) {
4151
- var $wrapper, insertionMethod, promise, replacement;
4329
+ var $wrapper, keepPlan, promise, replacement;
4152
4330
  transition || (transition = 'none');
4153
4331
  if (options.source === 'keep') {
4154
4332
  options = u.merge(options, {
@@ -4157,11 +4335,13 @@ are based on this module.
4157
4335
  }
4158
4336
  up.motion.finish($old);
4159
4337
  if (pseudoClass) {
4160
- insertionMethod = pseudoClass === 'before' ? 'prepend' : 'append';
4161
4338
  $wrapper = $new.contents().wrap('<span class="up-insertion"></span>').parent();
4162
- $old[insertionMethod]($wrapper);
4163
- u.copyAttributes($new, $old);
4164
- elementsInserted($wrapper.children(), options);
4339
+ if (pseudoClass === 'before') {
4340
+ $old.prepend($wrapper);
4341
+ } else {
4342
+ $old.append($wrapper);
4343
+ }
4344
+ hello($wrapper.children(), options);
4165
4345
  promise = up.layout.revealOrRestoreScroll($wrapper, options);
4166
4346
  promise = promise.then(function() {
4167
4347
  return up.animate($wrapper, transition, options);
@@ -4169,34 +4349,179 @@ are based on this module.
4169
4349
  promise = promise.then(function() {
4170
4350
  return u.unwrapElement($wrapper);
4171
4351
  });
4172
- return promise;
4352
+ } else if (keepPlan = findKeepPlan($old, $new, options)) {
4353
+ emitFragmentKept(keepPlan);
4354
+ promise = u.resolvedPromise();
4173
4355
  } else {
4174
4356
  replacement = function() {
4357
+ options.keepPlans = transferKeepableElements($old, $new, options);
4175
4358
  $new.insertBefore($old);
4176
- elementsInserted($new, options);
4177
- if ($old.is('body') && transition !== 'none') {
4178
- u.error('Cannot apply transitions to body-elements (%o)', transition);
4359
+ if (options.source !== false) {
4360
+ setSource($new, options.source);
4179
4361
  }
4362
+ autofocus($new);
4363
+ hello($new, options);
4180
4364
  return up.morph($old, $new, transition, options);
4181
4365
  };
4182
- return destroy($old, {
4366
+ promise = destroy($old, {
4183
4367
  animation: replacement
4184
4368
  });
4185
4369
  }
4370
+ return promise;
4371
+ };
4372
+ transferKeepableElements = function($old, $new, options) {
4373
+ var $keepable, $keepableClone, j, keepPlans, keepable, len, plan, ref;
4374
+ keepPlans = [];
4375
+ if (options.keep) {
4376
+ ref = $old.find('[up-keep]');
4377
+ for (j = 0, len = ref.length; j < len; j++) {
4378
+ keepable = ref[j];
4379
+ $keepable = $(keepable);
4380
+ if (plan = findKeepPlan($keepable, $new, u.merge(options, {
4381
+ descendantsOnly: true
4382
+ }))) {
4383
+ $keepableClone = $keepable.clone();
4384
+ $keepable.replaceWith($keepableClone);
4385
+ plan.$newElement.replaceWith($keepable);
4386
+ keepPlans.push(plan);
4387
+ }
4388
+ }
4389
+ }
4390
+ return keepPlans;
4391
+ };
4392
+ findKeepPlan = function($element, $new, options) {
4393
+ var $keepable, $partner, description, keepEventArgs, partnerSelector;
4394
+ if (options.keep) {
4395
+ $keepable = $element;
4396
+ if (partnerSelector = u.castedAttr($keepable, 'up-keep')) {
4397
+ u.isString(partnerSelector) || (partnerSelector = '&');
4398
+ partnerSelector = resolveSelector(partnerSelector, $keepable);
4399
+ if (options.descendantsOnly) {
4400
+ $partner = $new.find(partnerSelector);
4401
+ } else {
4402
+ $partner = u.findWithSelf($new, partnerSelector);
4403
+ }
4404
+ $partner = $partner.first();
4405
+ if ($partner.length && $partner.is('[up-keep]')) {
4406
+ description = {
4407
+ $element: $keepable,
4408
+ $newElement: $partner,
4409
+ newData: up.syntax.data($partner)
4410
+ };
4411
+ keepEventArgs = u.merge(description, {
4412
+ message: ['Keeping element %o', $keepable.get(0)]
4413
+ });
4414
+ if (up.bus.nobodyPrevents('up:fragment:keep', keepEventArgs)) {
4415
+ return description;
4416
+ }
4417
+ }
4418
+ }
4419
+ }
4186
4420
  };
4421
+
4422
+ /**
4423
+ Elements with an `up-keep` attribute will be persisted during
4424
+ [fragment updates](/a-up-target).
4425
+
4426
+ For example:
4427
+
4428
+ <audio up-keep src="song.mp3"></audio>
4429
+
4430
+ The element you're keeping should have an umambiguous class name, ID or `up-id`
4431
+ attribute so Up.js can find its new position within the page update.
4432
+
4433
+ Emits events [`up:fragment:keep`](/up:fragment:keep) and [`up:fragment:kept`](/up:fragment:kept).
4434
+
4435
+ \#\#\#\# Controlling if an element will be kept
4436
+
4437
+ Up.js will **only** keep an existing element if:
4438
+
4439
+ - The existing element has an `up-keep` attribute
4440
+ - The response contains an element matching the CSS selector of the existing element
4441
+ - The matching element *also* has an `up-keep` attribute
4442
+ - The [`up:fragment:keep`](/up:fragment:keep) event that is [emitted](/up.emit) on the existing element
4443
+ is not prevented by a event listener.
4444
+
4445
+ Let's say we want only keep an `<audio>` element as long as it plays
4446
+ the same song (as identified by the tag's `src` attribute).
4447
+
4448
+ On the client we can achieve this by listening to an `up:keep:fragment` event
4449
+ and preventing it if the `src` attribute of the old and new element differ:
4450
+
4451
+ up.compiler('audio', function($element) {
4452
+ $element.on('up:fragment:keep', function(event) {
4453
+ if $element.attr('src') !== event.$newElement.attr('src') {
4454
+ event.preventDefault();
4455
+ }
4456
+ });
4457
+ });
4458
+
4459
+ If we don't want to solve this on the client, we can achieve the same effect
4460
+ on the server. By setting the value of the `up-keep` attribute we can
4461
+ define the CSS selector used for matching elements.
4462
+
4463
+ <audio up-keep="audio[src='song.mp3']" src="song.mp3"></audio>
4464
+
4465
+ Now, if a response no longer contains an `<audio src="song.mp3">` tag, the existing
4466
+ element will be destroyed and replaced by a fragment from the response.
4467
+
4468
+ @selector [up-keep]
4469
+ @stable
4470
+ */
4471
+
4472
+ /**
4473
+ This event is [emitted](/up.emit) before an existing element is [kept](/up-keep) during
4474
+ a page update.
4475
+
4476
+ Event listeners can call `event.preventDefault()` on an `up:fragment:keep` event
4477
+ to prevent the element from being persisted. If the event is prevented, the element
4478
+ will be replaced by a fragment from the response.
4479
+
4480
+ @event up:fragment:keep
4481
+ @param event.preventDefault()
4482
+ Event listeners may call this method to prevent the element from being preserved.
4483
+ @param {jQuery} event.$element
4484
+ The fragment that will be kept.
4485
+ @param {jqQuery} event.$newElement
4486
+ The discarded element.
4487
+ @param {jQuery} event.newData
4488
+ The value of the [`up-data`](/up-data) attribute of the discarded element,
4489
+ parsed as a JSON object.
4490
+ @stable
4491
+ */
4492
+
4493
+ /**
4494
+ This event is [emitted](/up.emit) when an existing element has been [kept](/up-keep)
4495
+ during a page update.
4496
+
4497
+ Event listeners can inspect the discarded update through `event.$newElement`
4498
+ and `event.newData` and then modify the preserved element when necessary.
4499
+
4500
+ @event up:fragment:kept
4501
+ @param {jQuery} event.$element
4502
+ The fragment that has been kept.
4503
+ @param {jqQuery} event.$newElement
4504
+ The discarded element.
4505
+ @param {jQuery} event.newData
4506
+ The value of the [`up-data`](/up-data) attribute of the discarded element,
4507
+ parsed as a JSON object.
4508
+ @stable
4509
+ */
4187
4510
  parseImplantSteps = function(selector, options) {
4188
- var comma, disjunction, i, j, len, pseudoClass, results, selectorAtom, selectorParts, transition, transitionString, transitions;
4189
- transitionString = options.transition || options.animation || 'none';
4511
+ var comma, disjunction, i, j, len, pseudoClass, results, selectorAtom, selectorParts, transition, transitionArg, transitions;
4512
+ transitionArg = options.transition || options.animation || 'none';
4190
4513
  comma = /\ *,\ */;
4191
4514
  disjunction = selector.split(comma);
4192
- if (u.isPresent(transitionString)) {
4193
- transitions = transitionString.split(comma);
4515
+ if (u.isString(transitions)) {
4516
+ transitions = transitionArg.split(comma);
4517
+ } else {
4518
+ transitions = [transitionArg];
4194
4519
  }
4195
4520
  results = [];
4196
4521
  for (i = j = 0, len = disjunction.length; j < len; i = ++j) {
4197
4522
  selectorAtom = disjunction[i];
4198
4523
  selectorParts = selectorAtom.match(/^(.+?)(?:\:(before|after))?$/);
4199
- selectorParts || u.error('Could not parse selector atom %o', selectorAtom);
4524
+ selectorParts || u.error('Could not parse selector atom "%s"', selectorAtom);
4200
4525
  selector = selectorParts[1];
4201
4526
  if (selector === 'html') {
4202
4527
  selector = 'body';
@@ -4211,6 +4536,83 @@ are based on this module.
4211
4536
  }
4212
4537
  return results;
4213
4538
  };
4539
+
4540
+ /**
4541
+ Compiles a page fragment that has been inserted into the DOM
4542
+ without Up.js.
4543
+
4544
+ **As long as you manipulate the DOM using Up.js, you will never
4545
+ need to call this method.** You only need to use `up.hello` if the
4546
+ DOM is manipulated without Up.js' involvement, e.g. by setting
4547
+ the `innerHTML` property or calling jQuery methods like
4548
+ `html`, `insertAfter` or `appendTo`:
4549
+
4550
+ $element = $('.element');
4551
+ $element.html('<div>...</div>');
4552
+ up.hello($element);
4553
+
4554
+ This function emits the [`up:fragment:inserted`](/up:fragment:inserted)
4555
+ event.
4556
+
4557
+ @function up.hello
4558
+ @param {String|Element|jQuery} selectorOrElement
4559
+ @param {String|Element|jQuery} [options.origin]
4560
+ @param {String|Element|jQuery} [options.kept]
4561
+ @return {jQuery}
4562
+ The compiled element
4563
+ @stable
4564
+ */
4565
+ hello = function(selectorOrElement, options) {
4566
+ var $element, j, keptElements, len, plan, ref;
4567
+ $element = $(selectorOrElement);
4568
+ options = u.options(options, {
4569
+ keepPlans: []
4570
+ });
4571
+ keptElements = [];
4572
+ ref = options.keepPlans;
4573
+ for (j = 0, len = ref.length; j < len; j++) {
4574
+ plan = ref[j];
4575
+ emitFragmentKept(plan);
4576
+ keptElements.push(plan.$element);
4577
+ }
4578
+ up.syntax.compile($element, {
4579
+ skip: keptElements
4580
+ });
4581
+ emitFragmentInserted($element, options);
4582
+ return $element;
4583
+ };
4584
+
4585
+ /**
4586
+ When a page fragment has been [inserted or updated](/up.replace),
4587
+ this event is [emitted](/up.emit) on the fragment.
4588
+
4589
+ \#\#\#\# Example
4590
+
4591
+ up.on('up:fragment:inserted', function(event, $fragment) {
4592
+ console.log("Looks like we have a new %o!", $fragment);
4593
+ });
4594
+
4595
+ @event up:fragment:inserted
4596
+ @param {jQuery} event.$element
4597
+ The fragment that has been inserted or updated.
4598
+ @stable
4599
+ */
4600
+ emitFragmentInserted = function(fragment, options) {
4601
+ var $fragment;
4602
+ $fragment = $(fragment);
4603
+ return up.emit('up:fragment:inserted', {
4604
+ $element: $fragment,
4605
+ message: ['Inserted fragment %o', $fragment.get(0)],
4606
+ origin: options.origin
4607
+ });
4608
+ };
4609
+ emitFragmentKept = function(keepPlan) {
4610
+ var eventAttrs;
4611
+ eventAttrs = u.merge(keepPlan, {
4612
+ message: ['Kept fragment %o', keepPlan.$element.get(0)]
4613
+ });
4614
+ return up.emit('up:fragment:kept', eventAttrs);
4615
+ };
4214
4616
  autofocus = function($element) {
4215
4617
  var $control, selector;
4216
4618
  selector = '[autofocus]:last';
@@ -4290,10 +4692,15 @@ are based on this module.
4290
4692
  @stable
4291
4693
  */
4292
4694
  destroy = function(selectorOrElement, options) {
4293
- var $element, animateOptions, animationDeferred;
4695
+ var $element, animateOptions, animationDeferred, destroyMessage, destroyedMessage;
4294
4696
  $element = $(selectorOrElement);
4697
+ if (!$element.is('.up-placeholder, .up-tooltip, .up-modal, .up-popup')) {
4698
+ destroyMessage = ['Destroying fragment %o', $element.get(0)];
4699
+ destroyedMessage = ['Destroyed fragment %o', $element.get(0)];
4700
+ }
4295
4701
  if (up.bus.nobodyPrevents('up:fragment:destroy', {
4296
- $element: $element
4702
+ $element: $element,
4703
+ message: destroyMessage
4297
4704
  })) {
4298
4705
  options = u.options(options, {
4299
4706
  animation: false
@@ -4308,8 +4715,10 @@ are based on this module.
4308
4715
  }
4309
4716
  animationDeferred = u.presence(options.animation, u.isDeferred) || up.motion.animate($element, options.animation, animateOptions);
4310
4717
  animationDeferred.then(function() {
4718
+ up.syntax.clean($element);
4311
4719
  up.emit('up:fragment:destroyed', {
4312
- $element: $element
4720
+ $element: $element,
4721
+ message: destroyedMessage
4313
4722
  });
4314
4723
  return $element.remove();
4315
4724
  });
@@ -4377,23 +4786,27 @@ are based on this module.
4377
4786
  return replace(selectorOrElement, sourceUrl, options);
4378
4787
  };
4379
4788
  up.on('ready', function() {
4380
- return setSource(document.body, up.browser.url());
4789
+ var $body;
4790
+ $body = $(document.body);
4791
+ setSource($body, up.browser.url());
4792
+ return hello($body);
4381
4793
  });
4382
4794
  return {
4383
4795
  knife: eval(typeof Knife !== "undefined" && Knife !== null ? Knife.point : void 0),
4384
4796
  replace: replace,
4385
4797
  reload: reload,
4386
4798
  destroy: destroy,
4387
- implant: implant,
4799
+ extract: extract,
4388
4800
  first: first,
4389
4801
  source: source,
4390
- resolveSelector: resolveSelector
4802
+ resolveSelector: resolveSelector,
4803
+ hello: hello
4391
4804
  };
4392
4805
  })(jQuery);
4393
4806
 
4394
4807
  up.replace = up.flow.replace;
4395
4808
 
4396
- up.implant = up.flow.implant;
4809
+ up.extract = up.flow.extract;
4397
4810
 
4398
4811
  up.reload = up.flow.reload;
4399
4812
 
@@ -4401,6 +4814,8 @@ are based on this module.
4401
4814
 
4402
4815
  up.first = up.flow.first;
4403
4816
 
4817
+ up.hello = up.flow.hello;
4818
+
4404
4819
  }).call(this);
4405
4820
 
4406
4821
  /**
@@ -4436,7 +4851,7 @@ or [transitions](/up.transition) using Javascript or CSS.
4436
4851
 
4437
4852
  (function() {
4438
4853
  up.motion = (function($) {
4439
- var GHOSTING_PROMISE_KEY, animate, animateOptions, animation, animations, assertIsDeferred, config, defaultAnimations, defaultTransitions, findAnimation, finish, finishGhosting, isEnabled, morph, none, prependCopy, reset, resolvableWhen, skipMorph, snapshot, transition, transitions, u, withGhosts;
4854
+ var GHOSTING_PROMISE_KEY, animate, animateOptions, animation, animations, assertIsDeferred, config, defaultAnimations, defaultTransitions, ensureMorphable, findAnimation, finish, finishGhosting, isEnabled, morph, none, prependCopy, reset, resolvableWhen, skipMorph, snapshot, transition, transitions, u, withGhosts;
4440
4855
  u = up.util;
4441
4856
  animations = {};
4442
4857
  defaultAnimations = {};
@@ -4668,7 +5083,7 @@ or [transitions](/up.transition) using Javascript or CSS.
4668
5083
  finishGhosting = function($element) {
4669
5084
  var existingGhosting;
4670
5085
  if (existingGhosting = $element.data(GHOSTING_PROMISE_KEY)) {
4671
- u.debug('Canceling existing ghosting on %o', $element);
5086
+ up.puts('Canceling existing ghosting on %o', $element);
4672
5087
  return typeof existingGhosting.resolve === "function" ? existingGhosting.resolve() : void 0;
4673
5088
  }
4674
5089
  };
@@ -4744,37 +5159,51 @@ or [transitions](/up.transition) using Javascript or CSS.
4744
5159
  @stable
4745
5160
  */
4746
5161
  morph = function(source, target, transitionOrName, options) {
4747
- var $new, $old, animation, parsedOptions, parts, transition;
4748
- u.debug('Morphing %o to %o (using %o)', source, target, transitionOrName);
5162
+ var $new, $old;
5163
+ if (transitionOrName === 'none') {
5164
+ transitionOrName = false;
5165
+ }
4749
5166
  $old = $(source);
4750
5167
  $new = $(target);
4751
- parsedOptions = u.only(options, 'reveal', 'restoreScroll', 'source');
4752
- parsedOptions = u.extend(parsedOptions, animateOptions(options));
4753
- if (isEnabled()) {
4754
- finish($old);
4755
- finish($new);
4756
- if (transitionOrName === 'none' || transitionOrName === false) {
4757
- return skipMorph($old, $new, parsedOptions);
4758
- } else if (animation = animations[transitionOrName]) {
4759
- skipMorph($old, $new, parsedOptions);
4760
- return animate($new, animation, parsedOptions);
4761
- } else if (transition = u.presence(transitionOrName, u.isFunction) || transitions[transitionOrName]) {
4762
- return withGhosts($old, $new, parsedOptions, function($oldGhost, $newGhost) {
4763
- var transitionPromise;
4764
- transitionPromise = transition($oldGhost, $newGhost, parsedOptions);
4765
- return assertIsDeferred(transitionPromise, transitionOrName);
4766
- });
4767
- } else if (u.isString(transitionOrName) && transitionOrName.indexOf('/') >= 0) {
4768
- parts = transitionOrName.split('/');
4769
- transition = function($old, $new, options) {
4770
- return resolvableWhen(animate($old, parts[0], options), animate($new, parts[1], options));
4771
- };
4772
- return morph($old, $new, transition, parsedOptions);
5168
+ ensureMorphable($old, transitionOrName);
5169
+ ensureMorphable($new, transitionOrName);
5170
+ return up.log.group((transitionOrName ? 'Morphing %o to %o (using %o)' : void 0), source, target, transitionOrName, function() {
5171
+ var animation, parsedOptions, parts, transition;
5172
+ parsedOptions = u.only(options, 'reveal', 'restoreScroll', 'source');
5173
+ parsedOptions = u.extend(parsedOptions, animateOptions(options));
5174
+ if (isEnabled()) {
5175
+ finish($old);
5176
+ finish($new);
5177
+ if (!transitionOrName) {
5178
+ return skipMorph($old, $new, parsedOptions);
5179
+ } else if (animation = animations[transitionOrName]) {
5180
+ skipMorph($old, $new, parsedOptions);
5181
+ return animate($new, animation, parsedOptions);
5182
+ } else if (transition = u.presence(transitionOrName, u.isFunction) || transitions[transitionOrName]) {
5183
+ return withGhosts($old, $new, parsedOptions, function($oldGhost, $newGhost) {
5184
+ var transitionPromise;
5185
+ transitionPromise = transition($oldGhost, $newGhost, parsedOptions);
5186
+ return assertIsDeferred(transitionPromise, transitionOrName);
5187
+ });
5188
+ } else if (u.isString(transitionOrName) && transitionOrName.indexOf('/') >= 0) {
5189
+ parts = transitionOrName.split('/');
5190
+ transition = function($old, $new, options) {
5191
+ return resolvableWhen(animate($old, parts[0], options), animate($new, parts[1], options));
5192
+ };
5193
+ return morph($old, $new, transition, parsedOptions);
5194
+ } else {
5195
+ return u.error("Unknown transition %o", transitionOrName);
5196
+ }
4773
5197
  } else {
4774
- return u.error("Unknown transition %o", transitionOrName);
5198
+ return skipMorph($old, $new, parsedOptions);
4775
5199
  }
4776
- } else {
4777
- return skipMorph($old, $new, parsedOptions);
5200
+ });
5201
+ };
5202
+ ensureMorphable = function($element, transition) {
5203
+ var element;
5204
+ if (transition && $element.parents('body').length === 0) {
5205
+ element = $element.get(0);
5206
+ return u.error("Can't morph a <%s> element (%o)", element.tagName, element);
4778
5207
  }
4779
5208
  };
4780
5209
 
@@ -5161,7 +5590,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5161
5590
  var slice = [].slice;
5162
5591
 
5163
5592
  up.proxy = (function($) {
5164
- var $waitingLink, ajax, alias, busy, busyDelayTimer, busyEventEmitted, cache, cacheKey, cancelBusyDelay, cancelPreloadDelay, checkPreload, clear, config, get, idle, isIdempotent, load, loadEnded, loadOrQueue, loadStarted, normalizeRequest, pendingCount, pokeQueue, preload, preloadDelayTimer, queue, queuedRequests, remove, reset, set, startPreloadDelay, u;
5593
+ var $waitingLink, ajax, alias, busy, busyDelayTimer, busyEventEmitted, cache, cacheKey, cancelBusyDelay, cancelPreloadDelay, checkPreload, clear, config, get, idle, isIdempotent, load, loadEnded, loadOrQueue, loadStarted, normalizeRequest, pendingCount, pokeQueue, preload, preloadDelayTimer, queue, queuedRequests, remove, reset, responseReceived, set, startPreloadDelay, u;
5165
5594
  u = up.util;
5166
5595
  $waitingLink = void 0;
5167
5596
  preloadDelayTimer = void 0;
@@ -5226,8 +5655,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5226
5655
  expiry: function() {
5227
5656
  return config.cacheExpiry;
5228
5657
  },
5229
- key: cacheKey,
5230
- log: 'up.proxy'
5658
+ key: cacheKey
5231
5659
  });
5232
5660
 
5233
5661
  /**
@@ -5349,7 +5777,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5349
5777
  Once the response is received, a `up:proxy:receive` event will
5350
5778
  be emitted.
5351
5779
 
5352
- @function up.proxy.ajax
5780
+ @function up.ajax
5353
5781
  @param {String} request.url
5354
5782
  @param {String} [request.method='GET']
5355
5783
  @param {String} [request.target='body']
@@ -5371,11 +5799,13 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5371
5799
  forceCache = options.cache === true;
5372
5800
  ignoreCache = options.cache === false;
5373
5801
  request = u.only(options, 'url', 'method', 'data', 'target', 'headers', '_normalized');
5802
+ request = normalizeRequest(request);
5374
5803
  pending = true;
5375
5804
  if (!isIdempotent(request) && !forceCache) {
5376
5805
  clear();
5377
5806
  promise = loadOrQueue(request);
5378
5807
  } else if ((promise = get(request)) && !ignoreCache) {
5808
+ up.puts('Re-using cached response for %s %s', request.method, request.url);
5379
5809
  pending = promise.state() === 'pending';
5380
5810
  } else {
5381
5811
  promise = loadOrQueue(request);
@@ -5388,6 +5818,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5388
5818
  loadStarted();
5389
5819
  promise.always(loadEnded);
5390
5820
  }
5821
+ console.groupEnd();
5391
5822
  return promise;
5392
5823
  };
5393
5824
 
@@ -5429,7 +5860,9 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5429
5860
  if (wasIdle) {
5430
5861
  emission = function() {
5431
5862
  if (busy()) {
5432
- up.emit('up:proxy:busy');
5863
+ up.emit('up:proxy:busy', {
5864
+ message: 'Proxy is busy'
5865
+ });
5433
5866
  return busyEventEmitted = true;
5434
5867
  }
5435
5868
  };
@@ -5442,7 +5875,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5442
5875
  };
5443
5876
 
5444
5877
  /**
5445
- This event is [emitted]/(up.emit) when [AJAX requests](/up.proxy.ajax)
5878
+ This event is [emitted]/(up.emit) when [AJAX requests](/up.ajax)
5446
5879
  are taking long to finish.
5447
5880
 
5448
5881
  By default Up.js will wait 300 ms for an AJAX request to finish
@@ -5462,13 +5895,15 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5462
5895
  loadEnded = function() {
5463
5896
  pendingCount -= 1;
5464
5897
  if (idle() && busyEventEmitted) {
5465
- up.emit('up:proxy:idle');
5898
+ up.emit('up:proxy:idle', {
5899
+ message: 'Proxy is idle'
5900
+ });
5466
5901
  return busyEventEmitted = false;
5467
5902
  }
5468
5903
  };
5469
5904
 
5470
5905
  /**
5471
- This event is [emitted]/(up.emit) when [AJAX requests](/up.proxy.ajax)
5906
+ This event is [emitted]/(up.emit) when [AJAX requests](/up.ajax)
5472
5907
  have [taken long to finish](/up:proxy:busy), but have finished now.
5473
5908
 
5474
5909
  @event up:proxy:idle
@@ -5483,7 +5918,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5483
5918
  };
5484
5919
  queue = function(request) {
5485
5920
  var deferred, entry;
5486
- u.debug('Queuing URL %o', request.url);
5921
+ up.puts('Queuing request for %s %s', request.method, request.url);
5487
5922
  deferred = $.Deferred();
5488
5923
  entry = {
5489
5924
  deferred: deferred,
@@ -5494,8 +5929,9 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5494
5929
  };
5495
5930
  load = function(request) {
5496
5931
  var promise;
5497
- u.debug('Fetching %o via %o', request.url, request.method);
5498
- up.emit('up:proxy:load', request);
5932
+ up.emit('up:proxy:load', u.merge(request, {
5933
+ message: ['Loading %s %s', request.method, request.url]
5934
+ }));
5499
5935
  request = u.copy(request);
5500
5936
  request.headers || (request.headers = {});
5501
5937
  request.headers['X-Up-Target'] = request.target;
@@ -5508,12 +5944,21 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5508
5944
  request.method = 'POST';
5509
5945
  }
5510
5946
  promise = $.ajax(request);
5511
- promise.always(function() {
5512
- up.emit('up:proxy:received', request);
5513
- return pokeQueue();
5947
+ promise.done(function(data, textStatus, xhr) {
5948
+ return responseReceived(request, xhr);
5949
+ });
5950
+ promise.fail(function(xhr, textStatus, errorThrown) {
5951
+ return responseReceived(request, xhr);
5514
5952
  });
5515
5953
  return promise;
5516
5954
  };
5955
+ responseReceived = function(request, xhr) {
5956
+ var ref;
5957
+ up.emit('up:proxy:received', u.merge(request, {
5958
+ message: ['Server responded with %s %s (%d bytes)', xhr.status, xhr.statusText, (ref = xhr.responseText) != null ? ref.length : void 0]
5959
+ }));
5960
+ return pokeQueue();
5961
+ };
5517
5962
  pokeQueue = function() {
5518
5963
  var entry, promise;
5519
5964
  if (entry = queuedRequests.shift()) {
@@ -5532,7 +5977,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5532
5977
  };
5533
5978
 
5534
5979
  /**
5535
- This event is [emitted]/(up.emit) before an [AJAX request](/up.proxy.ajax)
5980
+ This event is [emitted]/(up.emit) before an [AJAX request](/up.ajax)
5536
5981
  is starting to load.
5537
5982
 
5538
5983
  @event up:proxy:load
@@ -5543,7 +5988,7 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5543
5988
  */
5544
5989
 
5545
5990
  /**
5546
- This event is [emitted]/(up.emit) when the response to an [AJAX request](/up.proxy.ajax)
5991
+ This event is [emitted]/(up.emit) when the response to an [AJAX request](/up.ajax)
5547
5992
  has been received.
5548
5993
 
5549
5994
  @event up:proxy:received
@@ -5589,11 +6034,12 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5589
6034
  if (isIdempotent({
5590
6035
  method: method
5591
6036
  })) {
5592
- u.debug("Preloading %o", $link);
5593
- options.preload = true;
5594
- return up.follow($link, options);
6037
+ return up.log.group("Preloading link %o", $link, function() {
6038
+ options.preload = true;
6039
+ return up.follow($link, options);
6040
+ });
5595
6041
  } else {
5596
- u.debug("Won't preload %o due to unsafe method %o", $link, method);
6042
+ up.puts("Won't preload %o due to unsafe method %s", $link, method);
5597
6043
  return u.resolvedPromise();
5598
6044
  }
5599
6045
  };
@@ -5634,6 +6080,8 @@ You can change (or remove) this delay by [configuring `up.proxy`](/up.proxy.conf
5634
6080
  };
5635
6081
  })(jQuery);
5636
6082
 
6083
+ up.ajax = up.proxy.ajax;
6084
+
5637
6085
  }).call(this);
5638
6086
 
5639
6087
  /**
@@ -6384,7 +6832,7 @@ open dialogs with sub-forms, etc. all without losing form state.
6384
6832
  delay = parseInt(delay);
6385
6833
  callback = null;
6386
6834
  if (u.isGiven(options.change)) {
6387
- up.error('up.observe now takes the change callback as the last argument');
6835
+ u.error('up.observe now takes the change callback as the last argument');
6388
6836
  }
6389
6837
  rawCallback = u.option(u.presentAttr($element, 'op-observe'), callbackArg);
6390
6838
  if (u.isString(rawCallback)) {
@@ -6510,7 +6958,7 @@ open dialogs with sub-forms, etc. all without losing form state.
6510
6958
  }));
6511
6959
  }
6512
6960
  if (u.isBlank(target)) {
6513
- error('Could not find default validation target for %o (tried ancestors %o)', $field, config.validateTargets);
6961
+ u.error('Could not find default validation target for %o (tried ancestors %o)', $field.get(0), config.validateTargets);
6514
6962
  }
6515
6963
  if (!u.isString(target)) {
6516
6964
  target = u.selectorForElement(target);
@@ -6567,17 +7015,13 @@ open dialogs with sub-forms, etc. all without losing form state.
6567
7015
  values = [':unchecked', ':blank'];
6568
7016
  }
6569
7017
  } else if ($field.is('input[type=radio]')) {
6570
- console.log('-- it is a radio button --');
6571
7018
  $checkedButton = $field.closest('form, body').find("input[type='radio'][name='" + ($field.attr('name')) + "']:checked");
6572
- console.log('checked button is %o', $checkedButton);
6573
- console.log('checked button val is %o', $checkedButton.val());
6574
7019
  if ($checkedButton.length) {
6575
7020
  values = [':checked', ':present', $checkedButton.val()];
6576
7021
  } else {
6577
7022
  values = [':unchecked', ':blank'];
6578
7023
  }
6579
7024
  } else {
6580
- console.log('-- else -- for %o', $field);
6581
7025
  value = $field.val();
6582
7026
  if (u.isPresent(value)) {
6583
7027
  values = [':present', value];
@@ -6641,7 +7085,7 @@ open dialogs with sub-forms, etc. all without losing form state.
6641
7085
  $field = $(fieldOrSelector);
6642
7086
  options = u.options(options);
6643
7087
  targets = u.option(options.target, $field.attr('up-toggle'));
6644
- u.isPresent(targets) || u.error("No toggle target given for %o", $field);
7088
+ u.isPresent(targets) || u.error("No toggle target given for %o", $field.get(0));
6645
7089
  fieldValues = currentValuesForToggle($field);
6646
7090
  return $(targets).each(function() {
6647
7091
  var $target, hideValues, show, showValues;
@@ -6976,7 +7420,7 @@ open dialogs with sub-forms, etc. all without losing form state.
6976
7420
 
6977
7421
  @selector [up-hide-for]
6978
7422
  @param up-hide-for
6979
- A space-separated list of values for which to show this element.
7423
+ A space-separated list of values for which to hide this element.
6980
7424
  @stable
6981
7425
  */
6982
7426
  up.on('change', '[up-toggle]', function(event, $field) {
@@ -7198,7 +7642,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7198
7642
  bottom: linkBox.top
7199
7643
  };
7200
7644
  default:
7201
- return u.error("Unknown position %o", position);
7645
+ return u.error("Unknown position option '%s'", position);
7202
7646
  }
7203
7647
  })();
7204
7648
  $popup = $('.up-popup');
@@ -7247,15 +7691,14 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7247
7691
  return $popup.removeAttr('up-covered-title');
7248
7692
  };
7249
7693
  createFrame = function(target, options) {
7250
- var $placeholder, $popup;
7694
+ var $popup;
7251
7695
  $popup = u.$createElementFromSelector('.up-popup');
7252
7696
  if (options.sticky) {
7253
7697
  $popup.attr('up-sticky', '');
7254
7698
  }
7255
7699
  $popup.attr('up-covered-url', up.browser.url());
7256
7700
  $popup.attr('up-covered-title', document.title);
7257
- $placeholder = u.$createElementFromSelector(target);
7258
- $placeholder.appendTo($popup);
7701
+ u.$createPlaceholder(target, $popup);
7259
7702
  $popup.appendTo(document.body);
7260
7703
  return $popup;
7261
7704
  };
@@ -7318,7 +7761,8 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7318
7761
  return up.browser.confirm(options.confirm).then(function() {
7319
7762
  var promise, wasOpen;
7320
7763
  if (up.bus.nobodyPrevents('up:popup:open', {
7321
- url: url
7764
+ url: url,
7765
+ message: 'Opening popup'
7322
7766
  })) {
7323
7767
  wasOpen = isOpen();
7324
7768
  if (wasOpen) {
@@ -7341,7 +7785,9 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7341
7785
  });
7342
7786
  }
7343
7787
  promise = promise.then(function() {
7344
- return up.emit('up:popup:opened');
7788
+ return up.emit('up:popup:opened', {
7789
+ message: 'Popup opened'
7790
+ });
7345
7791
  });
7346
7792
  return promise;
7347
7793
  } else {
@@ -7396,7 +7842,9 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7396
7842
  currentUrl = void 0;
7397
7843
  deferred = up.destroy($popup, options);
7398
7844
  deferred.then(function() {
7399
- return up.emit('up:popup:closed');
7845
+ return up.emit('up:popup:closed', {
7846
+ message: 'Popup closed'
7847
+ });
7400
7848
  });
7401
7849
  return deferred;
7402
7850
  } else {
@@ -7703,7 +8151,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7703
8151
  return $modal.removeAttr('up-covered-title');
7704
8152
  };
7705
8153
  createFrame = function(target, options) {
7706
- var $content, $dialog, $modal, $placeholder;
8154
+ var $content, $dialog, $modal;
7707
8155
  shiftElements();
7708
8156
  $modal = $(templateHtml());
7709
8157
  if (options.sticky) {
@@ -7722,8 +8170,7 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7722
8170
  $dialog.css('height', options.height);
7723
8171
  }
7724
8172
  $content = $modal.find('.up-modal-content');
7725
- $placeholder = u.$createElementFromSelector(target);
7726
- $placeholder.appendTo($content);
8173
+ u.$createPlaceholder(target, $content);
7727
8174
  $modal.appendTo(document.body);
7728
8175
  return $modal;
7729
8176
  };
@@ -7868,7 +8315,8 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7868
8315
  return up.browser.confirm(options.confirm).then(function() {
7869
8316
  var promise, wasOpen;
7870
8317
  if (up.bus.nobodyPrevents('up:modal:open', {
7871
- url: url
8318
+ url: url,
8319
+ message: 'Opening modal'
7872
8320
  })) {
7873
8321
  wasOpen = isOpen();
7874
8322
  if (wasOpen) {
@@ -7888,7 +8336,9 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7888
8336
  });
7889
8337
  }
7890
8338
  promise = promise.then(function() {
7891
- return up.emit('up:modal:opened');
8339
+ return up.emit('up:modal:opened', {
8340
+ message: 'Modal opened'
8341
+ });
7892
8342
  });
7893
8343
  return promise;
7894
8344
  } else {
@@ -7933,7 +8383,8 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7933
8383
  $modal = $('.up-modal');
7934
8384
  if ($modal.length) {
7935
8385
  if (up.bus.nobodyPrevents('up:modal:close', {
7936
- $element: $modal
8386
+ $element: $modal,
8387
+ message: 'Closing modal'
7937
8388
  })) {
7938
8389
  options = u.options(options, {
7939
8390
  animation: config.closeAnimation,
@@ -7944,7 +8395,9 @@ To disable this behavior, give the opening link an `up-sticky` attribute:
7944
8395
  promise = up.destroy($modal, options);
7945
8396
  promise = promise.then(function() {
7946
8397
  unshiftElements();
7947
- return up.emit('up:modal:closed');
8398
+ return up.emit('up:modal:closed', {
8399
+ message: 'Modal closed'
8400
+ });
7948
8401
  });
7949
8402
  return promise;
7950
8403
  } else {
@@ -8170,7 +8623,7 @@ The tooltip element is appended to the end of `<body>`.
8170
8623
  top: linkBox.top + linkBox.height
8171
8624
  };
8172
8625
  default:
8173
- return u.error("Unknown position %o", position);
8626
+ return u.error("Unknown position option '%s'", position);
8174
8627
  }
8175
8628
  })();
8176
8629
  $tooltip.attr('up-position', position);