upjs-rails 0.9.1 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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]'];
|