/** @module up */ (function() { window.up = {}; }).call(this); /** Utility functions ================= All methods in this module are for internal use by the Up.js framework and will frequently change between releases. If you use them in your own code, you will get hurt. @protected @class up.util */ (function() { var slice = [].slice; up.util = (function($) { var $createElementFromSelector, ANIMATION_PROMISE_KEY, CONSOLE_PLACEHOLDERS, ajax, any, cache, castedAttr, clientSize, compact, config, contains, copy, copyAttributes, createElement, createElementFromHtml, createSelectorFromElement, cssAnimate, debug, detect, each, emptyJQuery, endsWith, error, escapePressed, evalConsoleTemplate, extend, findWithSelf, finishCssAnimate, fixedToAbsolute, forceCompositing, identity, ifGiven, isArray, isBlank, isDeferred, isDefined, isElement, isFunction, isGiven, isHash, isJQuery, isMissing, isNull, isNumber, isObject, isPresent, isPromise, isStandardPort, isString, isUndefined, isUnmodifiedKeyEvent, isUnmodifiedMouseEvent, last, locationFromXhr, map, measure, memoize, merge, methodFromXhr, multiSelector, nextFrame, normalizeMethod, normalizeUrl, nullJquery, offsetParent, once, only, option, options, parseUrl, presence, presentAttr, remove, resolvableWhen, resolvedDeferred, resolvedPromise, scrollbarWidth, select, setMissingAttrs, startsWith, temporaryCss, times, titleFromXhr, toArray, trim, unJquery, uniq, unresolvablePromise, unwrapElement, warn; memoize = function(func) { var cache, cached; cache = void 0; cached = false; return function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; if (cached) { return cache; } else { cached = true; return cache = func.apply(null, args); } }; }; ajax = function(request) { request = copy(request); if (request.selector) { request.headers || (request.headers = {}); request.headers['X-Up-Selector'] = request.selector; } return $.ajax(request); }; /** @function up.util.isStandardPort @private */ isStandardPort = function(protocol, port) { return ((port === "" || port === "80") && protocol === 'http:') || (port === "443" && protocol === 'https:'); }; /** Normalizes URLs, relative paths and absolute paths to a full URL that can be checked for equality with other normalized URL. By default hashes are ignored, search queries are included. @function up.util.normalizeUrl @param {Boolean} [options.hash=false] Whether to include an `#hash` anchor in the normalized URL @param {Boolean} [options.search=true] Whether to include a `?query` string in the normalized URL @param {Boolean} [options.stripTrailingSlash=false] Whether to strip a trailing slash from the pathname @protected */ normalizeUrl = function(urlOrAnchor, options) { var anchor, normalized, pathname; anchor = parseUrl(urlOrAnchor); normalized = anchor.protocol + "//" + anchor.hostname; if (!isStandardPort(anchor.protocol, anchor.port)) { normalized += ":" + anchor.port; } pathname = anchor.pathname; if (pathname[0] !== '/') { pathname = "/" + pathname; } if ((options != null ? options.stripTrailingSlash : void 0) === true) { pathname = pathname.replace(/\/$/, ''); } normalized += pathname; if ((options != null ? options.hash : void 0) === true) { normalized += anchor.hash; } if ((options != null ? options.search : void 0) !== false) { normalized += anchor.search; } return normalized; }; /** @function up.util.parseUrl @private */ parseUrl = function(urlOrAnchor) { var anchor; anchor = null; if (isString(urlOrAnchor)) { anchor = $('').attr({ href: urlOrAnchor }).get(0); if (isBlank(anchor.hostname)) { anchor.href = anchor.href; } } else { anchor = unJquery(urlOrAnchor); } return anchor; }; /** @function up.util.normalizeMethod @protected */ normalizeMethod = function(method) { if (method) { return method.toUpperCase(); } else { return 'GET'; } }; $createElementFromSelector = function(selector) { var $element, $parent, $root, classes, conjunction, depthSelector, expression, html, id, iteration, j, k, len, len1, path, tag; path = selector.split(/[ >]/); $root = null; for (iteration = j = 0, len = path.length; j < len; iteration = ++j) { depthSelector = path[iteration]; conjunction = depthSelector.match(/(^|\.|\#)[A-Za-z0-9\-_]+/g); tag = "div"; classes = []; id = null; for (k = 0, len1 = conjunction.length; k < len1; k++) { expression = conjunction[k]; switch (expression[0]) { case ".": classes.push(expression.substr(1)); break; case "#": id = expression.substr(1); break; default: tag = expression; } } html = "<" + tag; if (classes.length) { html += " class=\"" + classes.join(" ") + "\""; } if (id) { html += " id=\"" + id + "\""; } html += ">"; $element = $(html); if ($parent) { $element.appendTo($parent); } if (iteration === 0) { $root = $element; } $parent = $element; } return $root; }; createElement = function(tagName, html) { var element; element = document.createElement(tagName); if (isPresent(html)) { element.innerHTML = html; } return element; }; /** @function up.debug @protected */ debug = function() { var args, message, ref; message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; message = "[UP] " + message; return (ref = up.browser).puts.apply(ref, ['debug', message].concat(slice.call(args))); }; /** @function up.warn @protected */ warn = function() { var args, message, ref; message = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; message = "[UP] " + message; return (ref = up.browser).puts.apply(ref, ['warn', message].concat(slice.call(args))); }; /** Throws a fatal error with the given message. - The error will be printed to the [error console](https://developer.mozilla.org/en-US/docs/Web/API/Console/error) - An [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) (exception) will be thrown, unwinding the current call stack - The error message will be printed in a corner of the screen \#\#\#\# Examples up.error('Division by zero') up.error('Unexpected result %o', result) @function up.error */ error = function() { var $error, args, asString, ref; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; args[0] = "[UP] " + args[0]; (ref = up.browser).puts.apply(ref, ['error'].concat(slice.call(args))); asString = evalConsoleTemplate.apply(null, args); $error = presence($('.up-error')) || $('
').prependTo('body'); $error.addClass('up-error'); $error.text(asString); throw new Error(asString); }; CONSOLE_PLACEHOLDERS = /\%[odisf]/g; evalConsoleTemplate = function() { var args, i, maxLength, message; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; message = args[0]; i = 0; maxLength = 80; return message.replace(CONSOLE_PLACEHOLDERS, function() { var arg, argType; i += 1; arg = args[i]; argType = typeof arg; if (argType === 'string') { arg = arg.replace(/\s+/g, ' '); if (arg.length > maxLength) { arg = (arg.substr(0, maxLength)) + "…"; } arg = "\"" + arg + "\""; } else if (argType === 'undefined') { arg = 'undefined'; } else if (argType === 'number' || argType === 'function') { arg = arg.toString(); } else { arg = JSON.stringify(arg); } if (arg.length > maxLength) { arg = (arg.substr(0, maxLength)) + " …"; if (argType === 'object' || argType === 'function') { arg += " }"; } } return arg; }); }; createSelectorFromElement = function(element) { var $element, classString, classes, id, j, klass, len, name, selector, upId; $element = $(element); selector = void 0; debug("Creating selector from element %o", $element.get(0)); if (upId = presence($element.attr("up-id"))) { selector = "[up-id='" + upId + "']"; } else if (id = presence($element.attr("id"))) { selector = "#" + id; } else if (name = presence($element.attr("name"))) { selector = "[name='" + name + "']"; } else if (classString = presence($element.attr("class"))) { classes = classString.split(' '); selector = ''; for (j = 0, len = classes.length; j < len; j++) { klass = classes[j]; selector += "." + klass; } } else { selector = $element.prop('tagName').toLowerCase(); } return selector; }; createElementFromHtml = function(html) { var anything, bodyElement, bodyMatch, bodyPattern, capture, closeTag, headElement, htmlElement, openTag, titleElement, titleMatch, titlePattern; openTag = function(tag) { return "<" + tag + "(?: [^>]*)?>"; }; closeTag = function(tag) { return ""; }; anything = '(?:.|\\n)*?'; capture = function(pattern) { return "(" + pattern + ")"; }; titlePattern = new RegExp(openTag('head') + anything + openTag('title') + capture(anything) + closeTag('title') + anything + closeTag('body'), 'i'); bodyPattern = new RegExp(openTag('body') + capture(anything) + closeTag('body'), 'i'); if (bodyMatch = html.match(bodyPattern)) { htmlElement = document.createElement('html'); bodyElement = createElement('body', bodyMatch[1]); htmlElement.appendChild(bodyElement); if (titleMatch = html.match(titlePattern)) { headElement = createElement('head'); htmlElement.appendChild(headElement); titleElement = createElement('title', titleMatch[1]); headElement.appendChild(titleElement); } return htmlElement; } else { return createElement('div', html); } }; extend = $.extend; trim = $.trim; each = function(collection, block) { var index, item, j, len, results; results = []; for (index = j = 0, len = collection.length; j < len; index = ++j) { item = collection[index]; results.push(block(item, index)); } return results; }; map = each; identity = function(x) { return x; }; times = function(count, block) { var iteration, j, ref, results; results = []; for (iteration = j = 0, ref = count - 1; 0 <= ref ? j <= ref : j >= ref; iteration = 0 <= ref ? ++j : --j) { results.push(block(iteration)); } return results; }; isNull = function(object) { return object === null; }; isUndefined = function(object) { return object === void(0); }; isDefined = function(object) { return !isUndefined(object); }; isMissing = function(object) { return isUndefined(object) || isNull(object); }; isGiven = function(object) { return !isMissing(object); }; isBlank = function(object) { return isMissing(object) || (isObject(object) && Object.keys(object).length === 0) || (object.length === 0); }; presence = function(object, checker) { if (checker == null) { checker = isPresent; } if (checker(object)) { return object; } else { return null; } }; isPresent = function(object) { return !isBlank(object); }; isFunction = function(object) { return typeof object === 'function'; }; isString = function(object) { return typeof object === 'string'; }; isNumber = function(object) { return typeof object === 'number'; }; isHash = function(object) { return typeof object === 'object' && !!object; }; isObject = function(object) { return isHash(object) || (typeof object === 'function'); }; isElement = function(object) { return !!(object && object.nodeType === 1); }; isJQuery = function(object) { return object instanceof jQuery; }; isPromise = function(object) { return isObject(object) && isFunction(object.then); }; isDeferred = function(object) { return isPromise(object) && isFunction(object.resolve); }; ifGiven = function(object) { if (isGiven(object)) { return object; } }; isArray = Array.isArray || function(object) { return Object.prototype.toString.call(object) === '[object Array]'; }; toArray = function(object) { return Array.prototype.slice.call(object); }; copy = function(object) { if (isArray(object)) { return object.slice(); } else { return extend({}, object); } }; unJquery = function(object) { if (isJQuery(object)) { return object.get(0); } else { return object; } }; merge = function(object, otherObject) { return extend(copy(object), otherObject); }; options = function(object, defaults) { var defaultValue, key, merged, value; merged = object ? copy(object) : {}; if (defaults) { for (key in defaults) { defaultValue = defaults[key]; value = merged[key]; if (!isGiven(value)) { merged[key] = defaultValue; } else if (isObject(defaultValue) && isObject(value)) { merged[key] = options(value, defaultValue); } } } return merged; }; /** Returns the first argument that is considered present. If an argument is a function, it is called and the value is checked for presence. This function is useful when you have multiple option sources and the value can be boolean. In that case you cannot change the sources with a `||` operator (since that doesn't short-circuit at `false`). @function up.util.option @param {Array} args... */ option = function() { var arg, args, j, len, match, value; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; match = void 0; for (j = 0, len = args.length; j < len; j++) { arg = args[j]; value = arg; if (isFunction(value)) { value = value(); } if (isGiven(value)) { match = value; break; } } return match; }; detect = function(array, tester) { var element, j, len, match; match = void 0; for (j = 0, len = array.length; j < len; j++) { element = array[j]; if (tester(element)) { match = element; break; } } return match; }; any = function(array, tester) { var match; match = detect(array, tester); return isDefined(match); }; compact = function(array) { return select(array, isGiven); }; uniq = function(array) { var seen; seen = {}; return select(array, function(element) { if (seen.hasOwnProperty(element)) { return false; } else { return seen[element] = true; } }); }; select = function(array, tester) { var matches; matches = []; each(array, function(element) { if (tester(element)) { return matches.push(element); } }); return matches; }; presentAttr = function() { var $element, attrName, attrNames, values; $element = arguments[0], attrNames = 2 <= arguments.length ? slice.call(arguments, 1) : []; values = (function() { var j, len, results; results = []; for (j = 0, len = attrNames.length; j < len; j++) { attrName = attrNames[j]; results.push($element.attr(attrName)); } return results; })(); return detect(values, isPresent); }; nextFrame = function(block) { return setTimeout(block, 0); }; last = function(array) { return array[array.length - 1]; }; clientSize = function() { var element; element = document.documentElement; return { width: element.clientWidth, height: element.clientHeight }; }; scrollbarWidth = memoize(function() { var $outer, outer, width; $outer = $('
').css({ position: 'absolute', top: '0', left: '0', width: '50px', height: '50px', overflowY: 'scroll' }); $outer.appendTo(document.body); outer = $outer.get(0); width = outer.offsetWidth - outer.clientWidth; $outer.remove(); return width; }); /** Modifies the given function so it only runs once. Subsequent calls will return the previous return value. @function up.util.once @private */ once = function(fun) { var result; result = void 0; return function() { if (fun != null) { result = fun(); } fun = void 0; return result; }; }; /** * Temporarily sets the CSS for the given element. # * @function up.util.temporaryCss * @param {jQuery} $element * @param {Object} css * @param {Function} [block] * If given, the CSS is set, the block is called and * the old CSS is restored. * @private */ temporaryCss = function($element, css, block) { var memo, oldCss; oldCss = $element.css(Object.keys(css)); $element.css(css); memo = function() { return $element.css(oldCss); }; if (block) { block(); return memo(); } else { return once(memo); } }; forceCompositing = function($element) { var memo, oldTransforms; oldTransforms = $element.css(['transform', '-webkit-transform']); if (isBlank(oldTransforms) || oldTransforms['transform'] === 'none') { memo = function() { return $element.css(oldTransforms); }; $element.css({ 'transform': 'translateZ(0)', '-webkit-transform': 'translateZ(0)' }); } else { memo = function() {}; } return memo; }; /** Animates the given element's CSS properties using CSS transitions. If the element is already being animated, the previous animation will instantly jump to its last frame before the new animation begins. To improve performance, the element will be forced into compositing for the duration of the animation. @function up.util.cssAnimate @param {Element|jQuery|String} elementOrSelector The element to animate. @param {Object} lastFrame The CSS properties that should be transitioned to. @param {Number} [options.duration=300] The duration of the animation, in milliseconds. @param {Number} [options.delay=0] The delay before the animation starts, in milliseconds. @param {String} [options.easing='ease'] The timing function that controls the animation's acceleration. See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function) for a list of pre-defined timing functions. @return A promise for the animation's end. */ cssAnimate = function(elementOrSelector, lastFrame, opts) { var $element, deferred, endTimeout, transition, withoutCompositing, withoutTransition; $element = $(elementOrSelector); if (up.browser.canCssAnimation()) { opts = options(opts, { duration: 300, delay: 0, easing: 'ease' }); deferred = $.Deferred(); transition = { 'transition-property': Object.keys(lastFrame).join(', '), 'transition-duration': opts.duration + "ms", 'transition-delay': opts.delay + "ms", 'transition-timing-function': opts.easing }; withoutCompositing = forceCompositing($element); withoutTransition = temporaryCss($element, transition); $element.css(lastFrame); deferred.then(withoutCompositing); deferred.then(withoutTransition); $element.data(ANIMATION_PROMISE_KEY, deferred); deferred.then(function() { return $element.removeData(ANIMATION_PROMISE_KEY); }); endTimeout = setTimeout((function() { return deferred.resolve(); }), opts.duration + opts.delay); deferred.then(function() { return clearTimeout(endTimeout); }); return deferred; } else { $element.css(lastFrame); return resolvedDeferred(); } }; ANIMATION_PROMISE_KEY = 'up-animation-promise'; /** Completes the animation for the given element by jumping to the last frame instantly. All callbacks chained to the original animation's promise will be called. Does nothing if the given element is not currently animating. Also see [`up.motion.finish`](/up.motion.finish). @function up.util.finishCssAnimate @protected @param {Element|jQuery|String} elementOrSelector */ finishCssAnimate = function(elementOrSelector) { return $(elementOrSelector).each(function() { var existingAnimation; if (existingAnimation = $(this).data(ANIMATION_PROMISE_KEY)) { return existingAnimation.resolve(); } }); }; measure = function($element, opts) { var $context, box, contextCoords, coordinates, elementCoords, viewport; opts = options(opts, { relative: false, inner: false, full: false }); if (opts.relative) { if (opts.relative === true) { coordinates = $element.position(); } else { $context = $(opts.relative); elementCoords = $element.offset(); if ($context.is(document)) { coordinates = elementCoords; } else { contextCoords = $context.offset(); coordinates = { left: elementCoords.left - contextCoords.left, top: elementCoords.top - contextCoords.top }; } } } else { coordinates = $element.offset(); } box = { left: coordinates.left, top: coordinates.top }; if (opts.inner) { box.width = $element.width(); box.height = $element.height(); } else { box.width = $element.outerWidth(); box.height = $element.outerHeight(); } if (opts.full) { viewport = clientSize(); box.right = viewport.width - (box.left + box.width); box.bottom = viewport.height - (box.top + box.height); } return box; }; copyAttributes = function($source, $target) { var attr, j, len, ref, results; ref = $source.get(0).attributes; results = []; for (j = 0, len = ref.length; j < len; j++) { attr = ref[j]; if (attr.specified) { results.push($target.attr(attr.name, attr.value)); } else { results.push(void 0); } } return results; }; findWithSelf = function($element, selector) { return $element.find(selector).addBack(selector); }; escapePressed = function(event) { return event.keyCode === 27; }; startsWith = function(string, element) { return string.indexOf(element) === 0; }; endsWith = function(string, element) { return string.indexOf(element) === string.length - element.length; }; contains = function(stringOrArray, element) { return stringOrArray.indexOf(element) >= 0; }; castedAttr = function($element, attrName) { var value; value = $element.attr(attrName); switch (value) { case 'false': return false; case 'true': return true; case '': return true; default: return value; } }; locationFromXhr = function(xhr) { return xhr.getResponseHeader('X-Up-Location'); }; titleFromXhr = function(xhr) { return xhr.getResponseHeader('X-Up-Title'); }; methodFromXhr = function(xhr) { return xhr.getResponseHeader('X-Up-Method'); }; only = function() { var filtered, j, key, keys, len, object; object = arguments[0], keys = 2 <= arguments.length ? slice.call(arguments, 1) : []; filtered = {}; for (j = 0, len = keys.length; j < len; j++) { key = keys[j]; if (object.hasOwnProperty(key)) { filtered[key] = object[key]; } } return filtered; }; isUnmodifiedKeyEvent = function(event) { return !(event.metaKey || event.shiftKey || event.ctrlKey); }; isUnmodifiedMouseEvent = function(event) { var isLeftButton; isLeftButton = isUndefined(event.button) || event.button === 0; return isLeftButton && isUnmodifiedKeyEvent(event); }; resolvedDeferred = function() { var deferred; deferred = $.Deferred(); deferred.resolve(); return deferred; }; resolvedPromise = function() { return resolvedDeferred().promise(); }; unresolvablePromise = function() { return $.Deferred().promise(); }; nullJquery = function() { return { is: function() { return false; }, attr: function() {}, find: function() { return []; } }; }; resolvableWhen = function() { var deferreds, joined; deferreds = 1 <= arguments.length ? slice.call(arguments, 0) : []; joined = $.when.apply($, deferreds); joined.resolve = function() { return each(deferreds, function(deferred) { return typeof deferred.resolve === "function" ? deferred.resolve() : void 0; }); }; return joined; }; setMissingAttrs = function($element, attrs) { var key, results, value; results = []; for (key in attrs) { value = attrs[key]; if (isMissing($element.attr(key))) { results.push($element.attr(key, value)); } else { results.push(void 0); } } return results; }; remove = function(array, element) { var index; index = array.indexOf(element); if (index >= 0) { array.splice(index, 1); return element; } }; emptyJQuery = function() { return $([]); }; multiSelector = function(parts) { var combinedSelector, elements, j, len, obj, part, selectors; obj = {}; selectors = []; elements = []; for (j = 0, len = parts.length; j < len; j++) { part = parts[j]; if (isString(part)) { selectors.push(part); } else { elements.push(part); } } obj.parsed = elements; if (selectors.length) { combinedSelector = selectors.join(', '); obj.parsed.push(combinedSelector); } obj.select = function() { return obj.find(void 0); }; obj.find = function($root) { var $matches, $result, k, len1, ref, selector; $result = emptyJQuery(); ref = obj.parsed; for (k = 0, len1 = ref.length; k < len1; k++) { selector = ref[k]; $matches = $root ? $root.find(selector) : $(selector); $result = $result.add($matches); } return $result; }; obj.findWithSelf = function($start) { var $matches; $matches = obj.find($start); if (obj.doesMatch($start)) { $matches = $matches.add($start); } return $matches; }; obj.doesMatch = function(element) { var $element; $element = $(element); return any(obj.parsed, function(selector) { return $element.is(selector); }); }; obj.seekUp = function(start) { var $element, $result, $start; $start = $(start); $element = $start; $result = void 0; while ($element.length) { if (obj.doesMatch($element)) { $result = $element; break; } $element = $element.parent(); } return $result || emptyJQuery(); }; return obj; }; /** @function up.util.cache @param {Number|Function} [config.size] Maximum number of cache entries. Set to `undefined` to not limit the cache size. @param {Number|Function} [config.expiry] The number of milliseconds after which a cache entry will be discarded. @param {String} [config.log] A prefix for log entries printed by this cache object. @param {Function} [config.key] A function that takes an argument and returns a `String` key for storage. If omitted, `toString()` is called on the argument. */ cache = function(config) { var alias, clear, expiryMilis, get, isFresh, keys, log, maxSize, normalizeStoreKey, set, store, timestamp; if (config == null) { config = {}; } store = void 0; clear = function() { return store = {}; }; clear(); log = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; if (config.log) { args[0] = "[" + config.log + "] " + args[0]; return debug.apply(null, args); } }; keys = function() { return Object.keys(store); }; maxSize = function() { if (isMissing(config.size)) { return void 0; } else if (isFunction(config.size)) { return config.size(); } else if (isNumber(config.size)) { return config.size; } else { return error("Invalid size config: %o", config.size); } }; expiryMilis = function() { if (isMissing(config.expiry)) { return void 0; } else if (isFunction(config.expiry)) { return config.expiry(); } else if (isNumber(config.expiry)) { return config.expiry; } else { return error("Invalid expiry config: %o", config.expiry); } }; normalizeStoreKey = function(key) { if (config.key) { return config.key(key); } else { return key.toString(); } }; trim = function() { var oldestKey, oldestTimestamp, size, storeKeys; storeKeys = copy(keys()); size = maxSize(); if (size && storeKeys.length > size) { oldestKey = null; oldestTimestamp = null; each(storeKeys, function(key) { var promise, timestamp; promise = store[key]; timestamp = promise.timestamp; if (!oldestTimestamp || oldestTimestamp > timestamp) { oldestKey = key; return oldestTimestamp = timestamp; } }); if (oldestKey) { return delete store[oldestKey]; } } }; alias = function(oldKey, newKey) { var value; value = get(oldKey); if (isDefined(value)) { return set(newKey, value); } }; timestamp = function() { return (new Date()).valueOf(); }; set = function(key, value) { var storeKey; storeKey = normalizeStoreKey(key); return store[storeKey] = { timestamp: timestamp(), value: value }; }; remove = function(key) { var storeKey; storeKey = normalizeStoreKey(key); return delete store[storeKey]; }; isFresh = function(entry) { var expiry, timeSinceTouch; expiry = expiryMilis(); if (expiry) { timeSinceTouch = timestamp() - entry.timestamp; return timeSinceTouch < expiryMilis(); } else { return true; } }; get = function(key, fallback) { var entry, storeKey; if (fallback == null) { fallback = void 0; } storeKey = normalizeStoreKey(key); if (entry = store[storeKey]) { if (isFresh(entry)) { log("Cache hit for %o", key); return entry.value; } else { log("Discarding stale cache entry for %o", key); remove(key); return fallback; } } else { log("Cache miss for %o", key); return fallback; } }; return { alias: alias, get: get, set: set, remove: remove, clear: clear, keys: keys }; }; config = function(factoryOptions) { var hash; if (factoryOptions == null) { factoryOptions = {}; } hash = {}; hash.reset = function() { return extend(hash, factoryOptions); }; hash.reset(); Object.preventExtensions(hash); return hash; }; unwrapElement = function(wrapper) { var parent, wrappedNodes; wrapper = unJquery(wrapper); parent = wrapper.parentNode; wrappedNodes = toArray(wrapper.childNodes); each(wrappedNodes, function(wrappedNode) { return parent.insertBefore(wrappedNode, wrapper); }); return parent.removeChild(wrapper); }; offsetParent = function($element) { var $match, position; $match = void 0; while (($element = $element.parent()) && $element.length) { position = $element.css('position'); if (position === 'absolute' || position === 'relative' || $element.is('body')) { $match = $element; break; } } return $match; }; fixedToAbsolute = function(element, $viewport) { var $element, $futureOffsetParent, elementCoords, futureParentCoords; $element = $(element); $futureOffsetParent = offsetParent($element); elementCoords = $element.position(); futureParentCoords = $futureOffsetParent.offset(); return $element.css({ position: 'absolute', left: elementCoords.left - futureParentCoords.left, top: elementCoords.top - futureParentCoords.top + $viewport.scrollTop(), right: '', bottom: '' }); }; return { offsetParent: offsetParent, fixedToAbsolute: fixedToAbsolute, presentAttr: presentAttr, createElement: createElement, parseUrl: parseUrl, normalizeUrl: normalizeUrl, normalizeMethod: normalizeMethod, createElementFromHtml: createElementFromHtml, $createElementFromSelector: $createElementFromSelector, createSelectorFromElement: createSelectorFromElement, ajax: ajax, extend: extend, copy: copy, merge: merge, options: options, option: option, error: error, debug: debug, warn: warn, each: each, map: map, identity: identity, times: times, any: any, detect: detect, select: select, compact: compact, uniq: uniq, last: last, isNull: isNull, isDefined: isDefined, isUndefined: isUndefined, isGiven: isGiven, isMissing: isMissing, isPresent: isPresent, isBlank: isBlank, presence: presence, isObject: isObject, isFunction: isFunction, isString: isString, isElement: isElement, isJQuery: isJQuery, isPromise: isPromise, isDeferred: isDeferred, isHash: isHash, ifGiven: ifGiven, isUnmodifiedKeyEvent: isUnmodifiedKeyEvent, isUnmodifiedMouseEvent: isUnmodifiedMouseEvent, nullJquery: nullJquery, unJquery: unJquery, nextFrame: nextFrame, measure: measure, temporaryCss: temporaryCss, cssAnimate: cssAnimate, finishCssAnimate: finishCssAnimate, forceCompositing: forceCompositing, escapePressed: escapePressed, copyAttributes: copyAttributes, findWithSelf: findWithSelf, contains: contains, startsWith: startsWith, endsWith: endsWith, isArray: isArray, toArray: toArray, castedAttr: castedAttr, locationFromXhr: locationFromXhr, titleFromXhr: titleFromXhr, methodFromXhr: methodFromXhr, clientSize: clientSize, only: only, trim: trim, unresolvablePromise: unresolvablePromise, resolvedPromise: resolvedPromise, resolvedDeferred: resolvedDeferred, resolvableWhen: resolvableWhen, setMissingAttrs: setMissingAttrs, remove: remove, memoize: memoize, scrollbarWidth: scrollbarWidth, config: config, cache: cache, unwrapElement: unwrapElement, multiSelector: multiSelector, emptyJQuery: emptyJQuery, evalConsoleTemplate: evalConsoleTemplate }; })($); up.error = up.util.error; up.warn = up.util.warn; up.debug = up.util.debug; }).call(this); /** Browser interface ================= Some browser-interfacing methods and switches that we can't currently get rid off. @protected @class up.browser */ (function() { var slice = [].slice; up.browser = (function($) { var canCssAnimation, canInputEvent, canLogSubstitution, canPushState, initialRequestMethod, isIE8OrWorse, isIE9OrWorse, isRecentJQuery, isSupported, loadPage, popCookie, puts, u, url; u = up.util; loadPage = function(url, options) { var $form, csrfParam, csrfToken, metadataInput, method, target; if (options == null) { options = {}; } method = u.option(options.method, 'get').toLowerCase(); if (method === 'get') { return location.href = url; } else if ($.rails) { target = options.target; csrfToken = $.rails.csrfToken(); csrfParam = $.rails.csrfParam(); $form = $("
"); metadataInput = ""; if (u.isDefined(csrfParam) && u.isDefined(csrfToken)) { metadataInput += ""; } if (target) { $form.attr('target', target); } $form.hide().append(metadataInput).appendTo('body'); return $form.submit(); } else { return error("Can't fake a " + (method.toUpperCase()) + " request without Rails UJS"); } }; /** A cross-browser way to interact with `console.log`, `console.error`, etc. This function falls back to `console.log` if the output stream is not implemented. It also prints substitution strings (e.g. `console.log("From %o to %o", "a", "b")`) as a single string if the browser console does not support substitution strings. @function up.browser.puts @protected */ puts = function() { var args, message, stream; stream = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; u.isDefined(console[stream]) || (stream = 'log'); if (canLogSubstitution()) { return console[stream].apply(console, args); } else { message = u.evalConsoleTemplate.apply(u, args); return console[stream](message); } }; url = function() { return location.href; }; canPushState = u.memoize(function() { return u.isDefined(history.pushState) && initialRequestMethod() === 'get'; }); isIE8OrWorse = u.memoize(function() { return u.isUndefined(document.addEventListener); }); isIE9OrWorse = u.memoize(function() { return isIE8OrWorse() || navigator.appVersion.indexOf('MSIE 9.') !== -1; }); canCssAnimation = u.memoize(function() { return 'transition' in document.documentElement.style; }); canInputEvent = u.memoize(function() { return 'oninput' in document.createElement('input'); }); canLogSubstitution = u.memoize(function() { return !isIE9OrWorse(); }); isRecentJQuery = u.memoize(function() { var major, minor, parts, version; version = $.fn.jquery; parts = version.split('.'); major = parseInt(parts[0]); minor = parseInt(parts[1]); return major >= 2 || (major === 1 && minor >= 9); }); popCookie = function(name) { var ref, value; value = (ref = document.cookie.match(new RegExp(name + "=(\\w+)"))) != null ? ref[1] : void 0; if (u.isPresent(value)) { document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'; } return value; }; initialRequestMethod = u.memoize(function() { return (popCookie('_up_request_method') || 'get').toLowerCase(); }); /** Returns whether Up.js supports the current browser. Currently Up.js supports IE9 with jQuery 1.9+. On older browsers Up.js will prevent itself from [booting](/up.boot) and ignores all registered [event handlers](/up.on) and [compilers](/up.compiler). This leaves you with a classic server-side application. @function up.browser.isSupported */ isSupported = function() { return (!isIE8OrWorse()) && isRecentJQuery(); }; return { url: url, loadPage: loadPage, canPushState: canPushState, canCssAnimation: canCssAnimation, canInputEvent: canInputEvent, canLogSubstitution: canLogSubstitution, isSupported: isSupported, puts: puts }; })(jQuery); }).call(this); /** Events ====== Up.js has a convenient way to [listen to DOM events](/up.on): up.on('click', 'button', function(event, $button) { // $button is a jQuery collection containing // the clicked