upjs-rails 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +142 -0
- data/README.md +4 -1
- data/design/ghost-debugging.txt +118 -0
- data/design/homepage.txt +236 -0
- data/dist/up-bootstrap.js +7 -3
- data/dist/up-bootstrap.min.js +1 -1
- data/dist/up.js +1611 -1222
- data/dist/up.min.js +2 -2
- data/lib/assets/javascripts/up/bus.js.coffee +1 -1
- data/lib/assets/javascripts/up/flow.js.coffee +21 -20
- data/lib/assets/javascripts/up/form.js.coffee +11 -12
- data/lib/assets/javascripts/up/history.js.coffee +137 -20
- data/lib/assets/javascripts/up/layout.js.coffee +134 -21
- data/lib/assets/javascripts/up/link.js.coffee +40 -17
- data/lib/assets/javascripts/up/modal.js.coffee +2 -2
- data/lib/assets/javascripts/up/motion.js.coffee +3 -1
- data/lib/assets/javascripts/up/navigation.js.coffee +5 -5
- data/lib/assets/javascripts/up/popup.js.coffee +2 -2
- data/lib/assets/javascripts/up/proxy.js.coffee +43 -82
- data/lib/assets/javascripts/up/tooltip.js.coffee +1 -1
- data/lib/assets/javascripts/up/util.js.coffee +145 -14
- data/lib/assets/javascripts/up-bootstrap/layout-ext.js.coffee +2 -2
- data/lib/assets/javascripts/up-bootstrap/navigation-ext.js.coffee +3 -1
- data/lib/assets/javascripts/up.js.coffee +2 -2
- data/lib/upjs/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/config/routes.rb +1 -2
- data/spec_app/spec/javascripts/helpers/knife.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +4 -0
- data/spec_app/spec/javascripts/helpers/set_timer.js.coffee +3 -3
- data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +8 -6
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +80 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +64 -4
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +2 -2
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +7 -7
- data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +6 -6
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +2 -2
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +22 -4
- metadata +7 -2
data/dist/up.js
CHANGED
@@ -25,7 +25,7 @@ If you use them in your own code, you will get hurt.
|
|
25
25
|
var slice = [].slice;
|
26
26
|
|
27
27
|
up.util = (function() {
|
28
|
-
var $createElementFromSelector, ANIMATION_PROMISE_KEY, CONSOLE_PLACEHOLDERS, ajax,
|
28
|
+
var $createElementFromSelector, ANIMATION_PROMISE_KEY, CONSOLE_PLACEHOLDERS, ajax, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, createSelectorFromElement, cssAnimate, debug, detect, each, endsWith, error, escapePressed, extend, findWithSelf, finishCssAnimate, forceCompositing, get, identity, ifGiven, isArray, isBlank, isDeferred, isDefined, isElement, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, keys, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, nextFrame, normalizeMethod, normalizeUrl, nullJquery, once, only, option, options, presence, presentAttr, remove, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, setMissingAttrs, startsWith, stringifyConsoleArgs, temporaryCss, times, toArray, trim, unJquery, uniq, unwrapElement, warn;
|
29
29
|
memoize = function(func) {
|
30
30
|
var cache, cached;
|
31
31
|
cache = void 0;
|
@@ -335,6 +335,9 @@ If you use them in your own code, you will get hurt.
|
|
335
335
|
isString = function(object) {
|
336
336
|
return typeof object === 'string';
|
337
337
|
};
|
338
|
+
isNumber = function(object) {
|
339
|
+
return typeof object === 'number';
|
340
|
+
};
|
338
341
|
isHash = function(object) {
|
339
342
|
return typeof object === 'object' && !!object;
|
340
343
|
};
|
@@ -691,11 +694,19 @@ If you use them in your own code, you will get hurt.
|
|
691
694
|
contains = function(stringOrArray, element) {
|
692
695
|
return stringOrArray.indexOf(element) >= 0;
|
693
696
|
};
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
697
|
+
castedAttr = function($element, attrName) {
|
698
|
+
var value;
|
699
|
+
value = $element.attr(attrName);
|
700
|
+
switch (value) {
|
701
|
+
case 'false':
|
702
|
+
return false;
|
703
|
+
case 'true':
|
704
|
+
return true;
|
705
|
+
case '':
|
706
|
+
return true;
|
707
|
+
default:
|
708
|
+
return value;
|
709
|
+
}
|
699
710
|
};
|
700
711
|
locationFromXhr = function(xhr) {
|
701
712
|
return xhr.getResponseHeader('X-Up-Location');
|
@@ -775,12 +786,153 @@ If you use them in your own code, you will get hurt.
|
|
775
786
|
return element;
|
776
787
|
}
|
777
788
|
};
|
789
|
+
|
790
|
+
/**
|
791
|
+
@method up.util.cache
|
792
|
+
@param {Number|Function} [config.size]
|
793
|
+
Maximum number of cache entries.
|
794
|
+
Set to `undefined` to not limit the cache size.
|
795
|
+
@param {Number|Function} [config.expiry]
|
796
|
+
The number of milliseconds after which a cache entry
|
797
|
+
will be discarded.
|
798
|
+
@param {String} [config.log]
|
799
|
+
A prefix for log entries printed by this cache object.
|
800
|
+
*/
|
801
|
+
cache = function(config) {
|
802
|
+
var alias, clear, expiryMilis, isFresh, log, maxSize, normalizeStoreKey, set, store, timestamp;
|
803
|
+
store = void 0;
|
804
|
+
clear = function() {
|
805
|
+
return store = {};
|
806
|
+
};
|
807
|
+
clear();
|
808
|
+
log = function() {
|
809
|
+
var args;
|
810
|
+
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
811
|
+
if (config.log) {
|
812
|
+
args[0] = "[" + config.log + "] " + args[0];
|
813
|
+
return debug.apply(null, args);
|
814
|
+
}
|
815
|
+
};
|
816
|
+
maxSize = function() {
|
817
|
+
if (isMissing(config.size)) {
|
818
|
+
return void 0;
|
819
|
+
} else if (isFunction(config.size)) {
|
820
|
+
return config.size();
|
821
|
+
} else if (isNumber(config.size)) {
|
822
|
+
return config.size;
|
823
|
+
} else {
|
824
|
+
return error("Invalid size config: %o", config.size);
|
825
|
+
}
|
826
|
+
};
|
827
|
+
expiryMilis = function() {
|
828
|
+
if (isMissing(config.expiry)) {
|
829
|
+
return void 0;
|
830
|
+
} else if (isFunction(config.expiry)) {
|
831
|
+
return config.expiry();
|
832
|
+
} else if (isNumber(config.expiry)) {
|
833
|
+
return config.expiry;
|
834
|
+
} else {
|
835
|
+
return error("Invalid expiry config: %o", config.expiry);
|
836
|
+
}
|
837
|
+
};
|
838
|
+
normalizeStoreKey = function(key) {
|
839
|
+
if (config.key) {
|
840
|
+
return config.key(key);
|
841
|
+
} else {
|
842
|
+
return key.toString();
|
843
|
+
}
|
844
|
+
};
|
845
|
+
trim = function() {
|
846
|
+
var oldestKey, oldestTimestamp, size, storeKeys;
|
847
|
+
storeKeys = copy(keys(store));
|
848
|
+
size = maxSize();
|
849
|
+
if (size && storeKeys.length > size) {
|
850
|
+
oldestKey = null;
|
851
|
+
oldestTimestamp = null;
|
852
|
+
each(storeKeys, function(key) {
|
853
|
+
var promise, timestamp;
|
854
|
+
promise = store[key];
|
855
|
+
timestamp = promise.timestamp;
|
856
|
+
if (!oldestTimestamp || oldestTimestamp > timestamp) {
|
857
|
+
oldestKey = key;
|
858
|
+
return oldestTimestamp = timestamp;
|
859
|
+
}
|
860
|
+
});
|
861
|
+
if (oldestKey) {
|
862
|
+
return delete store[oldestKey];
|
863
|
+
}
|
864
|
+
}
|
865
|
+
};
|
866
|
+
alias = function(oldKey, newKey) {
|
867
|
+
var value;
|
868
|
+
value = get(oldKey);
|
869
|
+
if (isDefined(value)) {
|
870
|
+
return set(newKey, value);
|
871
|
+
}
|
872
|
+
};
|
873
|
+
timestamp = function() {
|
874
|
+
return (new Date()).valueOf();
|
875
|
+
};
|
876
|
+
set = function(key, value) {
|
877
|
+
var storeKey;
|
878
|
+
storeKey = normalizeStoreKey(key);
|
879
|
+
return store[storeKey] = {
|
880
|
+
timestamp: timestamp(),
|
881
|
+
value: value
|
882
|
+
};
|
883
|
+
};
|
884
|
+
remove = function(key) {
|
885
|
+
var storeKey;
|
886
|
+
storeKey = normalizeStoreKey(key);
|
887
|
+
return delete store[storeKey];
|
888
|
+
};
|
889
|
+
isFresh = function(entry) {
|
890
|
+
var expiry, timeSinceTouch;
|
891
|
+
expiry = expiryMilis();
|
892
|
+
if (expiry) {
|
893
|
+
timeSinceTouch = timestamp() - entry.timestamp;
|
894
|
+
return timeSinceTouch < expiryMilis();
|
895
|
+
} else {
|
896
|
+
return true;
|
897
|
+
}
|
898
|
+
};
|
899
|
+
get = function(key, fallback) {
|
900
|
+
var entry, storeKey;
|
901
|
+
if (fallback == null) {
|
902
|
+
fallback = void 0;
|
903
|
+
}
|
904
|
+
storeKey = normalizeStoreKey(key);
|
905
|
+
if (entry = store[storeKey]) {
|
906
|
+
if (!isFresh(entry)) {
|
907
|
+
log("Discarding stale cache entry for %o", key);
|
908
|
+
remove(key);
|
909
|
+
return fallback;
|
910
|
+
} else {
|
911
|
+
log("Cache hit for %o", key);
|
912
|
+
return entry.value;
|
913
|
+
}
|
914
|
+
} else {
|
915
|
+
log("Cache miss for %o", key);
|
916
|
+
return fallback;
|
917
|
+
}
|
918
|
+
};
|
919
|
+
return {
|
920
|
+
alias: alias,
|
921
|
+
get: get,
|
922
|
+
set: set,
|
923
|
+
remove: remove,
|
924
|
+
clear: clear
|
925
|
+
};
|
926
|
+
};
|
778
927
|
config = function(factoryOptions) {
|
779
928
|
var apiKeys, hash;
|
780
929
|
if (factoryOptions == null) {
|
781
930
|
factoryOptions = {};
|
782
931
|
}
|
783
932
|
hash = {
|
933
|
+
ensureKeyExists: function(key) {
|
934
|
+
return factoryOptions.hasOwnProperty(key) || error("Unknown setting %o", key);
|
935
|
+
},
|
784
936
|
reset: function() {
|
785
937
|
var j, key, len, ownKeys;
|
786
938
|
ownKeys = copy(Object.getOwnPropertyNames(hash));
|
@@ -793,19 +945,23 @@ If you use them in your own code, you will get hurt.
|
|
793
945
|
return hash.update(copy(factoryOptions));
|
794
946
|
},
|
795
947
|
update: function(options) {
|
796
|
-
var key, value;
|
797
|
-
if (options
|
798
|
-
options
|
799
|
-
|
800
|
-
|
801
|
-
value = options[key];
|
802
|
-
if (factoryOptions.hasOwnProperty(key)) {
|
803
|
-
hash[key] = value;
|
948
|
+
var key, results, value;
|
949
|
+
if (options) {
|
950
|
+
if (isString(options)) {
|
951
|
+
hash.ensureKeyExists(options);
|
952
|
+
return hash[options];
|
804
953
|
} else {
|
805
|
-
|
954
|
+
results = [];
|
955
|
+
for (key in options) {
|
956
|
+
value = options[key];
|
957
|
+
hash.ensureKeyExists(key);
|
958
|
+
results.push(hash[key] = value);
|
959
|
+
}
|
960
|
+
return results;
|
806
961
|
}
|
962
|
+
} else {
|
963
|
+
return hash;
|
807
964
|
}
|
808
|
-
return hash;
|
809
965
|
}
|
810
966
|
};
|
811
967
|
apiKeys = Object.getOwnPropertyNames(hash);
|
@@ -884,8 +1040,7 @@ If you use them in your own code, you will get hurt.
|
|
884
1040
|
endsWith: endsWith,
|
885
1041
|
isArray: isArray,
|
886
1042
|
toArray: toArray,
|
887
|
-
|
888
|
-
castsToFalse: castsToFalse,
|
1043
|
+
castedAttr: castedAttr,
|
889
1044
|
locationFromXhr: locationFromXhr,
|
890
1045
|
methodFromXhr: methodFromXhr,
|
891
1046
|
clientSize: clientSize,
|
@@ -900,6 +1055,7 @@ If you use them in your own code, you will get hurt.
|
|
900
1055
|
memoize: memoize,
|
901
1056
|
scrollbarWidth: scrollbarWidth,
|
902
1057
|
config: config,
|
1058
|
+
cache: cache,
|
903
1059
|
unwrapElement: unwrapElement
|
904
1060
|
};
|
905
1061
|
})();
|
@@ -1154,7 +1310,6 @@ We need to work on this page:
|
|
1154
1310
|
emit = function() {
|
1155
1311
|
var args, callbacks, eventName;
|
1156
1312
|
eventName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
1157
|
-
u.debug("Emitting event %o with args %o", eventName, args);
|
1158
1313
|
callbacks = callbacksFor(eventName);
|
1159
1314
|
return u.each(callbacks, function(callback) {
|
1160
1315
|
return callback.apply(null, args);
|
@@ -1172,1258 +1327,1511 @@ We need to work on this page:
|
|
1172
1327
|
}).call(this);
|
1173
1328
|
|
1174
1329
|
/**
|
1175
|
-
|
1176
|
-
|
1330
|
+
Registering behavior and custom elements
|
1331
|
+
========================================
|
1332
|
+
|
1333
|
+
Up.js keeps a persistent Javascript environment during page transitions.
|
1334
|
+
To prevent memory leaks it is important to cleanly set up and tear down
|
1335
|
+
event handlers and custom elements.
|
1177
1336
|
|
1178
|
-
|
1337
|
+
\#\#\# Incomplete documentation!
|
1179
1338
|
|
1180
|
-
|
1339
|
+
We need to work on this page:
|
1340
|
+
|
1341
|
+
- Better class-level introduction for this module
|
1342
|
+
|
1343
|
+
@class up.magic
|
1181
1344
|
*/
|
1182
1345
|
|
1183
1346
|
(function() {
|
1184
1347
|
var slice = [].slice;
|
1185
1348
|
|
1186
|
-
up.
|
1187
|
-
var
|
1349
|
+
up.magic = (function() {
|
1350
|
+
var DESTROYABLE_CLASS, DESTROYER_KEY, applyCompiler, compile, compiler, compilers, data, defaultCompilers, defaultLiveDescriptions, destroy, live, liveDescriptions, onEscape, ready, reset, snapshot, u;
|
1188
1351
|
u = up.util;
|
1352
|
+
DESTROYABLE_CLASS = 'up-destroyable';
|
1353
|
+
DESTROYER_KEY = 'up-destroyer';
|
1189
1354
|
|
1190
1355
|
/**
|
1356
|
+
Binds an event handler to the document, which will be executed whenever the
|
1357
|
+
given event is triggered on the given selector:
|
1191
1358
|
|
1359
|
+
up.on('click', '.button', function(event, $element) {
|
1360
|
+
console.log("Someone clicked the button %o", $element);
|
1361
|
+
});
|
1192
1362
|
|
1193
|
-
|
1194
|
-
@param {String} [options.viewport]
|
1195
|
-
@param {String} [options.fixedTop]
|
1196
|
-
@param {String} [options.fixedBottom]
|
1197
|
-
@param {Number} [options.duration]
|
1198
|
-
@param {String} [options.easing]
|
1199
|
-
@param {Number} [options.snap]
|
1200
|
-
*/
|
1201
|
-
config = u.config({
|
1202
|
-
duration: 0,
|
1203
|
-
viewport: 'body, .up-modal, [up-viewport]',
|
1204
|
-
fixedTop: '[up-fixed~=top]',
|
1205
|
-
fixedBottom: '[up-fixed~=bottom]',
|
1206
|
-
snap: 50,
|
1207
|
-
easing: 'swing'
|
1208
|
-
});
|
1209
|
-
reset = function() {
|
1210
|
-
return config.reset();
|
1211
|
-
};
|
1212
|
-
SCROLL_PROMISE_KEY = 'up-scroll-promise';
|
1213
|
-
|
1214
|
-
/**
|
1215
|
-
Scrolls the given viewport to the given Y-position.
|
1216
|
-
|
1217
|
-
A "viewport" is an element that has scrollbars, e.g. `<body>` or
|
1218
|
-
a container with `overflow-x: scroll`.
|
1363
|
+
This is roughly equivalent to binding a jQuery element to `document`.
|
1219
1364
|
|
1220
|
-
\#\#\#\# Example
|
1221
1365
|
|
1222
|
-
|
1366
|
+
\#\#\#\# Attaching structured data
|
1223
1367
|
|
1224
|
-
|
1368
|
+
In case you want to attach structured data to the event you're observing,
|
1369
|
+
you can serialize the data to JSON and put it into an `[up-data]` attribute:
|
1225
1370
|
|
1226
|
-
|
1371
|
+
<span class="person" up-data="{ age: 18, name: 'Bob' }">Bob</span>
|
1372
|
+
<span class="person" up-data="{ age: 22, name: 'Jim' }">Jim</span>
|
1227
1373
|
|
1228
|
-
The
|
1374
|
+
The JSON will parsed and handed to your event handler as a third argument:
|
1229
1375
|
|
1230
|
-
up.
|
1231
|
-
|
1232
|
-
duration: 250
|
1376
|
+
up.on('click', '.person', function(event, $element, data) {
|
1377
|
+
console.log("This is %o who is %o years old", data.name, data.age);
|
1233
1378
|
});
|
1234
1379
|
|
1235
|
-
If the given viewport is already in a scroll animation when `up.scroll`
|
1236
|
-
is called a second time, the previous animation will instantly jump to the
|
1237
|
-
last frame before the next animation is started.
|
1238
1380
|
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
@return {Deferred}
|
1250
|
-
A promise that will be resolved when the scrolling ends.
|
1251
|
-
*/
|
1252
|
-
scroll = function(viewport, scrollTop, options) {
|
1253
|
-
var $view, deferred, duration, easing, targetProps;
|
1254
|
-
$view = $(viewport);
|
1255
|
-
options = u.options(options);
|
1256
|
-
duration = u.option(options.duration, config.duration);
|
1257
|
-
easing = u.option(options.easing, config.easing);
|
1258
|
-
finishScrolling($view);
|
1259
|
-
if (duration > 0) {
|
1260
|
-
deferred = $.Deferred();
|
1261
|
-
$view.data(SCROLL_PROMISE_KEY, deferred);
|
1262
|
-
deferred.then(function() {
|
1263
|
-
$view.removeData(SCROLL_PROMISE_KEY);
|
1264
|
-
return $view.finish();
|
1381
|
+
\#\#\#\# Migrating jQuery event handlers to `up.on`
|
1382
|
+
|
1383
|
+
Within the event handler, Up.js will bind `this` to the
|
1384
|
+
native DOM element to help you migrate your existing jQuery code to
|
1385
|
+
this new syntax.
|
1386
|
+
|
1387
|
+
So if you had this before:
|
1388
|
+
|
1389
|
+
$(document).on('click', '.button', function() {
|
1390
|
+
$(this).something();
|
1265
1391
|
});
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
easing: easing,
|
1272
|
-
complete: function() {
|
1273
|
-
return deferred.resolve();
|
1274
|
-
}
|
1392
|
+
|
1393
|
+
... you can simply copy the event handler to `up.on`:
|
1394
|
+
|
1395
|
+
up.on('click', '.button', function() {
|
1396
|
+
$(this).something();
|
1275
1397
|
});
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
}
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1398
|
+
|
1399
|
+
|
1400
|
+
@method up.on
|
1401
|
+
@param {String} events
|
1402
|
+
A space-separated list of event names to bind.
|
1403
|
+
@param {String} selector
|
1404
|
+
The selector an on which the event must be triggered.
|
1405
|
+
@param {Function(event, $element, data)} behavior
|
1406
|
+
The handler that should be called.
|
1407
|
+
The function takes the affected element as the first argument (as a jQuery object).
|
1408
|
+
If the element has an `up-data` attribute, its value is parsed as JSON
|
1409
|
+
and passed as a second argument.
|
1286
1410
|
*/
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
}
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
measurePosition = function(obstructor, cssAttr) {
|
1298
|
-
var $obstructor, anchorPosition;
|
1299
|
-
$obstructor = $(obstructor);
|
1300
|
-
anchorPosition = $obstructor.css(cssAttr);
|
1301
|
-
if (!u.isPresent(anchorPosition)) {
|
1302
|
-
u.error("Fixed element %o must have a CSS attribute %o", $obstructor, cssAttr);
|
1303
|
-
}
|
1304
|
-
return parseInt(anchorPosition) + $obstructor.height();
|
1305
|
-
};
|
1306
|
-
fixedTopBottoms = (function() {
|
1307
|
-
var i, len, ref, results;
|
1308
|
-
ref = $(config.fixedTop);
|
1309
|
-
results = [];
|
1310
|
-
for (i = 0, len = ref.length; i < len; i++) {
|
1311
|
-
obstructor = ref[i];
|
1312
|
-
results.push(measurePosition(obstructor, 'top'));
|
1313
|
-
}
|
1314
|
-
return results;
|
1315
|
-
})();
|
1316
|
-
fixedBottomTops = (function() {
|
1317
|
-
var i, len, ref, results;
|
1318
|
-
ref = $(config.fixedBottom);
|
1319
|
-
results = [];
|
1320
|
-
for (i = 0, len = ref.length; i < len; i++) {
|
1321
|
-
obstructor = ref[i];
|
1322
|
-
results.push(measurePosition(obstructor, 'bottom'));
|
1411
|
+
liveDescriptions = [];
|
1412
|
+
defaultLiveDescriptions = null;
|
1413
|
+
live = function(events, selector, behavior) {
|
1414
|
+
var description, ref;
|
1415
|
+
if (!up.browser.isSupported()) {
|
1416
|
+
return;
|
1417
|
+
}
|
1418
|
+
description = [
|
1419
|
+
events, selector, function(event) {
|
1420
|
+
return behavior.apply(this, [event, $(this), data(this)]);
|
1323
1421
|
}
|
1324
|
-
|
1325
|
-
|
1326
|
-
return
|
1327
|
-
top: Math.max.apply(Math, [0].concat(slice.call(fixedTopBottoms))),
|
1328
|
-
bottom: Math.max.apply(Math, [0].concat(slice.call(fixedBottomTops)))
|
1329
|
-
};
|
1422
|
+
];
|
1423
|
+
liveDescriptions.push(description);
|
1424
|
+
return (ref = $(document)).on.apply(ref, description);
|
1330
1425
|
};
|
1331
1426
|
|
1332
1427
|
/**
|
1333
|
-
|
1334
|
-
is
|
1335
|
-
|
1336
|
-
By default Up.js will always reveal an element before
|
1337
|
-
updating it with Javascript functions like [`up.replace`](/up.flow#up.replace)
|
1338
|
-
or UJS behavior like [`[up-target]`](/up.link#up-target).
|
1428
|
+
Registers a function to be called whenever an element with
|
1429
|
+
the given selector is inserted into the DOM through Up.js.
|
1339
1430
|
|
1340
|
-
|
1431
|
+
This is a great way to integrate jQuery plugins.
|
1432
|
+
Let's say your Javascript plugin wants you to call `lightboxify()`
|
1433
|
+
on links that should open a lightbox. You decide to
|
1434
|
+
do this for all links with an `[rel=lightbox]` attribute:
|
1341
1435
|
|
1342
|
-
|
1343
|
-
|
1436
|
+
<a href="river.png" rel="lightbox">River</a>
|
1437
|
+
<a href="ocean.png" rel="lightbox">Ocean</a>
|
1344
1438
|
|
1345
|
-
|
1346
|
-
- an element with the attribute `[up-viewport]`
|
1347
|
-
- the `<body>` element
|
1348
|
-
- an element matching the selector you have configured using `up.viewport.defaults({ viewSelector: 'my-custom-selector' })`
|
1439
|
+
This Javascript will do exactly that:
|
1349
1440
|
|
1350
|
-
|
1441
|
+
up.compiler('a[rel=lightbox]', function($element) {
|
1442
|
+
$element.lightboxify();
|
1443
|
+
});
|
1351
1444
|
|
1352
|
-
|
1353
|
-
|
1445
|
+
Note that within the compiler, Up.js will bind `this` to the
|
1446
|
+
native DOM element to help you migrate your existing jQuery code to
|
1447
|
+
this new syntax.
|
1354
1448
|
|
1355
|
-
To make `up.aware` of these fixed elements you can either:
|
1356
1449
|
|
1357
|
-
|
1358
|
-
- [configure default options](#up.layout.defaults) for `fixedTop` or `fixedBottom`
|
1450
|
+
\#\#\#\# Custom elements
|
1359
1451
|
|
1360
|
-
|
1361
|
-
@param {String|Element|jQuery} element
|
1362
|
-
@param {String|Element|jQuery} [options.viewport]
|
1363
|
-
@param {Number} [options.duration]
|
1364
|
-
@param {String} [options.easing]
|
1365
|
-
@param {String} [options.snap]
|
1366
|
-
@return {Deferred}
|
1367
|
-
A promise that will be resolved when the element is revealed.
|
1368
|
-
*/
|
1369
|
-
reveal = function(elementOrSelector, options) {
|
1370
|
-
var $element, $viewport, elementDims, firstElementRow, lastElementRow, newScrollPos, obstruction, offsetShift, originalScrollPos, predictFirstVisibleRow, predictLastVisibleRow, snap, viewportHeight, viewportIsBody;
|
1371
|
-
options = u.options(options);
|
1372
|
-
$element = $(elementOrSelector);
|
1373
|
-
$viewport = findViewport($element, options.viewport);
|
1374
|
-
snap = u.option(options.snap, config.snap);
|
1375
|
-
viewportIsBody = $viewport.is('body');
|
1376
|
-
viewportHeight = viewportIsBody ? u.clientSize().height : $viewport.height();
|
1377
|
-
originalScrollPos = $viewport.scrollTop();
|
1378
|
-
newScrollPos = originalScrollPos;
|
1379
|
-
offsetShift = void 0;
|
1380
|
-
obstruction = void 0;
|
1381
|
-
if (viewportIsBody) {
|
1382
|
-
obstruction = measureObstruction();
|
1383
|
-
offsetShift = 0;
|
1384
|
-
} else {
|
1385
|
-
obstruction = {
|
1386
|
-
top: 0,
|
1387
|
-
bottom: 0
|
1388
|
-
};
|
1389
|
-
offsetShift = originalScrollPos;
|
1390
|
-
}
|
1391
|
-
predictFirstVisibleRow = function() {
|
1392
|
-
return newScrollPos + obstruction.top;
|
1393
|
-
};
|
1394
|
-
predictLastVisibleRow = function() {
|
1395
|
-
return newScrollPos + viewportHeight - obstruction.bottom - 1;
|
1396
|
-
};
|
1397
|
-
elementDims = u.measure($element, {
|
1398
|
-
relative: true
|
1399
|
-
});
|
1400
|
-
firstElementRow = elementDims.top + offsetShift;
|
1401
|
-
lastElementRow = firstElementRow + elementDims.height - 1;
|
1402
|
-
if (lastElementRow > predictLastVisibleRow()) {
|
1403
|
-
newScrollPos += lastElementRow - predictLastVisibleRow();
|
1404
|
-
}
|
1405
|
-
if (firstElementRow < predictFirstVisibleRow()) {
|
1406
|
-
newScrollPos = firstElementRow - obstruction.top;
|
1407
|
-
}
|
1408
|
-
if (newScrollPos < snap) {
|
1409
|
-
newScrollPos = 0;
|
1410
|
-
}
|
1411
|
-
if (newScrollPos !== originalScrollPos) {
|
1412
|
-
return scroll($viewport, newScrollPos, options);
|
1413
|
-
} else {
|
1414
|
-
return u.resolvedDeferred();
|
1415
|
-
}
|
1416
|
-
};
|
1417
|
-
|
1418
|
-
/**
|
1419
|
-
@private
|
1420
|
-
@method up.viewport.findViewport
|
1421
|
-
*/
|
1422
|
-
findViewport = function($element, viewportSelectorOrElement) {
|
1423
|
-
var $viewport, vieportSelector;
|
1424
|
-
$viewport = void 0;
|
1425
|
-
if (u.isJQuery(viewportSelectorOrElement)) {
|
1426
|
-
$viewport = viewportSelectorOrElement;
|
1427
|
-
} else {
|
1428
|
-
vieportSelector = u.presence(viewportSelectorOrElement) || config.viewport;
|
1429
|
-
$viewport = $element.closest(vieportSelector);
|
1430
|
-
}
|
1431
|
-
$viewport.length || u.error("Could not find viewport for %o", $element);
|
1432
|
-
return $viewport;
|
1433
|
-
};
|
1434
|
-
|
1435
|
-
/**
|
1436
|
-
Marks this element as a scrolling container. Apply this ttribute if your app uses
|
1437
|
-
a custom panel layout with fixed positioning instead of scrolling `<body>`.
|
1452
|
+
You can also use `up.compiler` to implement custom elements like this:
|
1438
1453
|
|
1439
|
-
|
1440
|
-
to the element that is being revealed. By default this is the `<body>` element.
|
1454
|
+
<clock></clock>
|
1441
1455
|
|
1442
|
-
|
1456
|
+
Here is the Javascript that inserts the current time into to these elements:
|
1443
1457
|
|
1444
|
-
|
1445
|
-
|
1458
|
+
up.compiler('clock', function($element) {
|
1459
|
+
var now = new Date();
|
1460
|
+
$element.text(now.toString()));
|
1461
|
+
});
|
1446
1462
|
|
1447
|
-
.side {
|
1448
|
-
position: fixed;
|
1449
|
-
top: 0;
|
1450
|
-
bottom: 0;
|
1451
|
-
left: 0;
|
1452
|
-
width: 100px;
|
1453
|
-
overflow-y: scroll;
|
1454
|
-
}
|
1455
1463
|
|
1456
|
-
|
1457
|
-
position: fixed;
|
1458
|
-
top: 0;
|
1459
|
-
bottom: 0;
|
1460
|
-
left: 100px;
|
1461
|
-
right: 0;
|
1462
|
-
overflow-y: scroll;
|
1463
|
-
}
|
1464
|
+
\#\#\#\# Cleaning up after yourself
|
1464
1465
|
|
1465
|
-
|
1466
|
+
If your compiler returns a function, Up.js will use this as a *destructor* to
|
1467
|
+
clean up if the element leaves the DOM. Note that in Up.js the same DOM ad Javascript environment
|
1468
|
+
will persist through many page loads, so it's important to not create
|
1469
|
+
[memory leaks](https://makandracards.com/makandra/31325-how-to-create-memory-leaks-in-jquery).
|
1466
1470
|
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
<a href="/emails/9002" up-target=".main">Fwd: Room reservation</a>
|
1471
|
-
</div>
|
1471
|
+
You should clean up after yourself whenever your compilers have global
|
1472
|
+
side effects, like a [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval)
|
1473
|
+
or event handlers bound to the document root.
|
1472
1474
|
|
1473
|
-
|
1474
|
-
|
1475
|
-
<p>
|
1476
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
|
1477
|
-
Stet clita kasd gubergren, no sea takimata sanctus est.
|
1478
|
-
</p>
|
1479
|
-
</div>
|
1475
|
+
Here is a version of `<clock>` that updates
|
1476
|
+
the time every second, and cleans up once it's done:
|
1480
1477
|
|
1481
|
-
|
1482
|
-
@ujs
|
1483
|
-
*/
|
1484
|
-
|
1485
|
-
/**
|
1486
|
-
Marks this element as a navigation fixed to the top edge of the screen
|
1487
|
-
using `position: fixed`.
|
1478
|
+
up.compiler('clock', function($element) {
|
1488
1479
|
|
1489
|
-
|
1490
|
-
|
1480
|
+
function update() {
|
1481
|
+
var now = new Date();
|
1482
|
+
$element.text(now.toString()));
|
1483
|
+
}
|
1491
1484
|
|
1492
|
-
|
1485
|
+
setInterval(update, 1000);
|
1493
1486
|
|
1494
|
-
|
1487
|
+
return function() {
|
1488
|
+
clearInterval(update);
|
1489
|
+
};
|
1495
1490
|
|
1496
|
-
|
1497
|
-
@ujs
|
1498
|
-
*/
|
1499
|
-
|
1500
|
-
/**
|
1501
|
-
Marks this element as a navigation fixed to the bottom edge of the screen
|
1502
|
-
using `position: fixed`.
|
1491
|
+
});
|
1503
1492
|
|
1504
|
-
|
1505
|
-
|
1493
|
+
If we didn't clean up after ourselves, we would have many ticking intervals
|
1494
|
+
operating on detached DOM elements after we have created and removed a couple
|
1495
|
+
of `<clock>` elements.
|
1506
1496
|
|
1507
|
-
Example:
|
1508
1497
|
|
1509
|
-
|
1498
|
+
\#\#\#\# Attaching structured data
|
1510
1499
|
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
return {
|
1516
|
-
reveal: reveal,
|
1517
|
-
scroll: scroll,
|
1518
|
-
finishScrolling: finishScrolling,
|
1519
|
-
defaults: config.update
|
1520
|
-
};
|
1521
|
-
})();
|
1522
|
-
|
1523
|
-
up.scroll = up.layout.scroll;
|
1524
|
-
|
1525
|
-
up.reveal = up.layout.reveal;
|
1526
|
-
|
1527
|
-
}).call(this);
|
1528
|
-
|
1529
|
-
/**
|
1530
|
-
Changing page fragments programmatically
|
1531
|
-
========================================
|
1532
|
-
|
1533
|
-
This module contains Up's core functions to insert, change
|
1534
|
-
or destroy page fragments.
|
1535
|
-
|
1536
|
-
\#\#\# Incomplete documentation!
|
1537
|
-
|
1538
|
-
We need to work on this page:
|
1539
|
-
|
1540
|
-
- Explain the UJS approach vs. pragmatic approach
|
1541
|
-
- Examples
|
1542
|
-
|
1543
|
-
|
1544
|
-
@class up.flow
|
1545
|
-
*/
|
1546
|
-
|
1547
|
-
(function() {
|
1548
|
-
up.flow = (function() {
|
1549
|
-
var autofocus, destroy, elementsInserted, findOldFragment, first, fragmentNotFound, implant, isRealElement, parseImplantSteps, parseResponse, reload, replace, reset, reveal, setSource, source, swapElements, u;
|
1550
|
-
u = up.util;
|
1551
|
-
setSource = function(element, sourceUrl) {
|
1552
|
-
var $element;
|
1553
|
-
$element = $(element);
|
1554
|
-
if (u.isPresent(sourceUrl)) {
|
1555
|
-
sourceUrl = u.normalizeUrl(sourceUrl);
|
1556
|
-
}
|
1557
|
-
return $element.attr("up-source", sourceUrl);
|
1558
|
-
};
|
1559
|
-
source = function(element) {
|
1560
|
-
var $element;
|
1561
|
-
$element = $(element).closest("[up-source]");
|
1562
|
-
return u.presence($element.attr("up-source")) || up.browser.url();
|
1563
|
-
};
|
1564
|
-
|
1565
|
-
/**
|
1566
|
-
Replaces elements on the current page with corresponding elements
|
1567
|
-
from a new page fetched from the server.
|
1500
|
+
In case you want to attach structured data to the event you're observing,
|
1501
|
+
you can serialize the data to JSON and put it into an `[up-data]` attribute.
|
1502
|
+
For instance, a container for a [Google Map](https://developers.google.com/maps/documentation/javascript/tutorial)
|
1503
|
+
might attach the location and names of its marker pins:
|
1568
1504
|
|
1569
|
-
|
1505
|
+
<div class="google-map" up-data="[
|
1506
|
+
{ lat: 48.36, lng: 10.99, title: 'Friedberg' },
|
1507
|
+
{ lat: 48.75, lng: 11.45, title: 'Ingolstadt' }
|
1508
|
+
]"></div>
|
1570
1509
|
|
1571
|
-
|
1572
|
-
@param {String|Element|jQuery} selectorOrElement
|
1573
|
-
The CSS selector to update. You can also pass a DOM element or jQuery element
|
1574
|
-
here, in which case a selector will be inferred from the element's class and ID.
|
1575
|
-
@param {String} url
|
1576
|
-
The URL to fetch from the server.
|
1577
|
-
@param {String} [options.method='get']
|
1578
|
-
@param {String} [options.title]
|
1579
|
-
@param {String} [options.transition='none']
|
1580
|
-
@param {String|Boolean} [options.history=true]
|
1581
|
-
If a `String` is given, it is used as the URL the browser's location bar and history.
|
1582
|
-
If omitted or true, the `url` argument will be used.
|
1583
|
-
If set to `false`, the history will remain unchanged.
|
1584
|
-
@param {String|Boolean} [options.source=true]
|
1585
|
-
@param {String} [options.scroll]
|
1586
|
-
Up.js will try to [reveal](/up.layout#up.reveal) the element being updated, by
|
1587
|
-
scrolling its containing viewport. Set this option to `false` to prevent any scrolling.
|
1510
|
+
The JSON will parsed and handed to your event handler as a second argument:
|
1588
1511
|
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1512
|
+
up.compiler('.google-map', function($element, pins) {
|
1513
|
+
|
1514
|
+
var map = new google.maps.Map($element);
|
1515
|
+
|
1516
|
+
pins.forEach(function(pin) {
|
1517
|
+
var position = new google.maps.LatLng(pin.lat, pin.lng);
|
1518
|
+
new google.maps.Marker({
|
1519
|
+
position: position,
|
1520
|
+
map: map,
|
1521
|
+
title: pin.title
|
1522
|
+
});
|
1523
|
+
});
|
1524
|
+
|
1525
|
+
});
|
1526
|
+
|
1527
|
+
|
1528
|
+
\#\#\#\# Migrating jQuery event handlers to `up.on`
|
1529
|
+
|
1530
|
+
Within the compiler, Up.js will bind `this` to the
|
1531
|
+
native DOM element to help you migrate your existing jQuery code to
|
1532
|
+
this new syntax.
|
1533
|
+
|
1534
|
+
|
1535
|
+
@method up.compiler
|
1536
|
+
@param {String} selector
|
1537
|
+
The selector to match.
|
1538
|
+
@param {Boolean} [options.batch=false]
|
1539
|
+
If set to `true` and a fragment insertion contains multiple
|
1540
|
+
elements matching the selector, `compiler` is only called once
|
1541
|
+
with a jQuery collection containing all matching elements.
|
1542
|
+
@param {Function($element, data)} compiler
|
1543
|
+
The function to call when a matching element is inserted.
|
1544
|
+
The function takes the new element as the first argument (as a jQuery object).
|
1545
|
+
If the element has an `up-data` attribute, its value is parsed as JSON
|
1546
|
+
and passed as a second argument.
|
1547
|
+
|
1548
|
+
The function may return a destructor function that destroys the compiled
|
1549
|
+
object before it is removed from the DOM. The destructor is supposed to
|
1550
|
+
clear global state such as time-outs and event handlers bound to the document.
|
1551
|
+
The destructor is *not* expected to remove the element from the DOM, which
|
1552
|
+
is already handled by [`up.destroy`](/up.flow#up.destroy).
|
1595
1553
|
*/
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
}
|
1604
|
-
return u.resolvedPromise();
|
1554
|
+
compilers = [];
|
1555
|
+
defaultCompilers = null;
|
1556
|
+
compiler = function() {
|
1557
|
+
var args, options, selector;
|
1558
|
+
selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
1559
|
+
if (!up.browser.isSupported()) {
|
1560
|
+
return;
|
1605
1561
|
}
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1562
|
+
compiler = args.pop();
|
1563
|
+
options = u.options(args[0], {
|
1564
|
+
batch: false
|
1565
|
+
});
|
1566
|
+
return compilers.push({
|
1609
1567
|
selector: selector,
|
1610
|
-
|
1611
|
-
|
1568
|
+
callback: compiler,
|
1569
|
+
batch: options.batch
|
1570
|
+
});
|
1571
|
+
};
|
1572
|
+
applyCompiler = function(compiler, $jqueryElement, nativeElement) {
|
1573
|
+
var destroyer;
|
1574
|
+
u.debug("Applying compiler %o on %o", compiler.selector, nativeElement);
|
1575
|
+
destroyer = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)]);
|
1576
|
+
if (u.isFunction(destroyer)) {
|
1577
|
+
$jqueryElement.addClass(DESTROYABLE_CLASS);
|
1578
|
+
return $jqueryElement.data(DESTROYER_KEY, destroyer);
|
1579
|
+
}
|
1580
|
+
};
|
1581
|
+
compile = function($fragment) {
|
1582
|
+
var $matches, i, len, results;
|
1583
|
+
u.debug("Compiling fragment %o", $fragment);
|
1584
|
+
results = [];
|
1585
|
+
for (i = 0, len = compilers.length; i < len; i++) {
|
1586
|
+
compiler = compilers[i];
|
1587
|
+
$matches = u.findWithSelf($fragment, compiler.selector);
|
1588
|
+
if ($matches.length) {
|
1589
|
+
if (compiler.batch) {
|
1590
|
+
results.push(applyCompiler(compiler, $matches, $matches.get()));
|
1591
|
+
} else {
|
1592
|
+
results.push($matches.each(function() {
|
1593
|
+
return applyCompiler(compiler, $(this), this);
|
1594
|
+
}));
|
1595
|
+
}
|
1596
|
+
} else {
|
1597
|
+
results.push(void 0);
|
1598
|
+
}
|
1599
|
+
}
|
1600
|
+
return results;
|
1601
|
+
};
|
1602
|
+
destroy = function($fragment) {
|
1603
|
+
return u.findWithSelf($fragment, "." + DESTROYABLE_CLASS).each(function() {
|
1604
|
+
var $element, destroyer;
|
1605
|
+
$element = $(this);
|
1606
|
+
destroyer = $element.data(DESTROYER_KEY);
|
1607
|
+
return destroyer();
|
1608
|
+
});
|
1609
|
+
};
|
1610
|
+
|
1611
|
+
/**
|
1612
|
+
Checks if the given element has an `up-data` attribute.
|
1613
|
+
If yes, parses the attribute value as JSON and returns the parsed object.
|
1614
|
+
|
1615
|
+
Returns an empty object if the element has no `up-data` attribute.
|
1616
|
+
|
1617
|
+
The API of this method is likely to change in the future, so
|
1618
|
+
we can support getting or setting individual keys.
|
1619
|
+
|
1620
|
+
@protected
|
1621
|
+
@method up.magic.data
|
1622
|
+
@param {String|Element|jQuery} elementOrSelector
|
1623
|
+
*/
|
1624
|
+
|
1625
|
+
/*
|
1626
|
+
Stores a JSON-string with the element.
|
1627
|
+
|
1628
|
+
If an element annotated with [`up-data`] is inserted into the DOM,
|
1629
|
+
Up will parse the JSON and pass the resulting object to any matching
|
1630
|
+
[`up.compiler`](/up.magic#up.magic.compiler) handlers.
|
1631
|
+
|
1632
|
+
Similarly, when an event is triggered on an element annotated with
|
1633
|
+
[`up-data`], the parsed object will be passed to any matching
|
1634
|
+
[`up.on`](/up.magic#up.on) handlers.
|
1635
|
+
|
1636
|
+
@ujs
|
1637
|
+
@method [up-data]
|
1638
|
+
@param {JSON} [up-data]
|
1639
|
+
*/
|
1640
|
+
data = function(elementOrSelector) {
|
1641
|
+
var $element, json;
|
1642
|
+
$element = $(elementOrSelector);
|
1643
|
+
json = $element.attr('up-data');
|
1644
|
+
if (u.isString(json) && u.trim(json) !== '') {
|
1645
|
+
return JSON.parse(json);
|
1646
|
+
} else {
|
1647
|
+
return {};
|
1648
|
+
}
|
1649
|
+
};
|
1650
|
+
|
1651
|
+
/**
|
1652
|
+
Makes a snapshot of the currently registered event listeners,
|
1653
|
+
to later be restored through [`up.bus.reset`](/up.bus#up.bus.reset).
|
1654
|
+
|
1655
|
+
@private
|
1656
|
+
@method up.magic.snapshot
|
1657
|
+
*/
|
1658
|
+
snapshot = function() {
|
1659
|
+
defaultLiveDescriptions = u.copy(liveDescriptions);
|
1660
|
+
return defaultCompilers = u.copy(compilers);
|
1661
|
+
};
|
1662
|
+
|
1663
|
+
/**
|
1664
|
+
Resets the list of registered event listeners to the
|
1665
|
+
moment when the framework was booted.
|
1666
|
+
|
1667
|
+
@private
|
1668
|
+
@method up.magic.reset
|
1669
|
+
*/
|
1670
|
+
reset = function() {
|
1671
|
+
var description, i, len, ref;
|
1672
|
+
for (i = 0, len = liveDescriptions.length; i < len; i++) {
|
1673
|
+
description = liveDescriptions[i];
|
1674
|
+
if (!u.contains(defaultLiveDescriptions, description)) {
|
1675
|
+
(ref = $(document)).off.apply(ref, description);
|
1676
|
+
}
|
1677
|
+
}
|
1678
|
+
liveDescriptions = u.copy(defaultLiveDescriptions);
|
1679
|
+
return compilers = u.copy(defaultCompilers);
|
1680
|
+
};
|
1681
|
+
|
1682
|
+
/**
|
1683
|
+
Sends a notification that the given element has been inserted
|
1684
|
+
into the DOM. This causes Up.js to compile the fragment (apply
|
1685
|
+
event listeners, etc.).
|
1686
|
+
|
1687
|
+
This method is called automatically if you change elements through
|
1688
|
+
other Up.js methods. You will only need to call this if you
|
1689
|
+
manipulate the DOM without going through Up.js.
|
1690
|
+
|
1691
|
+
@method up.ready
|
1692
|
+
@param {String|Element|jQuery} selectorOrFragment
|
1693
|
+
*/
|
1694
|
+
ready = function(selectorOrFragment) {
|
1695
|
+
var $fragment;
|
1696
|
+
$fragment = $(selectorOrFragment);
|
1697
|
+
up.bus.emit('fragment:ready', $fragment);
|
1698
|
+
return $fragment;
|
1699
|
+
};
|
1700
|
+
onEscape = function(handler) {
|
1701
|
+
return live('keydown', 'body', function(event) {
|
1702
|
+
if (u.escapePressed(event)) {
|
1703
|
+
return handler(event);
|
1704
|
+
}
|
1705
|
+
});
|
1706
|
+
};
|
1707
|
+
up.bus.on('app:ready', (function() {
|
1708
|
+
return ready(document.body);
|
1709
|
+
}));
|
1710
|
+
up.bus.on('fragment:ready', compile);
|
1711
|
+
up.bus.on('fragment:destroy', destroy);
|
1712
|
+
up.bus.on('framework:ready', snapshot);
|
1713
|
+
up.bus.on('framework:reset', reset);
|
1714
|
+
return {
|
1715
|
+
compiler: compiler,
|
1716
|
+
on: live,
|
1717
|
+
ready: ready,
|
1718
|
+
onEscape: onEscape,
|
1719
|
+
data: data
|
1720
|
+
};
|
1721
|
+
})();
|
1722
|
+
|
1723
|
+
up.compiler = up.magic.compiler;
|
1724
|
+
|
1725
|
+
up.on = up.magic.on;
|
1726
|
+
|
1727
|
+
up.ready = up.magic.ready;
|
1728
|
+
|
1729
|
+
up.awaken = function() {
|
1730
|
+
var args;
|
1731
|
+
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
1732
|
+
up.util.warn("up.awaken has been renamed to up.compiler and will be removed in a future version");
|
1733
|
+
return up.compiler.apply(up, args);
|
1734
|
+
};
|
1735
|
+
|
1736
|
+
}).call(this);
|
1737
|
+
|
1738
|
+
/**
|
1739
|
+
Manipulating the browser history
|
1740
|
+
=======
|
1741
|
+
|
1742
|
+
\#\#\# Incomplete documentation!
|
1743
|
+
|
1744
|
+
We need to work on this page:
|
1745
|
+
|
1746
|
+
- Explain how the other modules manipulate history
|
1747
|
+
- Decide whether we want to expose these methods as public API
|
1748
|
+
- Document methods and parameters
|
1749
|
+
|
1750
|
+
@class up.history
|
1751
|
+
*/
|
1752
|
+
|
1753
|
+
(function() {
|
1754
|
+
up.history = (function() {
|
1755
|
+
var buildState, config, currentUrl, isCurrentUrl, manipulate, nextPreviousUrl, normalizeUrl, observeNewUrl, pop, previousUrl, push, register, replace, reset, restoreStateOnPop, u;
|
1756
|
+
u = up.util;
|
1757
|
+
|
1758
|
+
/**
|
1759
|
+
@method up.history.defaults
|
1760
|
+
@param {Array<String>} [options.popTargets=['body']]
|
1761
|
+
An array of CSS selectors to replace when the user goes
|
1762
|
+
back in history.
|
1763
|
+
@param {Boolean} [options.restoreScroll=true]
|
1764
|
+
Whether to restore the known scroll positions
|
1765
|
+
when the user goes back or forward in history.
|
1766
|
+
*/
|
1767
|
+
config = u.config({
|
1768
|
+
popTargets: ['body'],
|
1769
|
+
restoreScroll: true
|
1770
|
+
});
|
1771
|
+
|
1772
|
+
/**
|
1773
|
+
Returns the previous URL in the browser history.
|
1774
|
+
|
1775
|
+
Note that this will only work reliably for history changes that
|
1776
|
+
were applied by [`up.history.push`](#up.history.replace) or
|
1777
|
+
[`up.history.replace`](#up.history.replace).
|
1778
|
+
|
1779
|
+
@method up.history.previousUrl
|
1780
|
+
@protected
|
1781
|
+
*/
|
1782
|
+
previousUrl = void 0;
|
1783
|
+
nextPreviousUrl = void 0;
|
1784
|
+
reset = function() {
|
1785
|
+
config.reset();
|
1786
|
+
previousUrl = void 0;
|
1787
|
+
return nextPreviousUrl = void 0;
|
1788
|
+
};
|
1789
|
+
normalizeUrl = function(url) {
|
1790
|
+
return u.normalizeUrl(url, {
|
1791
|
+
hash: true
|
1792
|
+
});
|
1793
|
+
};
|
1794
|
+
|
1795
|
+
/**
|
1796
|
+
Returns a normalized URL for the current history entry.
|
1797
|
+
|
1798
|
+
@method up.history.url
|
1799
|
+
@protected
|
1800
|
+
*/
|
1801
|
+
currentUrl = function() {
|
1802
|
+
return normalizeUrl(up.browser.url());
|
1803
|
+
};
|
1804
|
+
isCurrentUrl = function(url) {
|
1805
|
+
return normalizeUrl(url) === currentUrl();
|
1806
|
+
};
|
1807
|
+
observeNewUrl = function(url) {
|
1808
|
+
console.log("observing new url %o", url);
|
1809
|
+
if (nextPreviousUrl) {
|
1810
|
+
previousUrl = nextPreviousUrl;
|
1811
|
+
nextPreviousUrl = void 0;
|
1812
|
+
}
|
1813
|
+
return nextPreviousUrl = url;
|
1814
|
+
};
|
1815
|
+
|
1816
|
+
/**
|
1817
|
+
@method up.history.replace
|
1818
|
+
@param {String} url
|
1819
|
+
@param {Boolean} [options.force=false]
|
1820
|
+
@protected
|
1821
|
+
*/
|
1822
|
+
replace = function(url, options) {
|
1823
|
+
return manipulate('replace', url, options);
|
1824
|
+
};
|
1825
|
+
|
1826
|
+
/**
|
1827
|
+
@method up.history.push
|
1828
|
+
@param {String} url
|
1829
|
+
@protected
|
1830
|
+
*/
|
1831
|
+
push = function(url, options) {
|
1832
|
+
return manipulate('push', url, options);
|
1833
|
+
};
|
1834
|
+
manipulate = function(method, url, options) {
|
1835
|
+
var fullMethod, state;
|
1836
|
+
options = u.options(options, {
|
1837
|
+
force: false
|
1838
|
+
});
|
1839
|
+
if (options.force || !isCurrentUrl(url)) {
|
1840
|
+
if (up.browser.canPushState()) {
|
1841
|
+
fullMethod = method + "State";
|
1842
|
+
state = buildState();
|
1843
|
+
u.debug("Changing history to URL %o (%o)", url, method);
|
1844
|
+
window.history[fullMethod](state, '', url);
|
1845
|
+
return observeNewUrl(currentUrl());
|
1846
|
+
} else {
|
1847
|
+
return u.error("This browser doesn't support history.pushState");
|
1848
|
+
}
|
1849
|
+
}
|
1850
|
+
};
|
1851
|
+
buildState = function() {
|
1852
|
+
return {
|
1853
|
+
fromUp: true
|
1612
1854
|
};
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1855
|
+
};
|
1856
|
+
restoreStateOnPop = function(state) {
|
1857
|
+
var popSelector, url;
|
1858
|
+
url = currentUrl();
|
1859
|
+
u.debug("Restoring state %o (now on " + url + ")", state);
|
1860
|
+
popSelector = config.popTargets.join(', ');
|
1861
|
+
return up.replace(popSelector, url, {
|
1862
|
+
history: false,
|
1863
|
+
reveal: false,
|
1864
|
+
transition: 'none',
|
1865
|
+
saveScroll: false,
|
1866
|
+
restoreScroll: config.restoreScroll
|
1867
|
+
});
|
1868
|
+
};
|
1869
|
+
pop = function(event) {
|
1870
|
+
var state;
|
1871
|
+
u.debug("History state popped to URL %o", currentUrl());
|
1872
|
+
observeNewUrl(currentUrl());
|
1873
|
+
up.layout.saveScroll({
|
1874
|
+
url: previousUrl
|
1875
|
+
});
|
1876
|
+
state = event.originalEvent.state;
|
1877
|
+
if (state != null ? state.fromUp : void 0) {
|
1878
|
+
return restoreStateOnPop(state);
|
1879
|
+
} else {
|
1880
|
+
return u.debug('Discarding unknown state %o', state);
|
1881
|
+
}
|
1882
|
+
};
|
1883
|
+
if (up.browser.canPushState()) {
|
1884
|
+
register = function() {
|
1885
|
+
$(window).on("popstate", pop);
|
1886
|
+
return replace(currentUrl(), {
|
1887
|
+
force: true
|
1888
|
+
});
|
1889
|
+
};
|
1890
|
+
if (typeof jasmine !== "undefined" && jasmine !== null) {
|
1891
|
+
register();
|
1892
|
+
} else {
|
1893
|
+
setTimeout(register, 100);
|
1894
|
+
}
|
1895
|
+
}
|
1896
|
+
|
1897
|
+
/**
|
1898
|
+
Changes the link's destination so it points to the previous URL.
|
1899
|
+
|
1900
|
+
Note that this will *not* call `location.back()`, but will set
|
1901
|
+
the link's `up-href` attribute to the actual, previous URL.
|
1902
|
+
|
1903
|
+
\#\#\#\# Under the hood
|
1904
|
+
|
1905
|
+
This link ...
|
1906
|
+
|
1907
|
+
<a href="/default" up-back>
|
1908
|
+
Go back
|
1909
|
+
</a>
|
1910
|
+
|
1911
|
+
... will be transformed to:
|
1912
|
+
|
1913
|
+
<a href="/default" up-href="/previous-page" up-restore-scroll up-follow>
|
1914
|
+
Goback
|
1915
|
+
</a>
|
1916
|
+
|
1917
|
+
@ujs
|
1918
|
+
@method [up-back]
|
1919
|
+
*/
|
1920
|
+
up.compiler('[up-back]', function($link) {
|
1921
|
+
console.log("up-back", $link, previousUrl);
|
1922
|
+
if (u.isPresent(previousUrl)) {
|
1923
|
+
u.setMissingAttrs($link, {
|
1924
|
+
'up-href': previousUrl,
|
1925
|
+
'up-restore-scroll': ''
|
1926
|
+
});
|
1927
|
+
$link.removeAttr('up-back');
|
1928
|
+
return up.link.makeFollowable($link);
|
1929
|
+
}
|
1930
|
+
});
|
1931
|
+
up.bus.on('framework:reset', reset);
|
1932
|
+
return {
|
1933
|
+
defaults: config.update,
|
1934
|
+
push: push,
|
1935
|
+
replace: replace,
|
1936
|
+
url: currentUrl,
|
1937
|
+
previousUrl: function() {
|
1938
|
+
return previousUrl;
|
1939
|
+
},
|
1940
|
+
normalizeUrl: normalizeUrl
|
1941
|
+
};
|
1942
|
+
})();
|
1943
|
+
|
1944
|
+
}).call(this);
|
1945
|
+
|
1946
|
+
/**
|
1947
|
+
Viewport scrolling
|
1948
|
+
==================
|
1949
|
+
|
1950
|
+
This modules contains functions to scroll the viewport and reveal contained elements.
|
1951
|
+
|
1952
|
+
@class up.layout
|
1953
|
+
*/
|
1954
|
+
|
1955
|
+
(function() {
|
1956
|
+
var slice = [].slice;
|
1957
|
+
|
1958
|
+
up.layout = (function() {
|
1959
|
+
var SCROLL_PROMISE_KEY, config, finishScrolling, lastScrollTops, measureObstruction, reset, restoreScroll, reveal, saveScroll, scroll, scrollTops, u, viewportOf, viewportSelector, viewports, viewportsIn;
|
1960
|
+
u = up.util;
|
1961
|
+
|
1962
|
+
/**
|
1963
|
+
Configures the application layout.
|
1964
|
+
|
1965
|
+
@method up.layout.defaults
|
1966
|
+
@param {Array<String>} [options.viewports]
|
1967
|
+
An array of CSS selectors that find viewports
|
1968
|
+
(containers that scroll their contents).
|
1969
|
+
@param {Array<String>} [options.fixedTop]
|
1970
|
+
An array of CSS selectors that find elements fixed to the
|
1971
|
+
top edge of the screen (using `position: fixed`).
|
1972
|
+
@param {Array<String>} [options.fixedBottom]
|
1973
|
+
An array of CSS selectors that find elements fixed to the
|
1974
|
+
bottom edge of the screen (using `position: fixed`).
|
1975
|
+
@param {Number} [options.duration]
|
1976
|
+
The duration of the scrolling animation in milliseconds.
|
1977
|
+
Setting this to `0` will disable scrolling animations.
|
1978
|
+
@param {String} [options.easing]
|
1979
|
+
The timing function that controls the animation's acceleration.
|
1980
|
+
See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
|
1981
|
+
for a list of pre-defined timing functions.
|
1982
|
+
@param {Number} [options.snap]
|
1983
|
+
When [revealing](#up.reveal) elements, Up.js will scroll an viewport
|
1984
|
+
to the top when the revealed element is closer to the top than `options.snap`.
|
1985
|
+
*/
|
1986
|
+
config = u.config({
|
1987
|
+
duration: 0,
|
1988
|
+
viewports: ['body', '.up-modal', '[up-viewport]'],
|
1989
|
+
fixedTop: ['[up-fixed~=top]'],
|
1990
|
+
fixedBottom: ['[up-fixed~=bottom]'],
|
1991
|
+
snap: 50,
|
1992
|
+
easing: 'swing'
|
1993
|
+
});
|
1994
|
+
lastScrollTops = u.cache({
|
1995
|
+
size: 30,
|
1996
|
+
key: up.history.normalizeUrl
|
1997
|
+
});
|
1998
|
+
reset = function() {
|
1999
|
+
config.reset();
|
2000
|
+
return lastScrollTops.clear();
|
2001
|
+
};
|
2002
|
+
SCROLL_PROMISE_KEY = 'up-scroll-promise';
|
2003
|
+
|
2004
|
+
/**
|
2005
|
+
Scrolls the given viewport to the given Y-position.
|
2006
|
+
|
2007
|
+
A "viewport" is an element that has scrollbars, e.g. `<body>` or
|
2008
|
+
a container with `overflow-x: scroll`.
|
2009
|
+
|
2010
|
+
\#\#\#\# Example
|
2011
|
+
|
2012
|
+
This will scroll a `<div class="main">...</div>` to a Y-position of 100 pixels:
|
2013
|
+
|
2014
|
+
up.scoll('.main', 100);
|
2015
|
+
|
2016
|
+
\#\#\#\# Animating the scrolling motion
|
2017
|
+
|
2018
|
+
The scrolling can (optionally) be animated.
|
2019
|
+
|
2020
|
+
up.scoll('.main', 100, {
|
2021
|
+
easing: 'swing',
|
2022
|
+
duration: 250
|
2023
|
+
});
|
2024
|
+
|
2025
|
+
If the given viewport is already in a scroll animation when `up.scroll`
|
2026
|
+
is called a second time, the previous animation will instantly jump to the
|
2027
|
+
last frame before the next animation is started.
|
2028
|
+
|
2029
|
+
@protected
|
2030
|
+
@method up.scroll
|
2031
|
+
@param {String|Element|jQuery} viewport
|
2032
|
+
The container element to scroll.
|
2033
|
+
@param {Number} scrollPos
|
2034
|
+
The absolute number of pixels to set the scroll position to.
|
2035
|
+
@param {Number}[options.duration]
|
2036
|
+
The number of miliseconds for the scrolling's animation.
|
2037
|
+
@param {String}[options.easing]
|
2038
|
+
The timing function that controls the acceleration for the scrolling's animation.
|
2039
|
+
@return {Deferred}
|
2040
|
+
A promise that will be resolved when the scrolling ends.
|
2041
|
+
*/
|
2042
|
+
scroll = function(viewport, scrollTop, options) {
|
2043
|
+
var $viewport, deferred, duration, easing, targetProps;
|
2044
|
+
$viewport = $(viewport);
|
2045
|
+
options = u.options(options);
|
2046
|
+
duration = u.option(options.duration, config.duration);
|
2047
|
+
easing = u.option(options.easing, config.easing);
|
2048
|
+
finishScrolling($viewport);
|
2049
|
+
if (duration > 0) {
|
2050
|
+
deferred = $.Deferred();
|
2051
|
+
$viewport.data(SCROLL_PROMISE_KEY, deferred);
|
2052
|
+
deferred.then(function() {
|
2053
|
+
$viewport.removeData(SCROLL_PROMISE_KEY);
|
2054
|
+
return $viewport.finish();
|
2055
|
+
});
|
2056
|
+
targetProps = {
|
2057
|
+
scrollTop: scrollTop
|
2058
|
+
};
|
2059
|
+
$viewport.animate(targetProps, {
|
2060
|
+
duration: duration,
|
2061
|
+
easing: easing,
|
2062
|
+
complete: function() {
|
2063
|
+
return deferred.resolve();
|
2064
|
+
}
|
2065
|
+
});
|
2066
|
+
return deferred;
|
2067
|
+
} else {
|
2068
|
+
$viewport.scrollTop(scrollTop);
|
2069
|
+
return u.resolvedDeferred();
|
2070
|
+
}
|
2071
|
+
};
|
2072
|
+
|
2073
|
+
/**
|
2074
|
+
@method up.viewport.finishScrolling
|
2075
|
+
@private
|
2076
|
+
*/
|
2077
|
+
finishScrolling = function(elementOrSelector) {
|
2078
|
+
return $(elementOrSelector).each(function() {
|
2079
|
+
var existingScrolling;
|
2080
|
+
if (existingScrolling = $(this).data(SCROLL_PROMISE_KEY)) {
|
2081
|
+
return existingScrolling.resolve();
|
1625
2082
|
}
|
1626
|
-
|
1627
|
-
|
2083
|
+
});
|
2084
|
+
};
|
2085
|
+
measureObstruction = function() {
|
2086
|
+
var fixedBottomTops, fixedTopBottoms, measurePosition, obstructor;
|
2087
|
+
measurePosition = function(obstructor, cssAttr) {
|
2088
|
+
var $obstructor, anchorPosition;
|
2089
|
+
$obstructor = $(obstructor);
|
2090
|
+
anchorPosition = $obstructor.css(cssAttr);
|
2091
|
+
if (!u.isPresent(anchorPosition)) {
|
2092
|
+
u.error("Fixed element %o must have a CSS attribute %o", $obstructor, cssAttr);
|
1628
2093
|
}
|
1629
|
-
|
1630
|
-
|
2094
|
+
return parseInt(anchorPosition) + $obstructor.height();
|
2095
|
+
};
|
2096
|
+
fixedTopBottoms = (function() {
|
2097
|
+
var i, len, ref, results;
|
2098
|
+
ref = $(config.fixedTop.join(', '));
|
2099
|
+
results = [];
|
2100
|
+
for (i = 0, len = ref.length; i < len; i++) {
|
2101
|
+
obstructor = ref[i];
|
2102
|
+
results.push(measurePosition(obstructor, 'top'));
|
1631
2103
|
}
|
1632
|
-
|
1633
|
-
|
2104
|
+
return results;
|
2105
|
+
})();
|
2106
|
+
fixedBottomTops = (function() {
|
2107
|
+
var i, len, ref, results;
|
2108
|
+
ref = $(config.fixedBottom.join(', '));
|
2109
|
+
results = [];
|
2110
|
+
for (i = 0, len = ref.length; i < len; i++) {
|
2111
|
+
obstructor = ref[i];
|
2112
|
+
results.push(measurePosition(obstructor, 'bottom'));
|
1634
2113
|
}
|
1635
|
-
|
1636
|
-
|
1637
|
-
return
|
2114
|
+
return results;
|
2115
|
+
})();
|
2116
|
+
return {
|
2117
|
+
top: Math.max.apply(Math, [0].concat(slice.call(fixedTopBottoms))),
|
2118
|
+
bottom: Math.max.apply(Math, [0].concat(slice.call(fixedBottomTops)))
|
2119
|
+
};
|
1638
2120
|
};
|
1639
2121
|
|
1640
2122
|
/**
|
1641
|
-
|
1642
|
-
|
2123
|
+
Scroll's the given element's viewport so the element
|
2124
|
+
is visible for the user.
|
1643
2125
|
|
1644
|
-
|
2126
|
+
By default Up.js will always reveal an element before
|
2127
|
+
updating it with Javascript functions like [`up.replace`](/up.flow#up.replace)
|
2128
|
+
or UJS behavior like [`[up-target]`](/up.link#up-target).
|
1645
2129
|
|
1646
|
-
|
1647
|
-
'<div class="middle">new-middle</div>' +
|
1648
|
-
'<div class="after">new-after</div>';
|
2130
|
+
\#\#\#\# How Up.js finds the viewport
|
1649
2131
|
|
1650
|
-
|
2132
|
+
The viewport (the container that is going to be scrolled)
|
2133
|
+
is the closest parent of the element that is either:
|
1651
2134
|
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
2135
|
+
- the currently open [modal](/up.modal)
|
2136
|
+
- an element with the attribute `[up-viewport]`
|
2137
|
+
- the `<body>` element
|
2138
|
+
- an element matching the selector you have configured using `up.viewport.defaults({ viewSelector: 'my-custom-selector' })`
|
2139
|
+
|
2140
|
+
\#\#\#\# Fixed elements obstruction the viewport
|
2141
|
+
|
2142
|
+
Many applications have a navigation bar fixed to the top or bottom,
|
2143
|
+
obstructing the view on an element.
|
2144
|
+
|
2145
|
+
To make `up.aware` of these fixed elements you can either:
|
2146
|
+
|
2147
|
+
- give the element an attribute [`up-fixed="top"`](#up-fixed-top) or [`up-fixed="bottom"`](up-fixed-bottom)
|
2148
|
+
- [configure default options](#up.layout.defaults) for `fixedTop` or `fixedBottom`
|
2149
|
+
|
2150
|
+
@method up.reveal
|
2151
|
+
@param {String|Element|jQuery} element
|
2152
|
+
@param {String|Element|jQuery} [options.viewport]
|
2153
|
+
@param {Number} [options.duration]
|
2154
|
+
@param {String} [options.easing]
|
2155
|
+
@param {String} [options.snap]
|
2156
|
+
@return {Deferred}
|
2157
|
+
A promise that will be resolved when the element is revealed.
|
1662
2158
|
*/
|
1663
|
-
|
1664
|
-
var $
|
1665
|
-
options = u.options(options
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
}
|
1685
|
-
return results;
|
1686
|
-
};
|
1687
|
-
findOldFragment = function(selector) {
|
1688
|
-
return first(".up-popup " + selector) || first(".up-modal " + selector) || first(selector) || fragmentNotFound(selector);
|
1689
|
-
};
|
1690
|
-
fragmentNotFound = function(selector) {
|
1691
|
-
var message;
|
1692
|
-
message = 'Could not find selector %o in current body HTML';
|
1693
|
-
if (message[0] === '#') {
|
1694
|
-
message += ' (avoid using IDs)';
|
2159
|
+
reveal = function(elementOrSelector, options) {
|
2160
|
+
var $element, $viewport, elementDims, firstElementRow, lastElementRow, newScrollPos, obstruction, offsetShift, originalScrollPos, predictFirstVisibleRow, predictLastVisibleRow, snap, viewportHeight, viewportIsBody;
|
2161
|
+
options = u.options(options);
|
2162
|
+
$element = $(elementOrSelector);
|
2163
|
+
$viewport = viewportOf($element, options.viewport);
|
2164
|
+
snap = u.option(options.snap, config.snap);
|
2165
|
+
viewportIsBody = $viewport.is('body');
|
2166
|
+
viewportHeight = viewportIsBody ? u.clientSize().height : $viewport.height();
|
2167
|
+
originalScrollPos = $viewport.scrollTop();
|
2168
|
+
newScrollPos = originalScrollPos;
|
2169
|
+
offsetShift = void 0;
|
2170
|
+
obstruction = void 0;
|
2171
|
+
if (viewportIsBody) {
|
2172
|
+
obstruction = measureObstruction();
|
2173
|
+
offsetShift = 0;
|
2174
|
+
} else {
|
2175
|
+
obstruction = {
|
2176
|
+
top: 0,
|
2177
|
+
bottom: 0
|
2178
|
+
};
|
2179
|
+
offsetShift = originalScrollPos;
|
1695
2180
|
}
|
1696
|
-
|
1697
|
-
|
1698
|
-
parseResponse = function(html) {
|
1699
|
-
var htmlElement;
|
1700
|
-
htmlElement = u.createElementFromHtml(html);
|
1701
|
-
return {
|
1702
|
-
title: function() {
|
1703
|
-
var ref;
|
1704
|
-
return (ref = htmlElement.querySelector("title")) != null ? ref.textContent : void 0;
|
1705
|
-
},
|
1706
|
-
find: function(selector) {
|
1707
|
-
var child;
|
1708
|
-
if (child = htmlElement.querySelector(selector)) {
|
1709
|
-
return $(child);
|
1710
|
-
} else {
|
1711
|
-
return u.error("Could not find selector %o in response %o", selector, html);
|
1712
|
-
}
|
1713
|
-
}
|
2181
|
+
predictFirstVisibleRow = function() {
|
2182
|
+
return newScrollPos + obstruction.top;
|
1714
2183
|
};
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
2184
|
+
predictLastVisibleRow = function() {
|
2185
|
+
return newScrollPos + viewportHeight - obstruction.bottom - 1;
|
2186
|
+
};
|
2187
|
+
elementDims = u.measure($element, {
|
2188
|
+
relative: true
|
2189
|
+
});
|
2190
|
+
firstElementRow = elementDims.top + offsetShift;
|
2191
|
+
lastElementRow = firstElementRow + elementDims.height - 1;
|
2192
|
+
if (lastElementRow > predictLastVisibleRow()) {
|
2193
|
+
newScrollPos += lastElementRow - predictLastVisibleRow();
|
1725
2194
|
}
|
1726
|
-
|
1727
|
-
|
1728
|
-
if (typeof options.insert === "function") {
|
1729
|
-
options.insert($new);
|
2195
|
+
if (firstElementRow < predictFirstVisibleRow()) {
|
2196
|
+
newScrollPos = firstElementRow - obstruction.top;
|
1730
2197
|
}
|
1731
|
-
if (
|
1732
|
-
|
1733
|
-
document.title = options.title;
|
1734
|
-
}
|
1735
|
-
up.history[options.historyMethod](options.history);
|
2198
|
+
if (newScrollPos < snap) {
|
2199
|
+
newScrollPos = 0;
|
1736
2200
|
}
|
1737
|
-
|
1738
|
-
|
1739
|
-
return up.ready($new);
|
1740
|
-
};
|
1741
|
-
swapElements = function($old, $new, pseudoClass, transition, options) {
|
1742
|
-
var $wrapper, insertionMethod;
|
1743
|
-
transition || (transition = 'none');
|
1744
|
-
up.motion.finish($old);
|
1745
|
-
if (pseudoClass) {
|
1746
|
-
insertionMethod = pseudoClass === 'before' ? 'prepend' : 'append';
|
1747
|
-
$wrapper = $new.contents().wrap('<span class="up-insertion"></span>').parent();
|
1748
|
-
$old[insertionMethod]($wrapper);
|
1749
|
-
u.copyAttributes($new, $old);
|
1750
|
-
elementsInserted($wrapper.children(), options);
|
1751
|
-
return reveal($wrapper, options).then(function() {
|
1752
|
-
return up.animate($wrapper, transition, options);
|
1753
|
-
}).then(function() {
|
1754
|
-
u.unwrapElement($wrapper);
|
1755
|
-
});
|
2201
|
+
if (newScrollPos !== originalScrollPos) {
|
2202
|
+
return scroll($viewport, newScrollPos, options);
|
1756
2203
|
} else {
|
1757
|
-
return
|
1758
|
-
return destroy($old, {
|
1759
|
-
animation: function() {
|
1760
|
-
$new.insertBefore($old);
|
1761
|
-
elementsInserted($new, options);
|
1762
|
-
if ($old.is('body') && transition !== 'none') {
|
1763
|
-
u.error('Cannot apply transitions to body-elements (%o)', transition);
|
1764
|
-
}
|
1765
|
-
return up.morph($old, $new, transition, options);
|
1766
|
-
}
|
1767
|
-
});
|
1768
|
-
});
|
1769
|
-
}
|
1770
|
-
};
|
1771
|
-
parseImplantSteps = function(selector, options) {
|
1772
|
-
var comma, disjunction, i, j, len, results, selectorAtom, selectorParts, transition, transitionString, transitions;
|
1773
|
-
transitionString = options.transition || options.animation || 'none';
|
1774
|
-
comma = /\ *,\ */;
|
1775
|
-
disjunction = selector.split(comma);
|
1776
|
-
if (u.isPresent(transitionString)) {
|
1777
|
-
transitions = transitionString.split(comma);
|
1778
|
-
}
|
1779
|
-
results = [];
|
1780
|
-
for (i = j = 0, len = disjunction.length; j < len; i = ++j) {
|
1781
|
-
selectorAtom = disjunction[i];
|
1782
|
-
selectorParts = selectorAtom.match(/^(.+?)(?:\:(before|after))?$/);
|
1783
|
-
transition = transitions[i] || u.last(transitions);
|
1784
|
-
results.push({
|
1785
|
-
selector: selectorParts[1],
|
1786
|
-
pseudoClass: selectorParts[2],
|
1787
|
-
transition: transition
|
1788
|
-
});
|
1789
|
-
}
|
1790
|
-
return results;
|
1791
|
-
};
|
1792
|
-
autofocus = function($element) {
|
1793
|
-
var $control, selector;
|
1794
|
-
selector = '[autofocus]:last';
|
1795
|
-
$control = u.findWithSelf($element, selector);
|
1796
|
-
if ($control.length && $control.get(0) !== document.activeElement) {
|
1797
|
-
return $control.focus();
|
2204
|
+
return u.resolvedDeferred();
|
1798
2205
|
}
|
1799
2206
|
};
|
1800
|
-
|
1801
|
-
|
1802
|
-
unreal = '.up-ghost, .up-destroying';
|
1803
|
-
return $element.closest(unreal).length === 0;
|
2207
|
+
viewportSelector = function() {
|
2208
|
+
return config.viewports.join(', ');
|
1804
2209
|
};
|
1805
2210
|
|
1806
2211
|
/**
|
1807
|
-
Returns the
|
1808
|
-
Excludes elements that also match `.up-ghost` or `.up-destroying`
|
1809
|
-
or that are children of elements with these selectors.
|
2212
|
+
Returns the viewport for the given element.
|
1810
2213
|
|
1811
|
-
|
2214
|
+
Throws an error if no viewport could be found.
|
1812
2215
|
|
1813
2216
|
@protected
|
1814
|
-
@method up.
|
1815
|
-
@param {String}
|
2217
|
+
@method up.layout.viewportOf
|
2218
|
+
@param {String|Element|jQuery} selectorOrElement
|
1816
2219
|
*/
|
1817
|
-
|
1818
|
-
var $element, $
|
1819
|
-
|
1820
|
-
$
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
break;
|
1827
|
-
}
|
2220
|
+
viewportOf = function(selectorOrElement, viewportSelectorOrElement) {
|
2221
|
+
var $element, $viewport, vieportSelector;
|
2222
|
+
$element = $(selectorOrElement);
|
2223
|
+
$viewport = void 0;
|
2224
|
+
if (u.isJQuery(viewportSelectorOrElement)) {
|
2225
|
+
$viewport = viewportSelectorOrElement;
|
2226
|
+
} else {
|
2227
|
+
vieportSelector = u.presence(viewportSelectorOrElement) || viewportSelector();
|
2228
|
+
$viewport = $element.closest(vieportSelector);
|
1828
2229
|
}
|
1829
|
-
|
2230
|
+
$viewport.length || u.error("Could not find viewport for %o", $element);
|
2231
|
+
return $viewport;
|
1830
2232
|
};
|
1831
2233
|
|
1832
2234
|
/**
|
1833
|
-
|
1834
|
-
|
1835
|
-
The element is removed from the DOM.
|
2235
|
+
Returns a jQuery collection of all the viewports contained within the
|
2236
|
+
given selector or element.
|
1836
2237
|
|
1837
|
-
@
|
1838
|
-
@
|
1839
|
-
@param {String}
|
1840
|
-
@
|
1841
|
-
@param {String} [options.animation='none']
|
1842
|
-
The animation to use before the element is removed from the DOM.
|
1843
|
-
@param {Number} [options.duration]
|
1844
|
-
The duration of the animation. See [`up.animate`](/up.motion#up.animate).
|
1845
|
-
@param {Number} [options.delay]
|
1846
|
-
The delay before the animation starts. See [`up.animate`](/up.motion#up.animate).
|
1847
|
-
@param {String} [options.easing]
|
1848
|
-
The timing function that controls the animation's acceleration. [`up.animate`](/up.motion#up.animate).
|
2238
|
+
@protected
|
2239
|
+
@method up.layout.viewportsIn
|
2240
|
+
@param {String|Element|jQuery} selectorOrElement
|
2241
|
+
@return jQuery
|
1849
2242
|
*/
|
1850
|
-
|
1851
|
-
var $element
|
2243
|
+
viewportsIn = function(selectorOrElement) {
|
2244
|
+
var $element;
|
1852
2245
|
$element = $(selectorOrElement);
|
1853
|
-
|
1854
|
-
animation: 'none'
|
1855
|
-
});
|
1856
|
-
animateOptions = up.motion.animateOptions(options);
|
1857
|
-
$element.addClass('up-destroying');
|
1858
|
-
if (u.isPresent(options.url)) {
|
1859
|
-
up.history.push(options.url);
|
1860
|
-
}
|
1861
|
-
if (u.isPresent(options.title)) {
|
1862
|
-
document.title = options.title;
|
1863
|
-
}
|
1864
|
-
up.bus.emit('fragment:destroy', $element);
|
1865
|
-
animationPromise = u.presence(options.animation, u.isPromise) || up.motion.animate($element, options.animation, animateOptions);
|
1866
|
-
animationPromise.then(function() {
|
1867
|
-
return $element.remove();
|
1868
|
-
});
|
1869
|
-
return animationPromise;
|
2246
|
+
return u.findWithSelf($element, viewportSelector());
|
1870
2247
|
};
|
1871
2248
|
|
1872
2249
|
/**
|
1873
|
-
|
1874
|
-
fetched from the server.
|
1875
|
-
|
1876
|
-
Up.js remembers the URL from which a fragment was loaded, so you
|
1877
|
-
don't usually need to give an URL when reloading.
|
2250
|
+
Returns a jQuery collection of all the viewports on the screen.
|
1878
2251
|
|
1879
|
-
@
|
1880
|
-
@
|
1881
|
-
@param {Object} [options]
|
1882
|
-
See options for [`up.replace`](#up.replace)
|
2252
|
+
@protected
|
2253
|
+
@method up.layout.viewports
|
1883
2254
|
*/
|
1884
|
-
|
1885
|
-
|
1886
|
-
options = u.options(options, {
|
1887
|
-
cache: false
|
1888
|
-
});
|
1889
|
-
sourceUrl = options.url || source(selectorOrElement);
|
1890
|
-
return replace(selectorOrElement, sourceUrl, options);
|
2255
|
+
viewports = function() {
|
2256
|
+
return $(viewportSelector());
|
1891
2257
|
};
|
1892
2258
|
|
1893
2259
|
/**
|
1894
|
-
|
1895
|
-
All custom event handlers, animations, etc. that have been registered
|
1896
|
-
will be discarded.
|
2260
|
+
Returns a hash with scroll positions.
|
1897
2261
|
|
1898
|
-
|
1899
|
-
|
2262
|
+
Each key in the hash is a viewport selector. The corresponding
|
2263
|
+
value is the viewport's top scroll position:
|
2264
|
+
|
2265
|
+
up.layout.scrollTops()
|
2266
|
+
=> { '.main': 0, '.sidebar': 73 }
|
1900
2267
|
|
1901
2268
|
@protected
|
1902
|
-
@method up.
|
2269
|
+
@method up.layout.scrollTops
|
2270
|
+
@return Object<String, Number>
|
1903
2271
|
*/
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
first: first
|
2272
|
+
scrollTops = function() {
|
2273
|
+
var $viewport, i, len, ref, topsBySelector, viewport;
|
2274
|
+
topsBySelector = {};
|
2275
|
+
ref = config.viewports;
|
2276
|
+
for (i = 0, len = ref.length; i < len; i++) {
|
2277
|
+
viewport = ref[i];
|
2278
|
+
$viewport = $(viewport);
|
2279
|
+
if ($viewport.length) {
|
2280
|
+
topsBySelector[viewport] = $viewport.scrollTop();
|
2281
|
+
}
|
2282
|
+
}
|
2283
|
+
return topsBySelector;
|
1917
2284
|
};
|
1918
|
-
})();
|
1919
|
-
|
1920
|
-
up.replace = up.flow.replace;
|
1921
|
-
|
1922
|
-
up.reload = up.flow.reload;
|
1923
|
-
|
1924
|
-
up.destroy = up.flow.destroy;
|
1925
|
-
|
1926
|
-
up.reset = up.flow.reset;
|
1927
|
-
|
1928
|
-
up.first = up.flow.first;
|
1929
|
-
|
1930
|
-
}).call(this);
|
1931
|
-
|
1932
|
-
/**
|
1933
|
-
Registering behavior and custom elements
|
1934
|
-
========================================
|
1935
|
-
|
1936
|
-
Up.js keeps a persistent Javascript environment during page transitions.
|
1937
|
-
To prevent memory leaks it is important to cleanly set up and tear down
|
1938
|
-
event handlers and custom elements.
|
1939
|
-
|
1940
|
-
\#\#\# Incomplete documentation!
|
1941
|
-
|
1942
|
-
We need to work on this page:
|
1943
|
-
|
1944
|
-
- Better class-level introduction for this module
|
1945
|
-
|
1946
|
-
@class up.magic
|
1947
|
-
*/
|
1948
|
-
|
1949
|
-
(function() {
|
1950
|
-
var slice = [].slice;
|
1951
|
-
|
1952
|
-
up.magic = (function() {
|
1953
|
-
var DESTROYABLE_CLASS, DESTROYER_KEY, applyCompiler, compile, compiler, compilers, data, defaultCompilers, defaultLiveDescriptions, destroy, live, liveDescriptions, onEscape, ready, reset, snapshot, u;
|
1954
|
-
u = up.util;
|
1955
|
-
DESTROYABLE_CLASS = 'up-destroyable';
|
1956
|
-
DESTROYER_KEY = 'up-destroyer';
|
1957
2285
|
|
1958
2286
|
/**
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
console.log("Someone clicked the button %o", $element);
|
1964
|
-
});
|
1965
|
-
|
1966
|
-
This is roughly equivalent to binding a jQuery element to `document`.
|
1967
|
-
|
1968
|
-
|
1969
|
-
\#\#\#\# Attaching structured data
|
1970
|
-
|
1971
|
-
In case you want to attach structured data to the event you're observing,
|
1972
|
-
you can serialize the data to JSON and put it into an `[up-data]` attribute:
|
1973
|
-
|
1974
|
-
<span class="person" up-data="{ age: 18, name: 'Bob' }">Bob</span>
|
1975
|
-
<span class="person" up-data="{ age: 22, name: 'Jim' }">Jim</span>
|
2287
|
+
Saves the top scroll positions of all the
|
2288
|
+
viewports configured in `up.layout.defaults('viewports').
|
2289
|
+
The saved scroll positions can be restored by calling
|
2290
|
+
[`up.layout.restoreScroll()`](#up.layout.restoreScroll).
|
1976
2291
|
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
|
1981
|
-
});
|
1982
|
-
|
1983
|
-
|
1984
|
-
\#\#\#\# Migrating jQuery event handlers to `up.on`
|
1985
|
-
|
1986
|
-
Within the event handler, Up.js will bind `this` to the
|
1987
|
-
native DOM element to help you migrate your existing jQuery code to
|
1988
|
-
this new syntax.
|
1989
|
-
|
1990
|
-
So if you had this before:
|
1991
|
-
|
1992
|
-
$(document).on('click', '.button', function() {
|
1993
|
-
$(this).something();
|
1994
|
-
});
|
1995
|
-
|
1996
|
-
... you can simply copy the event handler to `up.on`:
|
1997
|
-
|
1998
|
-
up.on('click', '.button', function() {
|
1999
|
-
$(this).something();
|
2000
|
-
});
|
2001
|
-
|
2002
|
-
|
2003
|
-
@method up.on
|
2004
|
-
@param {String} events
|
2005
|
-
A space-separated list of event names to bind.
|
2006
|
-
@param {String} selector
|
2007
|
-
The selector an on which the event must be triggered.
|
2008
|
-
@param {Function(event, $element, data)} behavior
|
2009
|
-
The handler that should be called.
|
2010
|
-
The function takes the affected element as the first argument (as a jQuery object).
|
2011
|
-
If the element has an `up-data` attribute, its value is parsed as JSON
|
2012
|
-
and passed as a second argument.
|
2292
|
+
@method up.layout.saveScroll
|
2293
|
+
@param {String} [options.url]
|
2294
|
+
@param {Object<String, Number>} [options.tops]
|
2295
|
+
@protected
|
2013
2296
|
*/
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
if (!up.browser.isSupported()) {
|
2019
|
-
return;
|
2297
|
+
saveScroll = function(options) {
|
2298
|
+
var tops, url;
|
2299
|
+
if (options == null) {
|
2300
|
+
options = {};
|
2020
2301
|
}
|
2021
|
-
|
2022
|
-
|
2023
|
-
|
2024
|
-
}
|
2025
|
-
];
|
2026
|
-
liveDescriptions.push(description);
|
2027
|
-
return (ref = $(document)).on.apply(ref, description);
|
2302
|
+
url = u.option(options.url, up.history.url());
|
2303
|
+
tops = u.option(options.tops, scrollTops());
|
2304
|
+
return lastScrollTops.set(url, tops);
|
2028
2305
|
};
|
2029
2306
|
|
2030
2307
|
/**
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
This is a great way to integrate jQuery plugins.
|
2035
|
-
Let's say your Javascript plugin wants you to call `lightboxify()`
|
2036
|
-
on links that should open a lightbox. You decide to
|
2037
|
-
do this for all links with an `[rel=lightbox]` attribute:
|
2038
|
-
|
2039
|
-
<a href="river.png" rel="lightbox">River</a>
|
2040
|
-
<a href="ocean.png" rel="lightbox">Ocean</a>
|
2041
|
-
|
2042
|
-
This Javascript will do exactly that:
|
2043
|
-
|
2044
|
-
up.compiler('a[rel=lightbox]', function($element) {
|
2045
|
-
$element.lightboxify();
|
2046
|
-
});
|
2047
|
-
|
2048
|
-
Note that within the compiler, Up.js will bind `this` to the
|
2049
|
-
native DOM element to help you migrate your existing jQuery code to
|
2050
|
-
this new syntax.
|
2051
|
-
|
2052
|
-
|
2053
|
-
\#\#\#\# Custom elements
|
2054
|
-
|
2055
|
-
You can also use `up.compiler` to implement custom elements like this:
|
2056
|
-
|
2057
|
-
<clock></clock>
|
2308
|
+
Restores the top scroll positions of all the
|
2309
|
+
viewports configured in `up.layout.defaults('viewports')`.
|
2058
2310
|
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2311
|
+
@method up.layout.restoreScroll
|
2312
|
+
@param {String} [options.within]
|
2313
|
+
@protected
|
2314
|
+
*/
|
2315
|
+
restoreScroll = function(options) {
|
2316
|
+
var $matchingViewport, $viewports, results, scrollTop, selector, tops;
|
2317
|
+
if (options == null) {
|
2318
|
+
options = {};
|
2319
|
+
}
|
2320
|
+
$viewports = options.within ? viewportsIn(options.within) : viewports();
|
2321
|
+
tops = lastScrollTops.get(up.history.url());
|
2322
|
+
results = [];
|
2323
|
+
for (selector in tops) {
|
2324
|
+
scrollTop = tops[selector];
|
2325
|
+
$matchingViewport = $viewports.filter(selector);
|
2326
|
+
results.push(up.scroll($matchingViewport, scrollTop, {
|
2327
|
+
duration: 0
|
2328
|
+
}));
|
2329
|
+
}
|
2330
|
+
return results;
|
2331
|
+
};
|
2332
|
+
|
2333
|
+
/**
|
2334
|
+
Marks this element as a scrolling container. Apply this ttribute if your app uses
|
2335
|
+
a custom panel layout with fixed positioning instead of scrolling `<body>`.
|
2073
2336
|
|
2074
|
-
|
2075
|
-
|
2076
|
-
or event handlers bound to the document root.
|
2337
|
+
[`up.reveal`](/up.reveal) will always try to scroll the viewport closest
|
2338
|
+
to the element that is being revealed. By default this is the `<body>` element.
|
2077
2339
|
|
2078
|
-
|
2079
|
-
the time every second, and cleans up once it's done:
|
2340
|
+
\#\#\#\# Example
|
2080
2341
|
|
2081
|
-
|
2342
|
+
Here is an example for a layout for an e-mail client, showing a list of e-mails
|
2343
|
+
on the left side and the e-mail text on the right side:
|
2082
2344
|
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2345
|
+
.side {
|
2346
|
+
position: fixed;
|
2347
|
+
top: 0;
|
2348
|
+
bottom: 0;
|
2349
|
+
left: 0;
|
2350
|
+
width: 100px;
|
2351
|
+
overflow-y: scroll;
|
2352
|
+
}
|
2087
2353
|
|
2088
|
-
|
2354
|
+
.main {
|
2355
|
+
position: fixed;
|
2356
|
+
top: 0;
|
2357
|
+
bottom: 0;
|
2358
|
+
left: 100px;
|
2359
|
+
right: 0;
|
2360
|
+
overflow-y: scroll;
|
2361
|
+
}
|
2089
2362
|
|
2090
|
-
|
2091
|
-
clearInterval(update);
|
2092
|
-
};
|
2363
|
+
This would be the HTML (notice the `up-viewport` attribute):
|
2093
2364
|
|
2094
|
-
|
2365
|
+
<div class=".side" up-viewport>
|
2366
|
+
<a href="/emails/5001" up-target=".main">Re: Your invoice</a>
|
2367
|
+
<a href="/emails/2023" up-target=".main">Quote for services</a>
|
2368
|
+
<a href="/emails/9002" up-target=".main">Fwd: Room reservation</a>
|
2369
|
+
</div>
|
2095
2370
|
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2371
|
+
<div class="main" up-viewport>
|
2372
|
+
<h1>Re: Your Invoice</h1>
|
2373
|
+
<p>
|
2374
|
+
Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
|
2375
|
+
Stet clita kasd gubergren, no sea takimata sanctus est.
|
2376
|
+
</p>
|
2377
|
+
</div>
|
2099
2378
|
|
2379
|
+
@method [up-viewport]
|
2380
|
+
@ujs
|
2381
|
+
*/
|
2382
|
+
|
2383
|
+
/**
|
2384
|
+
Marks this element as a navigation fixed to the top edge of the screen
|
2385
|
+
using `position: fixed`.
|
2100
2386
|
|
2101
|
-
|
2387
|
+
[`up.reveal`](/up.reveal) is aware of fixed elements and will scroll
|
2388
|
+
the viewport far enough so the revealed element is fully visible.
|
2102
2389
|
|
2103
|
-
|
2104
|
-
you can serialize the data to JSON and put it into an `[up-data]` attribute.
|
2105
|
-
For instance, a container for a [Google Map](https://developers.google.com/maps/documentation/javascript/tutorial)
|
2106
|
-
might attach the location and names of its marker pins:
|
2390
|
+
Example:
|
2107
2391
|
|
2108
|
-
<div class="
|
2109
|
-
{ lat: 48.36, lng: 10.99, title: 'Friedberg' },
|
2110
|
-
{ lat: 48.75, lng: 11.45, title: 'Ingolstadt' }
|
2111
|
-
]"></div>
|
2392
|
+
<div class="top-nav" up-fixed="top">...</div>
|
2112
2393
|
|
2113
|
-
|
2394
|
+
@method [up-fixed=top]
|
2395
|
+
@ujs
|
2396
|
+
*/
|
2397
|
+
|
2398
|
+
/**
|
2399
|
+
Marks this element as a navigation fixed to the bottom edge of the screen
|
2400
|
+
using `position: fixed`.
|
2114
2401
|
|
2115
|
-
|
2402
|
+
[`up.reveal`](/up.reveal) is aware of fixed elements and will scroll
|
2403
|
+
the viewport far enough so the revealed element is fully visible.
|
2116
2404
|
|
2117
|
-
|
2405
|
+
Example:
|
2118
2406
|
|
2119
|
-
|
2120
|
-
var position = new google.maps.LatLng(pin.lat, pin.lng);
|
2121
|
-
new google.maps.Marker({
|
2122
|
-
position: position,
|
2123
|
-
map: map,
|
2124
|
-
title: pin.title
|
2125
|
-
});
|
2126
|
-
});
|
2407
|
+
<div class="bottom-nav" up-fixed="bottom">...</div>
|
2127
2408
|
|
2128
|
-
|
2409
|
+
@method [up-fixed=bottom]
|
2410
|
+
@ujs
|
2411
|
+
*/
|
2412
|
+
up.bus.on('framework:reset', reset);
|
2413
|
+
return {
|
2414
|
+
reveal: reveal,
|
2415
|
+
scroll: scroll,
|
2416
|
+
finishScrolling: finishScrolling,
|
2417
|
+
defaults: config.update,
|
2418
|
+
viewportOf: viewportOf,
|
2419
|
+
viewportsIn: viewportsIn,
|
2420
|
+
viewports: viewports,
|
2421
|
+
scrollTops: scrollTops,
|
2422
|
+
saveScroll: saveScroll,
|
2423
|
+
restoreScroll: restoreScroll
|
2424
|
+
};
|
2425
|
+
})();
|
2426
|
+
|
2427
|
+
up.scroll = up.layout.scroll;
|
2428
|
+
|
2429
|
+
up.reveal = up.layout.reveal;
|
2430
|
+
|
2431
|
+
}).call(this);
|
2432
|
+
|
2433
|
+
/**
|
2434
|
+
Changing page fragments programmatically
|
2435
|
+
========================================
|
2436
|
+
|
2437
|
+
This module contains Up's core functions to insert, change
|
2438
|
+
or destroy page fragments.
|
2439
|
+
|
2440
|
+
\#\#\# Incomplete documentation!
|
2441
|
+
|
2442
|
+
We need to work on this page:
|
2443
|
+
|
2444
|
+
- Explain the UJS approach vs. pragmatic approach
|
2445
|
+
- Examples
|
2446
|
+
|
2447
|
+
|
2448
|
+
@class up.flow
|
2449
|
+
*/
|
2450
|
+
|
2451
|
+
(function() {
|
2452
|
+
up.flow = (function() {
|
2453
|
+
var autofocus, destroy, elementsInserted, findOldFragment, first, fragmentNotFound, implant, isRealElement, parseImplantSteps, parseResponse, reload, replace, reset, reveal, setSource, source, swapElements, u;
|
2454
|
+
u = up.util;
|
2455
|
+
setSource = function(element, sourceUrl) {
|
2456
|
+
var $element;
|
2457
|
+
$element = $(element);
|
2458
|
+
if (u.isPresent(sourceUrl)) {
|
2459
|
+
sourceUrl = u.normalizeUrl(sourceUrl);
|
2460
|
+
}
|
2461
|
+
return $element.attr("up-source", sourceUrl);
|
2462
|
+
};
|
2463
|
+
source = function(element) {
|
2464
|
+
var $element;
|
2465
|
+
$element = $(element).closest("[up-source]");
|
2466
|
+
return u.presence($element.attr("up-source")) || up.browser.url();
|
2467
|
+
};
|
2468
|
+
|
2469
|
+
/**
|
2470
|
+
Replaces elements on the current page with corresponding elements
|
2471
|
+
from a new page fetched from the server.
|
2129
2472
|
|
2473
|
+
The current and new elements must have the same CSS selector.
|
2130
2474
|
|
2131
|
-
|
2475
|
+
@method up.replace
|
2476
|
+
@param {String|Element|jQuery} selectorOrElement
|
2477
|
+
The CSS selector to update. You can also pass a DOM element or jQuery element
|
2478
|
+
here, in which case a selector will be inferred from the element's class and ID.
|
2479
|
+
@param {String} url
|
2480
|
+
The URL to fetch from the server.
|
2481
|
+
@param {String} [options.method='get']
|
2482
|
+
@param {String} [options.title]
|
2483
|
+
@param {String} [options.transition='none']
|
2484
|
+
@param {String|Boolean} [options.history=true]
|
2485
|
+
If a `String` is given, it is used as the URL the browser's location bar and history.
|
2486
|
+
If omitted or true, the `url` argument will be used.
|
2487
|
+
If set to `false`, the history will remain unchanged.
|
2488
|
+
@param {String|Boolean} [options.source=true]
|
2489
|
+
@param {String} [options.reveal]
|
2490
|
+
Up.js will try to [reveal](/up.layout#up.reveal) the element being updated, by
|
2491
|
+
scrolling its containing viewport. Set this option to `false` to prevent any scrolling.
|
2132
2492
|
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2493
|
+
If omitted, this will use the [default from `up.layout`](/up.layout#up.layout.defaults).
|
2494
|
+
@param {Boolean} [options.restoreScroll=`false`]
|
2495
|
+
If set to true, Up.js will try to restore the scroll position
|
2496
|
+
of all the viewports within the updated element. The position
|
2497
|
+
will be reset to the last known top position before a previous
|
2498
|
+
history change for the current URL.
|
2499
|
+
@param {Boolean} [options.cache]
|
2500
|
+
Whether to use a [cached response](/up.proxy) if available.
|
2501
|
+
@param {String} [options.historyMethod='push']
|
2502
|
+
@return {Promise}
|
2503
|
+
A promise that will be resolved when the page has been updated.
|
2504
|
+
*/
|
2505
|
+
replace = function(selectorOrElement, url, options) {
|
2506
|
+
var promise, request, selector;
|
2507
|
+
u.debug("Replace %o with %o", selectorOrElement, url);
|
2508
|
+
options = u.options(options);
|
2509
|
+
selector = u.presence(selectorOrElement) ? selectorOrElement : u.createSelectorFromElement($(selectorOrElement));
|
2510
|
+
if (!up.browser.canPushState() && options.history !== false) {
|
2511
|
+
if (!options.preload) {
|
2512
|
+
up.browser.loadPage(url, u.only(options, 'method'));
|
2513
|
+
}
|
2514
|
+
return u.resolvedPromise();
|
2515
|
+
}
|
2516
|
+
request = {
|
2517
|
+
url: url,
|
2518
|
+
method: options.method,
|
2519
|
+
selector: selector,
|
2520
|
+
cache: options.cache,
|
2521
|
+
preload: options.preload
|
2522
|
+
};
|
2523
|
+
promise = up.proxy.ajax(request);
|
2524
|
+
promise.done(function(html, textStatus, xhr) {
|
2525
|
+
var currentLocation, newRequest;
|
2526
|
+
if (currentLocation = u.locationFromXhr(xhr)) {
|
2527
|
+
u.debug('Location from server: %o', currentLocation);
|
2528
|
+
newRequest = {
|
2529
|
+
url: currentLocation,
|
2530
|
+
method: u.methodFromXhr(xhr),
|
2531
|
+
selector: selector
|
2532
|
+
};
|
2533
|
+
up.proxy.alias(request, newRequest);
|
2534
|
+
url = currentLocation;
|
2535
|
+
}
|
2536
|
+
if (options.history !== false) {
|
2537
|
+
options.history = url;
|
2538
|
+
}
|
2539
|
+
if (options.source !== false) {
|
2540
|
+
options.source = url;
|
2541
|
+
}
|
2542
|
+
if (!options.preload) {
|
2543
|
+
return implant(selector, html, options);
|
2544
|
+
}
|
2545
|
+
});
|
2546
|
+
promise.fail(u.error);
|
2547
|
+
return promise;
|
2548
|
+
};
|
2549
|
+
|
2550
|
+
/**
|
2551
|
+
Updates a selector on the current page with the
|
2552
|
+
same selector from the given HTML string.
|
2136
2553
|
|
2554
|
+
Example:
|
2137
2555
|
|
2138
|
-
|
2139
|
-
|
2140
|
-
|
2141
|
-
@param {Boolean} [options.batch=false]
|
2142
|
-
If set to `true` and a fragment insertion contains multiple
|
2143
|
-
elements matching the selector, `compiler` is only called once
|
2144
|
-
with a jQuery collection containing all matching elements.
|
2145
|
-
@param {Function($element, data)} compiler
|
2146
|
-
The function to call when a matching element is inserted.
|
2147
|
-
The function takes the new element as the first argument (as a jQuery object).
|
2148
|
-
If the element has an `up-data` attribute, its value is parsed as JSON
|
2149
|
-
and passed as a second argument.
|
2556
|
+
html = '<div class="before">new-before</div>' +
|
2557
|
+
'<div class="middle">new-middle</div>' +
|
2558
|
+
'<div class="after">new-after</div>';
|
2150
2559
|
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2154
|
-
|
2155
|
-
|
2560
|
+
up.flow.implant('.middle', html):
|
2561
|
+
|
2562
|
+
@method up.flow.implant
|
2563
|
+
@protected
|
2564
|
+
@param {String} selector
|
2565
|
+
@param {String} html
|
2566
|
+
@param {Object} [options]
|
2567
|
+
See options for [`up.replace`](#up.replace).
|
2156
2568
|
*/
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2160
|
-
|
2161
|
-
selector = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
2162
|
-
if (!up.browser.isSupported()) {
|
2163
|
-
return;
|
2164
|
-
}
|
2165
|
-
compiler = args.pop();
|
2166
|
-
options = u.options(args[0], {
|
2167
|
-
batch: false
|
2168
|
-
});
|
2169
|
-
return compilers.push({
|
2170
|
-
selector: selector,
|
2171
|
-
callback: compiler,
|
2172
|
-
batch: options.batch
|
2569
|
+
implant = function(selector, html, options) {
|
2570
|
+
var $new, $old, j, len, ref, response, results, step;
|
2571
|
+
options = u.options(options, {
|
2572
|
+
historyMethod: 'push'
|
2173
2573
|
});
|
2574
|
+
options.source = u.option(options.source, options.history);
|
2575
|
+
response = parseResponse(html);
|
2576
|
+
options.title || (options.title = response.title());
|
2577
|
+
if (options.saveScroll !== false) {
|
2578
|
+
up.layout.saveScroll();
|
2579
|
+
}
|
2580
|
+
ref = parseImplantSteps(selector, options);
|
2581
|
+
results = [];
|
2582
|
+
for (j = 0, len = ref.length; j < len; j++) {
|
2583
|
+
step = ref[j];
|
2584
|
+
$old = findOldFragment(step.selector);
|
2585
|
+
$new = response.find(step.selector).first();
|
2586
|
+
results.push(swapElements($old, $new, step.pseudoClass, step.transition, options));
|
2587
|
+
}
|
2588
|
+
return results;
|
2174
2589
|
};
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2590
|
+
findOldFragment = function(selector) {
|
2591
|
+
return first(".up-popup " + selector) || first(".up-modal " + selector) || first(selector) || fragmentNotFound(selector);
|
2592
|
+
};
|
2593
|
+
fragmentNotFound = function(selector) {
|
2594
|
+
var message;
|
2595
|
+
message = 'Could not find selector %o in current body HTML';
|
2596
|
+
if (message[0] === '#') {
|
2597
|
+
message += ' (avoid using IDs)';
|
2182
2598
|
}
|
2599
|
+
return u.error(message, selector);
|
2183
2600
|
};
|
2184
|
-
|
2185
|
-
var
|
2186
|
-
u.
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2601
|
+
parseResponse = function(html) {
|
2602
|
+
var htmlElement;
|
2603
|
+
htmlElement = u.createElementFromHtml(html);
|
2604
|
+
return {
|
2605
|
+
title: function() {
|
2606
|
+
var ref;
|
2607
|
+
return (ref = htmlElement.querySelector("title")) != null ? ref.textContent : void 0;
|
2608
|
+
},
|
2609
|
+
find: function(selector) {
|
2610
|
+
var child;
|
2611
|
+
if (child = htmlElement.querySelector(selector)) {
|
2612
|
+
return $(child);
|
2194
2613
|
} else {
|
2195
|
-
|
2196
|
-
return applyCompiler(compiler, $(this), this);
|
2197
|
-
}));
|
2614
|
+
return u.error("Could not find selector %o in response %o", selector, html);
|
2198
2615
|
}
|
2199
|
-
} else {
|
2200
|
-
results.push(void 0);
|
2201
2616
|
}
|
2617
|
+
};
|
2618
|
+
};
|
2619
|
+
reveal = function($element, options) {
|
2620
|
+
if (options.reveal !== false) {
|
2621
|
+
return up.reveal($element);
|
2622
|
+
} else {
|
2623
|
+
return u.resolvedDeferred();
|
2624
|
+
}
|
2625
|
+
};
|
2626
|
+
elementsInserted = function($new, options) {
|
2627
|
+
if (typeof options.insert === "function") {
|
2628
|
+
options.insert($new);
|
2629
|
+
}
|
2630
|
+
if (options.history) {
|
2631
|
+
if (options.title) {
|
2632
|
+
document.title = options.title;
|
2633
|
+
}
|
2634
|
+
up.history[options.historyMethod](options.history);
|
2635
|
+
}
|
2636
|
+
if (options.source !== false) {
|
2637
|
+
setSource($new, options.source);
|
2638
|
+
}
|
2639
|
+
if (options.restoreScroll) {
|
2640
|
+
up.layout.restoreScroll({
|
2641
|
+
within: $new
|
2642
|
+
});
|
2643
|
+
}
|
2644
|
+
autofocus($new);
|
2645
|
+
return up.ready($new);
|
2646
|
+
};
|
2647
|
+
swapElements = function($old, $new, pseudoClass, transition, options) {
|
2648
|
+
var $wrapper, insertionMethod;
|
2649
|
+
transition || (transition = 'none');
|
2650
|
+
up.motion.finish($old);
|
2651
|
+
if (pseudoClass) {
|
2652
|
+
insertionMethod = pseudoClass === 'before' ? 'prepend' : 'append';
|
2653
|
+
$wrapper = $new.contents().wrap('<span class="up-insertion"></span>').parent();
|
2654
|
+
$old[insertionMethod]($wrapper);
|
2655
|
+
u.copyAttributes($new, $old);
|
2656
|
+
elementsInserted($wrapper.children(), options);
|
2657
|
+
return reveal($wrapper, options).then(function() {
|
2658
|
+
return up.animate($wrapper, transition, options);
|
2659
|
+
}).then(function() {
|
2660
|
+
u.unwrapElement($wrapper);
|
2661
|
+
});
|
2662
|
+
} else {
|
2663
|
+
return reveal($old, options).then(function() {
|
2664
|
+
return destroy($old, {
|
2665
|
+
animation: function() {
|
2666
|
+
$new.insertBefore($old);
|
2667
|
+
elementsInserted($new, options);
|
2668
|
+
if ($old.is('body') && transition !== 'none') {
|
2669
|
+
u.error('Cannot apply transitions to body-elements (%o)', transition);
|
2670
|
+
}
|
2671
|
+
return up.morph($old, $new, transition, options);
|
2672
|
+
}
|
2673
|
+
});
|
2674
|
+
});
|
2675
|
+
}
|
2676
|
+
};
|
2677
|
+
parseImplantSteps = function(selector, options) {
|
2678
|
+
var comma, disjunction, i, j, len, results, selectorAtom, selectorParts, transition, transitionString, transitions;
|
2679
|
+
transitionString = options.transition || options.animation || 'none';
|
2680
|
+
comma = /\ *,\ */;
|
2681
|
+
disjunction = selector.split(comma);
|
2682
|
+
if (u.isPresent(transitionString)) {
|
2683
|
+
transitions = transitionString.split(comma);
|
2684
|
+
}
|
2685
|
+
results = [];
|
2686
|
+
for (i = j = 0, len = disjunction.length; j < len; i = ++j) {
|
2687
|
+
selectorAtom = disjunction[i];
|
2688
|
+
selectorParts = selectorAtom.match(/^(.+?)(?:\:(before|after))?$/);
|
2689
|
+
transition = transitions[i] || u.last(transitions);
|
2690
|
+
results.push({
|
2691
|
+
selector: selectorParts[1],
|
2692
|
+
pseudoClass: selectorParts[2],
|
2693
|
+
transition: transition
|
2694
|
+
});
|
2202
2695
|
}
|
2203
2696
|
return results;
|
2204
2697
|
};
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
return
|
2211
|
-
}
|
2698
|
+
autofocus = function($element) {
|
2699
|
+
var $control, selector;
|
2700
|
+
selector = '[autofocus]:last';
|
2701
|
+
$control = u.findWithSelf($element, selector);
|
2702
|
+
if ($control.length && $control.get(0) !== document.activeElement) {
|
2703
|
+
return $control.focus();
|
2704
|
+
}
|
2705
|
+
};
|
2706
|
+
isRealElement = function($element) {
|
2707
|
+
var unreal;
|
2708
|
+
unreal = '.up-ghost, .up-destroying';
|
2709
|
+
return $element.closest(unreal).length === 0;
|
2212
2710
|
};
|
2213
2711
|
|
2214
2712
|
/**
|
2215
|
-
|
2216
|
-
|
2217
|
-
|
2218
|
-
Returns an empty object if the element has no `up-data` attribute.
|
2713
|
+
Returns the first element matching the given selector.
|
2714
|
+
Excludes elements that also match `.up-ghost` or `.up-destroying`
|
2715
|
+
or that are children of elements with these selectors.
|
2219
2716
|
|
2220
|
-
|
2221
|
-
we can support getting or setting individual keys.
|
2717
|
+
Returns `null` if no element matches these conditions.
|
2222
2718
|
|
2223
2719
|
@protected
|
2224
|
-
@method up.
|
2225
|
-
@param {String
|
2720
|
+
@method up.first
|
2721
|
+
@param {String} selector
|
2226
2722
|
*/
|
2723
|
+
first = function(selector) {
|
2724
|
+
var $element, $match, element, elements, j, len;
|
2725
|
+
elements = $(selector).get();
|
2726
|
+
$match = null;
|
2727
|
+
for (j = 0, len = elements.length; j < len; j++) {
|
2728
|
+
element = elements[j];
|
2729
|
+
$element = $(element);
|
2730
|
+
if (isRealElement($element)) {
|
2731
|
+
$match = $element;
|
2732
|
+
break;
|
2733
|
+
}
|
2734
|
+
}
|
2735
|
+
return $match;
|
2736
|
+
};
|
2227
2737
|
|
2228
|
-
|
2229
|
-
|
2230
|
-
|
2231
|
-
|
2232
|
-
Up will parse the JSON and pass the resulting object to any matching
|
2233
|
-
[`up.compiler`](/up.magic#up.magic.compiler) handlers.
|
2234
|
-
|
2235
|
-
Similarly, when an event is triggered on an element annotated with
|
2236
|
-
[`up-data`], the parsed object will be passed to any matching
|
2237
|
-
[`up.on`](/up.magic#up.on) handlers.
|
2738
|
+
/**
|
2739
|
+
Destroys the given element or selector.
|
2740
|
+
Takes care that all destructors, if any, are called.
|
2741
|
+
The element is removed from the DOM.
|
2238
2742
|
|
2239
|
-
@
|
2240
|
-
@
|
2241
|
-
@param {
|
2743
|
+
@method up.destroy
|
2744
|
+
@param {String|Element|jQuery} selectorOrElement
|
2745
|
+
@param {String} [options.url]
|
2746
|
+
@param {String} [options.title]
|
2747
|
+
@param {String} [options.animation='none']
|
2748
|
+
The animation to use before the element is removed from the DOM.
|
2749
|
+
@param {Number} [options.duration]
|
2750
|
+
The duration of the animation. See [`up.animate`](/up.motion#up.animate).
|
2751
|
+
@param {Number} [options.delay]
|
2752
|
+
The delay before the animation starts. See [`up.animate`](/up.motion#up.animate).
|
2753
|
+
@param {String} [options.easing]
|
2754
|
+
The timing function that controls the animation's acceleration. [`up.animate`](/up.motion#up.animate).
|
2242
2755
|
*/
|
2243
|
-
|
2244
|
-
var $element,
|
2245
|
-
$element = $(
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2756
|
+
destroy = function(selectorOrElement, options) {
|
2757
|
+
var $element, animateOptions, animationPromise;
|
2758
|
+
$element = $(selectorOrElement);
|
2759
|
+
options = u.options(options, {
|
2760
|
+
animation: 'none'
|
2761
|
+
});
|
2762
|
+
animateOptions = up.motion.animateOptions(options);
|
2763
|
+
$element.addClass('up-destroying');
|
2764
|
+
if (u.isPresent(options.url)) {
|
2765
|
+
up.history.push(options.url);
|
2766
|
+
}
|
2767
|
+
if (u.isPresent(options.title)) {
|
2768
|
+
document.title = options.title;
|
2251
2769
|
}
|
2770
|
+
up.bus.emit('fragment:destroy', $element);
|
2771
|
+
animationPromise = u.presence(options.animation, u.isPromise) || up.motion.animate($element, options.animation, animateOptions);
|
2772
|
+
animationPromise.then(function() {
|
2773
|
+
return $element.remove();
|
2774
|
+
});
|
2775
|
+
return animationPromise;
|
2252
2776
|
};
|
2253
2777
|
|
2254
2778
|
/**
|
2255
|
-
|
2256
|
-
|
2779
|
+
Replaces the given selector or element with a fresh copy
|
2780
|
+
fetched from the server.
|
2257
2781
|
|
2258
|
-
|
2259
|
-
|
2260
|
-
*/
|
2261
|
-
snapshot = function() {
|
2262
|
-
defaultLiveDescriptions = u.copy(liveDescriptions);
|
2263
|
-
return defaultCompilers = u.copy(compilers);
|
2264
|
-
};
|
2265
|
-
|
2266
|
-
/**
|
2267
|
-
Resets the list of registered event listeners to the
|
2268
|
-
moment when the framework was booted.
|
2782
|
+
Up.js remembers the URL from which a fragment was loaded, so you
|
2783
|
+
don't usually need to give an URL when reloading.
|
2269
2784
|
|
2270
|
-
@
|
2271
|
-
@
|
2785
|
+
@method up.reload
|
2786
|
+
@param {String|Element|jQuery} selectorOrElement
|
2787
|
+
@param {Object} [options]
|
2788
|
+
See options for [`up.replace`](#up.replace)
|
2272
2789
|
*/
|
2273
|
-
|
2274
|
-
var
|
2275
|
-
|
2276
|
-
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
}
|
2281
|
-
liveDescriptions = u.copy(defaultLiveDescriptions);
|
2282
|
-
return compilers = u.copy(defaultCompilers);
|
2790
|
+
reload = function(selectorOrElement, options) {
|
2791
|
+
var sourceUrl;
|
2792
|
+
options = u.options(options, {
|
2793
|
+
cache: false
|
2794
|
+
});
|
2795
|
+
sourceUrl = options.url || source(selectorOrElement);
|
2796
|
+
return replace(selectorOrElement, sourceUrl, options);
|
2283
2797
|
};
|
2284
2798
|
|
2285
2799
|
/**
|
2286
|
-
|
2287
|
-
|
2288
|
-
|
2800
|
+
Resets Up.js to the state when it was booted.
|
2801
|
+
All custom event handlers, animations, etc. that have been registered
|
2802
|
+
will be discarded.
|
2289
2803
|
|
2290
|
-
This
|
2291
|
-
|
2292
|
-
manipulate the DOM without going through Up.js.
|
2804
|
+
This is an internal method for to enable unit testing.
|
2805
|
+
Don't use this in production.
|
2293
2806
|
|
2294
|
-
@
|
2295
|
-
@
|
2807
|
+
@protected
|
2808
|
+
@method up.reset
|
2296
2809
|
*/
|
2297
|
-
|
2298
|
-
|
2299
|
-
$fragment = $(selectorOrFragment);
|
2300
|
-
up.bus.emit('fragment:ready', $fragment);
|
2301
|
-
return $fragment;
|
2302
|
-
};
|
2303
|
-
onEscape = function(handler) {
|
2304
|
-
return live('keydown', 'body', function(event) {
|
2305
|
-
if (u.escapePressed(event)) {
|
2306
|
-
return handler(event);
|
2307
|
-
}
|
2308
|
-
});
|
2810
|
+
reset = function() {
|
2811
|
+
return up.bus.emit('framework:reset');
|
2309
2812
|
};
|
2310
|
-
up.bus.on('app:ready',
|
2311
|
-
return
|
2312
|
-
})
|
2313
|
-
up.bus.on('fragment:ready', compile);
|
2314
|
-
up.bus.on('fragment:destroy', destroy);
|
2315
|
-
up.bus.on('framework:ready', snapshot);
|
2316
|
-
up.bus.on('framework:reset', reset);
|
2813
|
+
up.bus.on('app:ready', function() {
|
2814
|
+
return setSource(document.body, up.browser.url());
|
2815
|
+
});
|
2317
2816
|
return {
|
2318
|
-
|
2319
|
-
|
2320
|
-
|
2321
|
-
|
2322
|
-
|
2817
|
+
replace: replace,
|
2818
|
+
reload: reload,
|
2819
|
+
destroy: destroy,
|
2820
|
+
implant: implant,
|
2821
|
+
reset: reset,
|
2822
|
+
first: first
|
2323
2823
|
};
|
2324
2824
|
})();
|
2325
2825
|
|
2326
|
-
up.
|
2327
|
-
|
2328
|
-
up.on = up.magic.on;
|
2329
|
-
|
2330
|
-
up.ready = up.magic.ready;
|
2331
|
-
|
2332
|
-
up.awaken = function() {
|
2333
|
-
var args;
|
2334
|
-
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
2335
|
-
up.util.warn("up.awaken has been renamed to up.compiler and will be removed in a future version");
|
2336
|
-
return up.compiler.apply(up, args);
|
2337
|
-
};
|
2338
|
-
|
2339
|
-
}).call(this);
|
2340
|
-
|
2341
|
-
/**
|
2342
|
-
Manipulating the browser history
|
2343
|
-
=======
|
2344
|
-
|
2345
|
-
\#\#\# Incomplete documentation!
|
2346
|
-
|
2347
|
-
We need to work on this page:
|
2348
|
-
|
2349
|
-
- Explain how the other modules manipulate history
|
2350
|
-
- Decide whether we want to expose these methods as public API
|
2351
|
-
- Document methods and parameters
|
2826
|
+
up.replace = up.flow.replace;
|
2352
2827
|
|
2353
|
-
|
2354
|
-
*/
|
2828
|
+
up.reload = up.flow.reload;
|
2355
2829
|
|
2356
|
-
|
2357
|
-
up.history = (function() {
|
2358
|
-
var isCurrentUrl, manipulate, pop, push, replace, u;
|
2359
|
-
u = up.util;
|
2360
|
-
isCurrentUrl = function(url) {
|
2361
|
-
return u.normalizeUrl(url, {
|
2362
|
-
hash: true
|
2363
|
-
}) === u.normalizeUrl(up.browser.url(), {
|
2364
|
-
hash: true
|
2365
|
-
});
|
2366
|
-
};
|
2830
|
+
up.destroy = up.flow.destroy;
|
2367
2831
|
|
2368
|
-
|
2369
|
-
@method up.history.replace
|
2370
|
-
@param {String} url
|
2371
|
-
@protected
|
2372
|
-
*/
|
2373
|
-
replace = function(url, options) {
|
2374
|
-
options = u.options(options, {
|
2375
|
-
force: false
|
2376
|
-
});
|
2377
|
-
if (options.force || !isCurrentUrl(url)) {
|
2378
|
-
return manipulate("replace", url);
|
2379
|
-
}
|
2380
|
-
};
|
2832
|
+
up.reset = up.flow.reset;
|
2381
2833
|
|
2382
|
-
|
2383
|
-
@method up.history.push
|
2384
|
-
@param {String} url
|
2385
|
-
@protected
|
2386
|
-
*/
|
2387
|
-
push = function(url) {
|
2388
|
-
if (!isCurrentUrl(url)) {
|
2389
|
-
return manipulate("push", url);
|
2390
|
-
}
|
2391
|
-
};
|
2392
|
-
manipulate = function(method, url) {
|
2393
|
-
if (up.browser.canPushState()) {
|
2394
|
-
method += "State";
|
2395
|
-
return window.history[method]({
|
2396
|
-
fromUp: true
|
2397
|
-
}, '', url);
|
2398
|
-
} else {
|
2399
|
-
return u.error("This browser doesn't support history.pushState");
|
2400
|
-
}
|
2401
|
-
};
|
2402
|
-
pop = function(event) {
|
2403
|
-
var state;
|
2404
|
-
state = event.originalEvent.state;
|
2405
|
-
if (state != null ? state.fromUp : void 0) {
|
2406
|
-
u.debug("Restoring state %o (now on " + (up.browser.url()) + ")", state);
|
2407
|
-
return up.visit(up.browser.url(), {
|
2408
|
-
historyMethod: 'replace'
|
2409
|
-
});
|
2410
|
-
} else {
|
2411
|
-
return u.debug('Discarding unknown state %o', state);
|
2412
|
-
}
|
2413
|
-
};
|
2414
|
-
if (up.browser.canPushState()) {
|
2415
|
-
setTimeout((function() {
|
2416
|
-
$(window).on("popstate", pop);
|
2417
|
-
return replace(up.browser.url(), {
|
2418
|
-
force: true
|
2419
|
-
});
|
2420
|
-
}), 200);
|
2421
|
-
}
|
2422
|
-
return {
|
2423
|
-
push: push,
|
2424
|
-
replace: replace
|
2425
|
-
};
|
2426
|
-
})();
|
2834
|
+
up.first = up.flow.first;
|
2427
2835
|
|
2428
2836
|
}).call(this);
|
2429
2837
|
|
@@ -2555,6 +2963,9 @@ We need to work on this page:
|
|
2555
2963
|
$element = $(elementOrSelector);
|
2556
2964
|
finish($element);
|
2557
2965
|
options = animateOptions(options);
|
2966
|
+
if (animation === 'none' || animation === false) {
|
2967
|
+
none();
|
2968
|
+
}
|
2558
2969
|
if (u.isFunction(animation)) {
|
2559
2970
|
return assertIsDeferred(animation($element, options), animation);
|
2560
2971
|
} else if (u.isString(animation)) {
|
@@ -2711,7 +3122,7 @@ We need to work on this page:
|
|
2711
3122
|
$new = $(target);
|
2712
3123
|
finish($old);
|
2713
3124
|
finish($new);
|
2714
|
-
if (transitionOrName === 'none') {
|
3125
|
+
if (transitionOrName === 'none' || transitionOrName === false) {
|
2715
3126
|
return none();
|
2716
3127
|
} else if (transition = u.presence(transitionOrName, u.isFunction) || transitions[transitionOrName]) {
|
2717
3128
|
return withGhosts($old, $new, function($oldGhost, $newGhost) {
|
@@ -3080,9 +3491,8 @@ You can change (or remove) this delay like this:
|
|
3080
3491
|
|
3081
3492
|
(function() {
|
3082
3493
|
up.proxy = (function() {
|
3083
|
-
var $waitingLink, SAFE_HTTP_METHODS, ajax, alias, busy, busyDelayTimer, busyEventEmitted, cache, cacheKey, cancelBusyDelay, cancelPreloadDelay, checkPreload, clear, config, get, idle,
|
3494
|
+
var $waitingLink, SAFE_HTTP_METHODS, ajax, alias, busy, busyDelayTimer, busyEventEmitted, cache, cacheKey, cancelBusyDelay, cancelPreloadDelay, checkPreload, clear, config, get, idle, isIdempotent, load, loadEnded, loadStarted, normalizeRequest, pendingCount, preload, preloadDelayTimer, remove, reset, set, startPreloadDelay, u;
|
3084
3495
|
u = up.util;
|
3085
|
-
cache = void 0;
|
3086
3496
|
$waitingLink = void 0;
|
3087
3497
|
preloadDelayTimer = void 0;
|
3088
3498
|
busyDelayTimer = void 0;
|
@@ -3110,6 +3520,44 @@ You can change (or remove) this delay like this:
|
|
3110
3520
|
cacheSize: 70,
|
3111
3521
|
cacheExpiry: 1000 * 60 * 5
|
3112
3522
|
});
|
3523
|
+
cacheKey = function(request) {
|
3524
|
+
normalizeRequest(request);
|
3525
|
+
return [request.url, request.method, request.data, request.selector].join('|');
|
3526
|
+
};
|
3527
|
+
cache = u.cache({
|
3528
|
+
size: function() {
|
3529
|
+
return config.cacheSize;
|
3530
|
+
},
|
3531
|
+
expiry: function() {
|
3532
|
+
return config.cacheExpiry;
|
3533
|
+
},
|
3534
|
+
key: cacheKey,
|
3535
|
+
log: 'up.proxy'
|
3536
|
+
});
|
3537
|
+
|
3538
|
+
/**
|
3539
|
+
@protected
|
3540
|
+
@method up.proxy.get
|
3541
|
+
*/
|
3542
|
+
get = cache.get;
|
3543
|
+
|
3544
|
+
/**
|
3545
|
+
@protected
|
3546
|
+
@method up.proxy.set
|
3547
|
+
*/
|
3548
|
+
set = cache.set;
|
3549
|
+
|
3550
|
+
/**
|
3551
|
+
@protected
|
3552
|
+
@method up.proxy.remove
|
3553
|
+
*/
|
3554
|
+
remove = cache.remove;
|
3555
|
+
|
3556
|
+
/**
|
3557
|
+
@protected
|
3558
|
+
@method up.proxy.clear
|
3559
|
+
*/
|
3560
|
+
clear = cache.clear;
|
3113
3561
|
cancelPreloadDelay = function() {
|
3114
3562
|
clearTimeout(preloadDelayTimer);
|
3115
3563
|
return preloadDelayTimer = null;
|
@@ -3119,42 +3567,16 @@ You can change (or remove) this delay like this:
|
|
3119
3567
|
return busyDelayTimer = null;
|
3120
3568
|
};
|
3121
3569
|
reset = function() {
|
3122
|
-
cache = {};
|
3123
3570
|
$waitingLink = null;
|
3124
3571
|
cancelPreloadDelay();
|
3125
3572
|
cancelBusyDelay();
|
3126
3573
|
pendingCount = 0;
|
3127
3574
|
config.reset();
|
3128
|
-
|
3575
|
+
busyEventEmitted = false;
|
3576
|
+
return cache.clear();
|
3129
3577
|
};
|
3130
3578
|
reset();
|
3131
|
-
|
3132
|
-
normalizeRequest(request);
|
3133
|
-
return [request.url, request.method, request.data, request.selector].join('|');
|
3134
|
-
};
|
3135
|
-
trim = function() {
|
3136
|
-
var keys, oldestKey, oldestTimestamp;
|
3137
|
-
keys = u.keys(cache);
|
3138
|
-
if (keys.length > config.cacheSize) {
|
3139
|
-
oldestKey = null;
|
3140
|
-
oldestTimestamp = null;
|
3141
|
-
u.each(keys, function(key) {
|
3142
|
-
var promise, timestamp;
|
3143
|
-
promise = cache[key];
|
3144
|
-
timestamp = promise.timestamp;
|
3145
|
-
if (!oldestTimestamp || oldestTimestamp > timestamp) {
|
3146
|
-
oldestKey = key;
|
3147
|
-
return oldestTimestamp = timestamp;
|
3148
|
-
}
|
3149
|
-
});
|
3150
|
-
if (oldestKey) {
|
3151
|
-
return delete cache[oldestKey];
|
3152
|
-
}
|
3153
|
-
}
|
3154
|
-
};
|
3155
|
-
timestamp = function() {
|
3156
|
-
return (new Date()).valueOf();
|
3157
|
-
};
|
3579
|
+
alias = cache.alias;
|
3158
3580
|
normalizeRequest = function(request) {
|
3159
3581
|
if (!request._normalized) {
|
3160
3582
|
request.method = u.normalizeMethod(request.method);
|
@@ -3166,13 +3588,6 @@ You can change (or remove) this delay like this:
|
|
3166
3588
|
}
|
3167
3589
|
return request;
|
3168
3590
|
};
|
3169
|
-
alias = function(oldRequest, newRequest) {
|
3170
|
-
var promise;
|
3171
|
-
u.debug("Aliasing %o to %o", oldRequest, newRequest);
|
3172
|
-
if (promise = get(oldRequest)) {
|
3173
|
-
return set(newRequest, promise);
|
3174
|
-
}
|
3175
|
-
};
|
3176
3591
|
|
3177
3592
|
/**
|
3178
3593
|
Makes a request to the given URL and caches the response.
|
@@ -3198,8 +3613,8 @@ You can change (or remove) this delay like this:
|
|
3198
3613
|
*/
|
3199
3614
|
ajax = function(options) {
|
3200
3615
|
var forceCache, ignoreCache, pending, promise, request;
|
3201
|
-
forceCache =
|
3202
|
-
ignoreCache =
|
3616
|
+
forceCache = options.cache === true;
|
3617
|
+
ignoreCache = options.cache === false;
|
3203
3618
|
request = u.only(options, 'url', 'method', 'data', 'selector', '_normalized');
|
3204
3619
|
pending = true;
|
3205
3620
|
if (!isIdempotent(request) && !forceCache) {
|
@@ -3284,64 +3699,6 @@ You can change (or remove) this delay like this:
|
|
3284
3699
|
normalizeRequest(request);
|
3285
3700
|
return u.contains(SAFE_HTTP_METHODS, request.method);
|
3286
3701
|
};
|
3287
|
-
isFresh = function(promise) {
|
3288
|
-
var timeSinceTouch;
|
3289
|
-
timeSinceTouch = timestamp() - promise.timestamp;
|
3290
|
-
return timeSinceTouch < config.cacheExpiry;
|
3291
|
-
};
|
3292
|
-
|
3293
|
-
/**
|
3294
|
-
@protected
|
3295
|
-
@method up.proxy.get
|
3296
|
-
*/
|
3297
|
-
get = function(request) {
|
3298
|
-
var key, promise;
|
3299
|
-
key = cacheKey(request);
|
3300
|
-
if (promise = cache[key]) {
|
3301
|
-
if (!isFresh(promise)) {
|
3302
|
-
u.debug("Discarding stale cache entry for %o (%o)", request.url, request);
|
3303
|
-
remove(request);
|
3304
|
-
return void 0;
|
3305
|
-
} else {
|
3306
|
-
u.debug("Cache hit for %o (%o)", request.url, request);
|
3307
|
-
return promise;
|
3308
|
-
}
|
3309
|
-
} else {
|
3310
|
-
u.debug("Cache miss for %o (%o)", request.url, request);
|
3311
|
-
return void 0;
|
3312
|
-
}
|
3313
|
-
};
|
3314
|
-
|
3315
|
-
/**
|
3316
|
-
@protected
|
3317
|
-
@method up.proxy.set
|
3318
|
-
*/
|
3319
|
-
set = function(request, promise) {
|
3320
|
-
var key;
|
3321
|
-
trim();
|
3322
|
-
key = cacheKey(request);
|
3323
|
-
promise.timestamp = timestamp();
|
3324
|
-
cache[key] = promise;
|
3325
|
-
return promise;
|
3326
|
-
};
|
3327
|
-
|
3328
|
-
/**
|
3329
|
-
@protected
|
3330
|
-
@method up.proxy.remove
|
3331
|
-
*/
|
3332
|
-
remove = function(request) {
|
3333
|
-
var key;
|
3334
|
-
key = cacheKey(request);
|
3335
|
-
return delete cache[key];
|
3336
|
-
};
|
3337
|
-
|
3338
|
-
/**
|
3339
|
-
@protected
|
3340
|
-
@method up.proxy.clear
|
3341
|
-
*/
|
3342
|
-
clear = function() {
|
3343
|
-
return cache = {};
|
3344
|
-
};
|
3345
3702
|
checkPreload = function($link) {
|
3346
3703
|
var curriedPreload, delay;
|
3347
3704
|
delay = parseInt(u.presentAttr($link, 'up-delay')) || config.preloadDelay;
|
@@ -3503,7 +3860,7 @@ Read on
|
|
3503
3860
|
|
3504
3861
|
(function() {
|
3505
3862
|
up.link = (function() {
|
3506
|
-
var childClicked, follow, followMethod, shouldProcessLinkEvent, u, visit;
|
3863
|
+
var childClicked, follow, followMethod, makeFollowable, shouldProcessLinkEvent, u, visit;
|
3507
3864
|
u = up.util;
|
3508
3865
|
|
3509
3866
|
/**
|
@@ -3554,9 +3911,8 @@ Read on
|
|
3554
3911
|
or to `body` if such an attribute does not exist.
|
3555
3912
|
@param {Function|String} [options.transition]
|
3556
3913
|
A transition function or name.
|
3557
|
-
@param {Element|jQuery|String} [options.
|
3558
|
-
|
3559
|
-
case the replaced element is not visible in the viewport.
|
3914
|
+
@param {Element|jQuery|String} [options.reveal]
|
3915
|
+
Whether to reveal the followed element within its viewport.
|
3560
3916
|
@param {Number} [options.duration]
|
3561
3917
|
The duration of the transition. See [`up.morph`](/up.motion#up.morph).
|
3562
3918
|
@param {Number} [options.delay]
|
@@ -3570,10 +3926,11 @@ Read on
|
|
3570
3926
|
options = u.options(options);
|
3571
3927
|
url = u.option($link.attr('up-href'), $link.attr('href'));
|
3572
3928
|
selector = u.option(options.target, $link.attr('up-target'), 'body');
|
3573
|
-
options.transition = u.option(options.transition, $link
|
3574
|
-
options.history = u.option(options.history, $link
|
3575
|
-
options.
|
3576
|
-
options.cache = u.option(options.cache, $link
|
3929
|
+
options.transition = u.option(options.transition, u.castedAttr($link, 'up-transition'), u.castedAttr($link, 'up-animation'));
|
3930
|
+
options.history = u.option(options.history, u.castedAttr($link, 'up-history'));
|
3931
|
+
options.reveal = u.option(options.reveal, u.castedAttr($link, 'up-reveal'));
|
3932
|
+
options.cache = u.option(options.cache, u.castedAttr($link, 'up-cache'));
|
3933
|
+
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($link, 'up-restore-scroll'));
|
3577
3934
|
options.method = followMethod($link, options);
|
3578
3935
|
options = u.merge(options, up.motion.animateOptions(options, $link));
|
3579
3936
|
return up.replace(selector, url, options);
|
@@ -3642,6 +3999,9 @@ Read on
|
|
3642
3999
|
@param [up-href]
|
3643
4000
|
The destination URL to follow.
|
3644
4001
|
If omitted, the the link's `href` attribute will be used.
|
4002
|
+
@param [up-restore-scroll='false']
|
4003
|
+
Whether to restore the scroll position of all viewports
|
4004
|
+
within the target selector.
|
3645
4005
|
*/
|
3646
4006
|
up.on('click', 'a[up-target], [up-href][up-target]', function(event, $link) {
|
3647
4007
|
if (shouldProcessLinkEvent(event, $link)) {
|
@@ -3694,6 +4054,23 @@ Read on
|
|
3694
4054
|
return u.isUnmodifiedMouseEvent(event) && !childClicked(event, $link);
|
3695
4055
|
};
|
3696
4056
|
|
4057
|
+
/**
|
4058
|
+
Makes sure that the given link is handled by Up.js.
|
4059
|
+
|
4060
|
+
This is done by giving the link an `up-follow` attribute
|
4061
|
+
if it doesn't already have it an `up-target` or `up-follow` attribute.
|
4062
|
+
|
4063
|
+
@method up.link.makeFollowable
|
4064
|
+
@protected
|
4065
|
+
*/
|
4066
|
+
makeFollowable = function(link) {
|
4067
|
+
var $link;
|
4068
|
+
$link = $(link);
|
4069
|
+
if (u.isMissing($link.attr('up-target')) && u.isMissing($link.attr('up-follow'))) {
|
4070
|
+
return $link.attr('up-follow', '');
|
4071
|
+
}
|
4072
|
+
};
|
4073
|
+
|
3697
4074
|
/**
|
3698
4075
|
If applied on a link, Follows this link via AJAX and replaces the
|
3699
4076
|
current `<body>` element with the response's `<body>` element.
|
@@ -3720,6 +4097,9 @@ Read on
|
|
3720
4097
|
@param [up-href]
|
3721
4098
|
The destination URL to follow.
|
3722
4099
|
If omitted, the the link's `href` attribute will be used.
|
4100
|
+
@param [up-restore-scroll='false']
|
4101
|
+
Whether to restore the scroll position of all viewports
|
4102
|
+
within the response.
|
3723
4103
|
*/
|
3724
4104
|
up.on('click', 'a[up-follow], [up-href][up-follow]', function(event, $link) {
|
3725
4105
|
if (shouldProcessLinkEvent(event, $link)) {
|
@@ -3750,10 +4130,10 @@ Read on
|
|
3750
4130
|
@ujs
|
3751
4131
|
@method [up-expand]
|
3752
4132
|
*/
|
3753
|
-
up.compiler('[up-expand]', function($
|
4133
|
+
up.compiler('[up-expand]', function($area) {
|
3754
4134
|
var attribute, i, len, link, name, newAttrs, ref, upAttributePattern;
|
3755
|
-
link = $
|
3756
|
-
link || u.error('No link to expand within %o', $
|
4135
|
+
link = $area.find('a, [up-href]').get(0);
|
4136
|
+
link || u.error('No link to expand within %o', $area);
|
3757
4137
|
upAttributePattern = /^up-/;
|
3758
4138
|
newAttrs = {};
|
3759
4139
|
newAttrs['up-href'] = $(link).attr('href');
|
@@ -3765,9 +4145,9 @@ Read on
|
|
3765
4145
|
newAttrs[name] = attribute.value;
|
3766
4146
|
}
|
3767
4147
|
}
|
3768
|
-
u.
|
3769
|
-
|
3770
|
-
return $
|
4148
|
+
u.setMissingAttrs($area, newAttrs);
|
4149
|
+
$area.removeAttr('up-expand');
|
4150
|
+
return makeFollowable($area);
|
3771
4151
|
});
|
3772
4152
|
|
3773
4153
|
/**
|
@@ -3791,12 +4171,12 @@ Read on
|
|
3791
4171
|
*/
|
3792
4172
|
up.compiler('[up-dash]', function($element) {
|
3793
4173
|
var newAttrs, target;
|
3794
|
-
target = $element
|
4174
|
+
target = u.castedAttr($element, 'up-dash');
|
3795
4175
|
newAttrs = {
|
3796
4176
|
'up-preload': 'true',
|
3797
4177
|
'up-instant': 'true'
|
3798
4178
|
};
|
3799
|
-
if (
|
4179
|
+
if (target === true) {
|
3800
4180
|
newAttrs['up-follow'] = '';
|
3801
4181
|
} else {
|
3802
4182
|
newAttrs['up-target'] = target;
|
@@ -3808,6 +4188,7 @@ Read on
|
|
3808
4188
|
knife: eval(typeof Knife !== "undefined" && Knife !== null ? Knife.point : void 0),
|
3809
4189
|
visit: visit,
|
3810
4190
|
follow: follow,
|
4191
|
+
makeFollowable: makeFollowable,
|
3811
4192
|
childClicked: childClicked,
|
3812
4193
|
followMethod: followMethod
|
3813
4194
|
};
|
@@ -3907,15 +4288,15 @@ We need to work on this page:
|
|
3907
4288
|
failureSelector = u.option(options.failTarget, $form.attr('up-fail-target'), function() {
|
3908
4289
|
return u.createSelectorFromElement($form);
|
3909
4290
|
});
|
3910
|
-
historyOption = u.option(options.history, $form
|
3911
|
-
successTransition = u.option(options.transition, $form
|
3912
|
-
failureTransition = u.option(options.failTransition, $form
|
4291
|
+
historyOption = u.option(options.history, u.castedAttr($form, 'up-history'), true);
|
4292
|
+
successTransition = u.option(options.transition, u.castedAttr($form, 'up-transition'));
|
4293
|
+
failureTransition = u.option(options.failTransition, u.castedAttr($form, 'up-fail-transition'), successTransition);
|
3913
4294
|
httpMethod = u.option(options.method, $form.attr('up-method'), $form.attr('data-method'), $form.attr('method'), 'post').toUpperCase();
|
3914
4295
|
animateOptions = up.motion.animateOptions(options, $form);
|
3915
|
-
useCache = u.option(options.cache, $form
|
4296
|
+
useCache = u.option(options.cache, u.castedAttr($form, 'up-cache'));
|
3916
4297
|
url = u.option(options.url, $form.attr('action'), up.browser.url());
|
3917
4298
|
$form.addClass('up-active');
|
3918
|
-
if (!up.browser.canPushState() &&
|
4299
|
+
if (!up.browser.canPushState() && historyOption !== false) {
|
3919
4300
|
$form.get(0).submit();
|
3920
4301
|
return;
|
3921
4302
|
}
|
@@ -3928,7 +4309,16 @@ We need to work on this page:
|
|
3928
4309
|
};
|
3929
4310
|
successUrl = function(xhr) {
|
3930
4311
|
var currentLocation;
|
3931
|
-
url =
|
4312
|
+
url = void 0;
|
4313
|
+
if (u.isGiven(historyOption)) {
|
4314
|
+
if (historyOption === false || u.isString(historyOption)) {
|
4315
|
+
url = historyOption;
|
4316
|
+
} else if (currentLocation = u.locationFromXhr(xhr)) {
|
4317
|
+
url = currentLocation;
|
4318
|
+
} else if (request.type === 'GET') {
|
4319
|
+
url = request.url + '?' + request.data;
|
4320
|
+
}
|
4321
|
+
}
|
3932
4322
|
return u.option(url, false);
|
3933
4323
|
};
|
3934
4324
|
return up.proxy.ajax(request).always(function() {
|
@@ -4298,8 +4688,8 @@ We need to work on this page:
|
|
4298
4688
|
selector = u.option(options.target, $link.attr('up-popup'), 'body');
|
4299
4689
|
position = u.option(options.position, $link.attr('up-position'), config.position);
|
4300
4690
|
animation = u.option(options.animation, $link.attr('up-animation'), config.openAnimation);
|
4301
|
-
sticky = u.option(options.sticky, $link
|
4302
|
-
history = up.browser.canPushState() ? u.option(options.history, $link
|
4691
|
+
sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'));
|
4692
|
+
history = up.browser.canPushState() ? u.option(options.history, u.castedAttr($link, 'up-history'), false) : false;
|
4303
4693
|
animateOptions = up.motion.animateOptions(options, $link);
|
4304
4694
|
close();
|
4305
4695
|
$popup = createHiddenPopup($link, selector, sticky);
|
@@ -4659,8 +5049,8 @@ For small popup overlays ("dropdowns") see [up.popup](/up.popup) instead.
|
|
4659
5049
|
maxWidth = u.option(options.maxWidth, $link.attr('up-max-width'), config.maxWidth);
|
4660
5050
|
height = u.option(options.height, $link.attr('up-height'), config.height);
|
4661
5051
|
animation = u.option(options.animation, $link.attr('up-animation'), config.openAnimation);
|
4662
|
-
sticky = u.option(options.sticky, $link
|
4663
|
-
history = up.browser.canPushState() ? u.option(options.history, $link
|
5052
|
+
sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'));
|
5053
|
+
history = up.browser.canPushState() ? u.option(options.history, u.castedAttr($link, 'up-history'), true) : false;
|
4664
5054
|
animateOptions = up.motion.animateOptions(options, $link);
|
4665
5055
|
close();
|
4666
5056
|
$modal = createHiddenModal({
|
@@ -4938,7 +5328,7 @@ We need to work on this page:
|
|
4938
5328
|
$link = $(linkOrSelector);
|
4939
5329
|
html = u.option(options.html, $link.attr('up-tooltip'), $link.attr('title'));
|
4940
5330
|
position = u.option(options.position, $link.attr('up-position'), 'top');
|
4941
|
-
animation = u.option(options.animation, $link
|
5331
|
+
animation = u.option(options.animation, u.castedAttr($link, 'up-animation'), 'fade-in');
|
4942
5332
|
animateOptions = up.motion.animateOptions(options, $link);
|
4943
5333
|
close();
|
4944
5334
|
$tooltip = createElement(html);
|
@@ -5028,18 +5418,17 @@ by providing instant feedback for user interactions.
|
|
5028
5418
|
The class to set on [links that point the current location](#up-current).
|
5029
5419
|
*/
|
5030
5420
|
config = u.config({
|
5031
|
-
|
5421
|
+
currentClasses: ['up-current']
|
5032
5422
|
});
|
5033
5423
|
reset = function() {
|
5034
5424
|
return config.reset();
|
5035
5425
|
};
|
5036
5426
|
currentClass = function() {
|
5037
|
-
var
|
5038
|
-
|
5039
|
-
|
5040
|
-
|
5041
|
-
|
5042
|
-
return klass;
|
5427
|
+
var classes;
|
5428
|
+
classes = config.currentClasses;
|
5429
|
+
classes = classes.concat(['up-current']);
|
5430
|
+
classes = u.uniq(classes);
|
5431
|
+
return classes.join(' ');
|
5043
5432
|
};
|
5044
5433
|
CLASS_ACTIVE = 'up-active';
|
5045
5434
|
SELECTORS_SECTION = ['a', '[up-href]', '[up-alias]'];
|