From e5d077d5e607fe54f78c0693ec616164f5fe0cdb Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Mon, 4 Mar 2019 12:19:09 +0100 Subject: [PATCH 01/78] [ADD] web: es6-promise polyfill lib --- .../lib/es6-promise/es6-promise-polyfill.js | 1176 +++++++++++++++++ addons/web/views/webclient_templates.xml | 1 + 2 files changed, 1177 insertions(+) create mode 100644 addons/web/static/lib/es6-promise/es6-promise-polyfill.js diff --git a/addons/web/static/lib/es6-promise/es6-promise-polyfill.js b/addons/web/static/lib/es6-promise/es6-promise-polyfill.js new file mode 100644 index 00000000000..527ac8bdd57 --- /dev/null +++ b/addons/web/static/lib/es6-promise/es6-promise-polyfill.js @@ -0,0 +1,1176 @@ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.5+7f2b526d + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.ES6Promise = factory()); +}(this, (function () { + 'use strict'; + + function objectOrFunction(x) { + var type = typeof x; + return x !== null && (type === 'object' || type === 'function'); + } + + function isFunction(x) { + return typeof x === 'function'; + } + + + + var _isArray = void 0; + if (Array.isArray) { + _isArray = Array.isArray; + } else { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } + + var isArray = _isArray; + + var len = 0; + var vertxNext = void 0; + var customSchedulerFn = void 0; + + var asap = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (customSchedulerFn) { + customSchedulerFn(flush); + } else { + scheduleFlush(); + } + } + }; + + function setScheduler(scheduleFn) { + customSchedulerFn = scheduleFn; + } + + function setAsap(asapFn) { + asap = asapFn; + } + + var browserWindow = typeof window !== 'undefined' ? window : undefined; + var browserGlobal = browserWindow || {}; + var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; + var isNode = false; + + // test for web worker but not in IE10 + var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; + + // node + function useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function () { + return process.nextTick(flush); + }; + } + + // vertx + function useVertxTimer() { + if (typeof vertxNext !== 'undefined') { + return function () { + vertxNext(flush); + }; + } + + return useSetTimeout(); + } + + function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function () { + node.data = iterations = ++iterations % 2; + }; + } + + // web worker + function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + return channel.port2.postMessage(0); + }; + } + + function useSetTimeout() { + // Store setTimeout reference so es6-promise will be unaffected by + // other code modifying setTimeout (like sinon.useFakeTimers()) + var globalSetTimeout = setTimeout; + return function () { + return globalSetTimeout(flush, 1); + }; + } + + var queue = new Array(1000); + function flush() { + for (var i = 0; i < len; i += 2) { + var callback = queue[i]; + var arg = queue[i + 1]; + + callback(arg); + + queue[i] = undefined; + queue[i + 1] = undefined; + } + + len = 0; + } + + function attemptVertx() { + try { + var vertx = Function('return this')().require('vertx'); + vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch (e) { + return useSetTimeout(); + } + } + + var scheduleFlush = void 0; + // Decide what async method to use to triggering processing of queued callbacks: + if (isNode) { + scheduleFlush = useNextTick(); + } else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); + } else if (isWorker) { + scheduleFlush = useMessageChannel(); + } else if (browserWindow === undefined && typeof require === 'function') { + scheduleFlush = attemptVertx(); + } else { + scheduleFlush = useSetTimeout(); + } + + function then(onFulfillment, onRejection) { + var parent = this; + + var child = new this.constructor(noop); + + if (child[PROMISE_ID] === undefined) { + makePromise(child); + } + + var _state = parent._state; + + + if (_state) { + var callback = arguments[_state - 1]; + asap(function () { + return invokeCallback(_state, child, callback, parent._result); + }); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } + + return child; + } + + /** + `Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` + */ + function resolve$1(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(noop); + resolve(promise, object); + return promise; + } + + var PROMISE_ID = Math.random().toString(36).substring(2); + + function noop() { } + + var PENDING = void 0; + var FULFILLED = 1; + var REJECTED = 2; + + var TRY_CATCH_ERROR = { error: null }; + + function selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } + + function cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } + + function getThen(promise) { + try { + return promise.then; + } catch (error) { + TRY_CATCH_ERROR.error = error; + return TRY_CATCH_ERROR; + } + } + + function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) { + try { + then$$1.call(value, fulfillmentHandler, rejectionHandler); + } catch (e) { + return e; + } + } + + function handleForeignThenable(promise, thenable, then$$1) { + asap(function (promise) { + var sealed = false; + var error = tryThen(then$$1, thenable, function (value) { + if (sealed) { + return; + } + sealed = true; + if (thenable !== value) { + resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function (reason) { + if (sealed) { + return; + } + sealed = true; + + reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + reject(promise, error); + } + }, promise); + } + + function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (thenable._state === REJECTED) { + reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function (value) { + return resolve(promise, value); + }, function (reason) { + return reject(promise, reason); + }); + } + } + + function handleMaybeThenable(promise, maybeThenable, then$$1) { + if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) { + handleOwnThenable(promise, maybeThenable); + } else { + if (then$$1 === TRY_CATCH_ERROR) { + reject(promise, TRY_CATCH_ERROR.error); + TRY_CATCH_ERROR.error = null; + } else if (then$$1 === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then$$1)) { + handleForeignThenable(promise, maybeThenable, then$$1); + } else { + fulfill(promise, maybeThenable); + } + } + } + + function resolve(promise, value) { + if (promise === value) { + reject(promise, selfFulfillment()); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value, getThen(value)); + } else { + fulfill(promise, value); + } + } + + function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + publish(promise); + } + + function fulfill(promise, value) { + if (promise._state !== PENDING) { + return; + } + + promise._result = value; + promise._state = FULFILLED; + + if (promise._subscribers.length !== 0) { + asap(publish, promise); + } + } + + function reject(promise, reason) { + if (promise._state !== PENDING) { + return; + } + promise._state = REJECTED; + promise._result = reason; + + asap(publishRejection, promise); + } + + function subscribe(parent, child, onFulfillment, onRejection) { + var _subscribers = parent._subscribers; + var length = _subscribers.length; + + + parent._onerror = null; + + _subscribers[length] = child; + _subscribers[length + FULFILLED] = onFulfillment; + _subscribers[length + REJECTED] = onRejection; + + if (length === 0 && parent._state) { + asap(publish, parent); + } + } + + function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { + return; + } + + var child = void 0, + callback = void 0, + detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; + } + + function tryCatch(callback, detail) { + try { + return callback(detail); + } catch (e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } + } + + function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value = void 0, + error = void 0, + succeeded = void 0, + failed = void 0; + + if (hasCallback) { + value = tryCatch(callback, detail); + + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value.error = null; + } else { + succeeded = true; + } + + if (promise === value) { + reject(promise, cannotReturnOwn()); + return; + } + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + reject(promise, value); + } + } + + function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value) { + resolve(promise, value); + }, function rejectPromise(reason) { + reject(promise, reason); + }); + } catch (e) { + reject(promise, e); + } + } + + var id = 0; + function nextId() { + return id++; + } + + function makePromise(promise) { + promise[PROMISE_ID] = id++; + promise._state = undefined; + promise._result = undefined; + promise._subscribers = []; + } + + function validationError() { + return new Error('Array Methods must be provided an Array'); + } + + var Enumerator = function () { + function Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop); + + if (!this.promise[PROMISE_ID]) { + makePromise(this.promise); + } + + if (isArray(input)) { + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(input); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + reject(this.promise, validationError()); + } + } + + Enumerator.prototype._enumerate = function _enumerate(input) { + for (var i = 0; this._state === PENDING && i < input.length; i++) { + this._eachEntry(input[i], i); + } + }; + + Enumerator.prototype._eachEntry = function _eachEntry(entry, i) { + var c = this._instanceConstructor; + var resolve$$1 = c.resolve; + + + if (resolve$$1 === resolve$1) { + var _then = getThen(entry); + + if (_then === then && entry._state !== PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof _then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === Promise$2) { + var promise = new c(noop); + handleMaybeThenable(promise, entry, _then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function (resolve$$1) { + return resolve$$1(entry); + }), i); + } + } else { + this._willSettleAt(resolve$$1(entry), i); + } + }; + + Enumerator.prototype._settledAt = function _settledAt(state, i, value) { + var promise = this.promise; + + + if (promise._state === PENDING) { + this._remaining--; + + if (state === REJECTED) { + reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + fulfill(promise, this._result); + } + }; + + Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) { + var enumerator = this; + + subscribe(promise, undefined, function (value) { + return enumerator._settledAt(FULFILLED, i, value); + }, function (reason) { + return enumerator._settledAt(REJECTED, i, reason); + }); + }; + + return Enumerator; + }(); + + /** + `Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = resolve(2); + let promise3 = resolve(3); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = reject(new Error("2")); + let promise3 = reject(new Error("3")); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static + */ + function all(entries) { + return new Enumerator(this, entries).promise; + } + + /** + `Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` + + `Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @static + @param {Array} promises array of promises to observe + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. + */ + function race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + if (!isArray(entries)) { + return new Constructor(function (_, reject) { + return reject(new TypeError('You must pass an array to race.')); + }); + } else { + return new Constructor(function (resolve, reject) { + var length = entries.length; + for (var i = 0; i < length; i++) { + Constructor.resolve(entries[i]).then(resolve, reject); + } + }); + } + } + + /** + `Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. + */ + function reject$1(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop); + reject(promise, reason); + return promise; + } + + function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } + + function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + let promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + let xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {Function} resolver + Useful for tooling. + @constructor + */ + + var Promise$2 = function () { + function Promise(resolver) { + this[PROMISE_ID] = nextId(); + this._result = this._state = undefined; + this._subscribers = []; + + if (noop !== resolver) { + typeof resolver !== 'function' && needsResolver(); + this instanceof Promise ? initializePromise(this, resolver) : needsNew(); + } + } + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + Chaining + -------- + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + Assimilation + ------------ + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + If the assimliated promise rejects, then the downstream promise will also reject. + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + Simple Example + -------------- + Synchronous Example + ```javascript + let result; + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + Errback Example + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + Promise Example; + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + Advanced Example + -------------- + Synchronous Example + ```javascript + let author, books; + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + Errback Example + ```js + function foundBooks(books) { + } + function failure(reason) { + } + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + Promise Example; + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + + + Promise.prototype.catch = function _catch(onRejection) { + return this.then(null, onRejection); + }; + + /** + `finally` will be invoked regardless of the promise's fate just as native + try/catch/finally behaves + + Synchronous example: + + ```js + findAuthor() { + if (Math.random() > 0.5) { + throw new Error(); + } + return new Author(); + } + + try { + return findAuthor(); // succeed or fail + } catch(error) { + return findOtherAuther(); + } finally { + // always runs + // doesn't affect the return value + } + ``` + + Asynchronous example: + + ```js + findAuthor().catch(function(reason){ + return findOtherAuther(); + }).finally(function(){ + // author was either found, or not + }); + ``` + + @method finally + @param {Function} callback + @return {Promise} + */ + + + Promise.prototype.finally = function _finally(callback) { + var promise = this; + var constructor = promise.constructor; + + if (isFunction(callback)) { + return promise.then(function (value) { + return constructor.resolve(callback()).then(function () { + return value; + }); + }, function (reason) { + return constructor.resolve(callback()).then(function () { + throw reason; + }); + }); + } + + return promise.then(callback, callback); + }; + + return Promise; + }(); + + Promise$2.prototype.then = then; + Promise$2.all = all; + Promise$2.race = race; + Promise$2.resolve = resolve$1; + Promise$2.reject = reject$1; + Promise$2._setScheduler = setScheduler; + Promise$2._setAsap = setAsap; + Promise$2._asap = asap; + + /*global self*/ + function polyfill() { + var local = void 0; + + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + + + var P = local.Promise; + + if (P) { + var promiseToString = null; + try { + promiseToString = Object.prototype.toString.call(P.resolve()); + } catch (e) { + // silently ignored + } + + if (promiseToString === '[object Promise]' && !P.cast) { + return; + } + } + + local.Promise = Promise$2; + } + + // Strange compat.. + Promise$2.polyfill = polyfill; + Promise$2.Promise = Promise$2; + + Promise$2.polyfill(); + + return Promise$2; + +}))); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index ccf02f5c743..897b2d20d32 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -67,6 +67,7 @@ + From bb004e3d7f3df5a5af6b2e994ec0af79e2da890f Mon Sep 17 00:00:00 2001 From: David Monjoie Date: Fri, 1 Mar 2019 15:50:29 +0100 Subject: [PATCH 02/78] [REF] web: update jquery to v3.3.1 Part of task 1896658 --- addons/web/static/lib/jquery/jquery.js | 17842 ++++++++++++----------- 1 file changed, 8949 insertions(+), 8893 deletions(-) diff --git a/addons/web/static/lib/jquery/jquery.js b/addons/web/static/lib/jquery/jquery.js index e237564d01b..01b858f1bf7 100644 --- a/addons/web/static/lib/jquery/jquery.js +++ b/addons/web/static/lib/jquery/jquery.js @@ -1,57 +1,63 @@ /*! - * jQuery JavaScript Library v1.11.1 - * http://jquery.com/ + * jQuery JavaScript Library v3.3.1 + * https://jquery.com/ * * Includes Sizzle.js - * http://sizzlejs.com/ + * https://sizzlejs.com/ * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Copyright JS Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://jquery.org/license * - * Date: 2014-05-01T17:42Z + * Date: 2018-01-20T17:24Z */ +( function( global, factory ) { -(function( global, factory ) { + "use strict"; - if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } // Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { -// Can't do this because several apps including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; -var deletedIds = []; +var arr = []; -var slice = deletedIds.slice; +var document = window.document; -var concat = deletedIds.concat; +var getProto = Object.getPrototypeOf; -var push = deletedIds.push; +var slice = arr.slice; -var indexOf = deletedIds.indexOf; +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; var class2type = {}; @@ -59,844 +65,804 @@ var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + var support = {}; +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + noModule: true + }; + + function DOMEval( code, doc, node ) { + doc = doc || document; + + var i, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + if ( node[ i ] ) { + script[ i ] = node[ i ]; + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + var - version = "1.11.1", + version = "3.3.1", - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, + // Define a local copy of jQuery + jQuery = function( selector, context ) { - // Support: Android<4.1, IE<9 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - constructor: jQuery, + // The current version of jQuery being used + jquery: version, - // Start with an empty selector - selector: "", + constructor: jQuery, - // The default length of a jQuery object is 0 - length: 0, + // The default length of a jQuery object is 0 + length: 0, - toArray: function() { - return slice.call( this ); - }, + toArray: function() { + return slice.call( this ); + }, - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num != null ? + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } - // Return all the elements in a clean array - slice.call( this ); - }, + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; + // Add the old object onto the stack (as a reference) + ret.prevObject = this; - // Return the newly-formed element set - return ret; - }, + // Return the newly-formed element set + return ret; + }, - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, - first: function() { - return this.eq( 0 ); - }, + first: function() { + return this.eq( 0 ); + }, - last: function() { - return this.eq( -1 ); - }, + last: function() { + return this.eq( -1 ); + }, - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, - end: function() { - return this.prevObject || this.constructor(null); - }, + end: function() { + return this.prevObject || this.constructor(); + }, - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: deletedIds.sort, - splice: deletedIds.splice + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { - var src, copyIsArray, copy, name, options, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; - // skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } - // extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; + for ( ; i < length; i++ ) { - // Prevent never-ending loop - if ( target === copy ) { - continue; - } + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } + // Prevent never-ending loop + if ( target === copy ) { + continue; + } - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; - // Return the modified object - return target; + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; }; -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), +jQuery.extend( { - // Assume jQuery is ready without the ready module - isReady: true, + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - error: function( msg ) { - throw new Error( msg ); - }, + // Assume jQuery is ready without the ready module + isReady: true, - noop: function() {}, + error: function( msg ) { + throw new Error( msg ); + }, - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, + noop: function() {}, - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, + isPlainObject: function( obj ) { + var proto, Ctor; - isWindow: function( obj ) { - /* jshint eqeqeq: false */ - return obj != null && obj == obj.window; - }, + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } - isNumeric: function( obj ) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; - }, + proto = getProto( obj ); - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } - isPlainObject: function( obj ) { - var key; + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } + isEmptyObject: function( obj ) { - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; - // Support: IE<9 - // Handle iteration over inherited properties before own properties. - if ( support.ownLast ) { - for ( key in obj ) { - return hasOwn.call( obj, key ); - } - } + for ( name in obj ) { + return false; + } + return true; + }, - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - for ( key in obj ) {} + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, - return key === undefined || hasOwn.call( obj, key ); - }, + each: function( obj, callback ) { + var length, i = 0; - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && jQuery.trim( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, + return obj; + }, - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); + return ret; + }, - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, - if ( value === false ) { - break; - } - } - } + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); + first.length = i; - if ( value === false ) { - break; - } - } - } - } + return first; + }, - return obj; - }, + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; - // Support: Android<4.1, IE<9 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; + return matches; + }, - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; - return ret; - }, + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); - inArray: function( elem, arr, i ) { - var len; + if ( value != null ) { + ret.push( value ); + } + } - if ( arr ) { - if ( indexOf ) { - return indexOf.call( arr, elem, i ); - } + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + if ( value != null ) { + ret.push( value ); + } + } + } - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } + // Flatten any nested arrays + return concat.apply( [], ret ); + }, - return -1; - }, + // A global GUID counter for objects + guid: 1, - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); - while ( j < len ) { - first[ i++ ] = second[ j++ ]; - } - - // Support: IE<9 - // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) - if ( len !== len ) { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var args, proxy, tmp; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: function() { - return +( new Date() ); - }, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} // Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); +function isArrayLike( obj ) { - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); - if ( obj.nodeType === 1 && length ) { - return true; - } + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! - * Sizzle CSS Selector Engine v1.10.19 - * http://sizzlejs.com/ + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ * - * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2014-04-18 + * Date: 2016-08-08 */ (function( window ) { var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + // Regular expressions - // Regular expressions + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", - pseudos = ":(" + characterEncoding + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, + rnative = /^[^{]+\{\s*\[native \w/, - rnative = /^[^{]+\{\s*\[native \w/, + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + rsibling = /[+~]/, - rsibling = /[+~]/, - rescape = /'|\\/g, + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); // Optimize for push.apply( _, NodeList ) try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { - push = { apply: arr.length ? + push = { apply: arr.length ? - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; } function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; - context = context || document; - results = results || []; + results = results || []; - if ( !selector || typeof selector !== "string" ) { - return results; - } + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } + return results; + } - if ( documentIsHTML && !seed ) { + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; + if ( documentIsHTML ) { - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; + // ID selector + if ( (m = match[1]) ) { - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } + // Element context + } else { - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry */ function createCache() { - var keys = []; + var keys = []; - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; } /** @@ -904,29 +870,29 @@ function createCache() { * @param {Function} fn The function to mark */ function markFunction( fn ) { - fn[ expando ] = true; - return fn; + fn[ expando ] = true; + return fn; } /** * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result + * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { - var div = document.createElement("div"); + var el = document.createElement("fieldset"); - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } } /** @@ -935,12 +901,12 @@ function assert( fn ) { * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; + var arr = attrs.split("|"), + i = arr.length; - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } } /** @@ -950,26 +916,25 @@ function addHandle( attrs, handler ) { * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } - return a ? 1 : -1; + return a ? 1 : -1; } /** @@ -977,10 +942,10 @@ function siblingCheck( a, b ) { * @param {String} type */ function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; } /** @@ -988,10 +953,66 @@ function createInputPseudo( type ) { * @param {String} type */ function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; } /** @@ -999,21 +1020,21 @@ function createButtonPseudo( type ) { * @param {Function} fn */ function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); } /** @@ -1022,7 +1043,7 @@ function createPositionalPseudo( fn ) { * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; + return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience @@ -1034,10 +1055,10 @@ support = Sizzle.support = {}; * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** @@ -1046,431 +1067,478 @@ isXML = Sizzle.isXML = function( elem ) { * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } - // Set our document - document = doc; - docElem = doc.documentElement; + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); - // Support tests - documentIsHTML = !isXML( doc ); + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer - if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); - /* Attributes - ---------------------------------------------------------------------- */ + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); + /* Attributes + ---------------------------------------------------------------------- */ - /* getElement(s)By* - ---------------------------------------------------------------------- */ + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); + /* getElement(s)By* + ---------------------------------------------------------------------- */ - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
"; + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); + if ( elem ) { - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } - return tmp; - } - return results; - }; + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; + return []; + } + }; + } - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); - // QSA and matchesSelector support + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; + return tmp; + } + return results; + }; - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); + // QSA and matchesSelector support - assert(function( div ) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); + assert(function( el ) { + el.innerHTML = "" + + ""; - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); - /* Sorting - ---------------------------------------------------------------------- */ + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { - // Otherwise we know they are disconnected - 1; + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } - // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; + /* Sorting + ---------------------------------------------------------------------- */ - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : + // Otherwise we know they are disconnected + 1; - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - return doc; + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; }; Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); + return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - try { - var ret = matches.call( elem, expr ); + try { + var ret = matches.call( elem, expr ); - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } - return Sizzle( expr, document, null, [ elem ] ).length > 0; + return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); }; Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); + throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** @@ -1478,32 +1546,32 @@ Sizzle.error = function( msg ) { * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; + var elem, + duplicates = [], + j = 0, + i = 0; - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; - return results; + return results; }; /** @@ -1511,510 +1579,548 @@ Sizzle.uniqueSort = function( results ) { * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes - return ret; + return ret; }; Expr = Sizzle.selectors = { - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); + Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); + Expr.pseudos[ i ] = createButtonPseudo( i ); } // Easy API for creating new setFilters @@ -2023,454 +2129,480 @@ setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } - soFar = selector; - groups = []; - preFilters = Expr.preFilter; + soFar = selector; + groups = []; + preFilters = Expr.preFilter; - while ( soFar ) { + while ( soFar ) { - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } - matched = false; + matched = false; - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } - if ( !matched ) { - break; - } - } + if ( !matched ) { + break; + } + } - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); }; function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; } function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; } function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; } function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; } function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } - return newUnmatched; + return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - // ...intermediate processing is necessary - [] : + // ...intermediate processing is necessary + [] : - // ...otherwise use results directly - results : - matcherIn; + // ...otherwise use results directly + results : + matcherIn; - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - seed[temp] = !(results[temp] = elem); - } - } - } + seed[temp] = !(results[temp] = elem); + } + } + } - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); } function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } - return elementMatcher( matchers ); + return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; - if ( outermost ) { - outermostContext = context !== document && context; - } + if ( outermost ) { + outermostContext = context === document || context || outermost; + } - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } - // Add matches to results - push.apply( results, setMatched ); + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { + // Add matches to results + push.apply( results, setMatched ); - Sizzle.uniqueSort( results ); - } - } + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } + Sizzle.uniqueSort( results ); + } + } - return unmatched; - }; + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } - return bySet ? - markFunction( superMatcher ) : - superMatcher; + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - // Save selector and tokenization - cached.selector = selector; - } - return cached; + // Save selector and tokenization + cached.selector = selector; + } + return cached; }; /** @@ -2483,73 +2615,73 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); - results = results || []; + results = results || []; - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } - selector = selector.slice( tokens.shift().value.length ); - } + selector = selector.slice( tokens.shift().value.length ); + } - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } - break; - } - } - } - } + break; + } + } + } + } - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; }; // One-time assignments @@ -2557,7 +2689,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; -// Support: Chrome<14 +// Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; @@ -2566,53 +2698,53 @@ setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; }) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; }) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; }) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); } return Sizzle; @@ -2623,111 +2755,145 @@ return Sizzle; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + var rneedsContext = jQuery.expr.match.needsContext; -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + -var risSimple = /^.[^:#\[\.,]*$/; // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } - } + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } - } - - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; - }); + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); } jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; + var elem = elems[ 0 ]; - if ( not ) { - expr = ":not(" + expr + ")"; - } + if ( not ) { + expr = ":not(" + expr + ")"; + } - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); }; -jQuery.fn.extend({ - find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } + ret = this.pushStack( [] ); - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - is: function( selector ) { - return !!winnow( - this, + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -}); + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); // Initialize a jQuery object @@ -2736,118 +2902,110 @@ jQuery.fn.extend({ // A central reference to the root jQuery(document) var rootjQuery, - // Use the correct document accordingly with window argument (sandbox) - document = window.document, + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; - init = jQuery.fn.init = function( selector, context ) { - var match, elem; + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { - } else { - match = rquickExpr.exec( selector ); - } + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { + } else { + match = rquickExpr.exec( selector ); + } - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { - // scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { - return this; + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } + return this; - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); - this.context = document; - this.selector = selector; - return this; - } + if ( elem ) { - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : - // Execute immediately if ready is not present - selector( jQuery ); - } + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : - return jQuery.makeArray( selector, this ); - }; + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; @@ -2857,1413 +3015,1929 @@ rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; - sibling: function( n, elem ) { - var r = []; + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); - return r; - } -}); + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { -jQuery.fn.extend({ - has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; + matched.push( cur ); + break; + } + } + } + } - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { + // Determine the position of an element within the set + index: function( elem ) { - matched.push( cur ); - break; - } - } - } + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { + // Locate the position of the desired element + return indexOf.call( this, - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; - } + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && cur.nodeType !== 1 ); - - return cur; + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; } -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } }, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - ret = jQuery.unique( ret ); - } + if ( this.length > 1 ) { - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - } + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } - return this.pushStack( ret ); - }; -}); -var rnotwhite = (/\S+/g); + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache +// Convert String-formatted options into Object-formatted ones function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; } /* * Create a callback list using the following parameters: * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * - * once: will ensure the callback list can only be fired once (like a Deferred) + * once: will ensure the callback list can only be fired once (like a Deferred) * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) * - * unique: will ensure a callback can only be added once (no duplicate in the list) + * unique: will ensure a callback can only be added once (no duplicate in the list) * - * stopOnFalse: interrupt callings when a callback returns false + * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); - var // Flag to know if list is currently firing - firing, - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // First callback to fire (used internally by add and fireWith) - firingStart, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; + var // Flag to know if list is currently firing + firing, - return self; + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; }; -jQuery.extend({ +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; +function adoptValue( value, resolve, reject, noValue ) { + var method; - // Keep pipe for back-compat - promise.pipe = promise.then; + try { - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; + // Other non-thenables + } else { - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { - // Make the deferred a promise - promise.promise( deferred ); + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } +jQuery.extend( { - // All done! - return deferred; - }, + Deferred: function( func ) { + var tuples = [ - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - } else if ( !(--remaining) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; - progressValues, progressContexts, resolveContexts; + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - return deferred.promise(); - } -}); // The deferred used on DOM ready -var readyList; +var readyList = jQuery.Deferred(); jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - return this; + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; }; -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, +jQuery.extend( { - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, - // Handle when the DOM is ready - ready: function( wait ) { + // Handle when the DOM is ready + ready: function( wait ) { - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } + // Remember that the DOM is ready + jQuery.isReady = true; - // Remember that the DOM is ready - jQuery.isReady = true; + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); +jQuery.ready.then = readyList.then; - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); - -/** - * Clean-up method for dom ready events - */ -function detach() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - - } else { - document.detachEvent( "onreadystatechange", completed ); - window.detachEvent( "onload", completed ); - } -} - -/** - * The ready event handler and self cleanup method - */ +// The ready event handler and self cleanup method function completed() { - // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { - detach(); - jQuery.ready(); - } + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); } -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - readyList = jQuery.Deferred(); + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); +} else { - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", completed ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", completed ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // detach all dom ready events - detach(); - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; - - -var strundefined = typeof undefined; - - - -// Support: IE<9 -// Iteration over object's inherited properties before its own -var i; -for ( i in jQuery( support ) ) { - break; -} -support.ownLast = i !== "0"; - -// Note: most support tests are defined in their respective modules. -// false until the test is run -support.inlineBlockNeedsLayout = false; - -// Execute ASAP in case we need to set body.style.zoom -jQuery(function() { - // Minified: var a,b,c,d - var val, div, body, container; - - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - // Return for frameset docs that don't have a body - return; - } - - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); - - if ( typeof div.style.zoom !== strundefined ) { - // Support: IE<8 - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; - - support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; - if ( val ) { - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - // Support: IE<8 - body.style.zoom = 1; - } - } - - body.removeChild( container ); -}); - - - - -(function() { - var div = document.createElement( "div" ); - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } - - // Null elements to avoid leaks in IE. - div = null; -})(); - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( elem ) { - var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], - nodeType = +elem.nodeType || 1; - - // Do not set data on non-element DOM nodes because it will not be cleared (#8335). - return nodeType !== 1 && nodeType !== 9 ? - false : - - // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute("classid") === noData; -}; - - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); } -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - -function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var ret, thisCache, - internalKey = jQuery.expando, - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - // Avoid exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( typeof name === "string" ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; -} - -function internalRemoveData( elem, name, pvt ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } else { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = name.concat( jQuery.map( name, jQuery.camelCase ) ); - } - - i = name.length; - while ( i-- ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - /* jshint eqeqeq: false */ - } else if ( support.deleteExpando || cache != cache.window ) { - /* jshint eqeqeq: true */ - delete cache[ id ]; - - // When all else fails, null - } else { - cache[ id ] = null; - } -} - -jQuery.extend({ - cache: {}, - - // The following elements (space-suffixed to avoid Object.prototype collisions) - // throw uncatchable exceptions if you attempt to set expando properties - noData: { - "applet ": true, - "embed ": true, - // ...but Flash objects (which have this classid) *can* handle expandos - "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data ) { - return internalData( elem, name, data ); - }, - - removeData: function( elem, name ) { - return internalRemoveData( elem, name ); - }, - - // For internal use only. - _data: function( elem, name, data ) { - return internalData( elem, name, data, true ); - }, - - _removeData: function( elem, name ) { - return internalRemoveData( elem, name, true ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[0], - attrs = elem && elem.attributes; - - // Special expections of .data basically thwart jQuery.access, - // so implement the relevant behavior ourselves - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE11+ - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - return arguments.length > 1 ? - - // Sets one value - this.each(function() { - jQuery.data( this, key, value ); - }) : - - // Gets one value - // Try to fetch any internally stored data first - elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - - -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHidden = function( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); - }; // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } - // Sets one value - } else if ( value !== undefined ) { - chainable = true; + // Sets one value + } else if ( value !== undefined ) { + chainable = true; - if ( !jQuery.isFunction( value ) ) { - raw = true; - } + if ( !isFunction( value ) ) { + raw = true; + } - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; + if ( bulk ) { - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } - return chainable ? - elems : + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; -var rcheckableType = (/^(?:checkbox|radio)$/i); -(function() { - // Minified: var a,b,c - var input = document.createElement( "input" ), - div = document.createElement( "div" ), - fragment = document.createDocumentFragment(); - // Setup - div.innerHTML = "
a"; +function Data() { + this.expando = jQuery.expando + Data.uid++; +} - // IE strips leading whitespace when .innerHTML is used - support.leadingWhitespace = div.firstChild.nodeType === 3; +Data.uid = 1; - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - support.tbody = !div.getElementsByTagName( "tbody" ).length; +Data.prototype = { - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + cache: function( owner ) { - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - support.html5Clone = - document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + // Check if the owner object already has a cache + var value = owner[ this.expando ]; - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - input.type = "checkbox"; - input.checked = true; - fragment.appendChild( input ); - support.appendChecked = input.checked; + // If not, create one + if ( !value ) { + value = {}; - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE6-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { - // #11217 - WebKit loses check when the name is after the checked attribute - fragment.appendChild( div ); - div.innerHTML = ""; + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } - // Support: IE<9 - // Opera does not clone events (and typeof div.attachEvent === undefined). - // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() - support.noCloneEvent = true; - if ( div.attachEvent ) { - div.attachEvent( "onclick", function() { - support.noCloneEvent = false; - }); + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); - div.cloneNode( true ).click(); - } + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } -})(); + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); -(function() { - var i, eventName, - div = document.createElement( "div" ); - // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) - for ( i in { submit: true, change: true, focusin: true }) { - eventName = "on" + i; +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - if ( !(support[ i + "Bubbles" ] = eventName in window) ) { - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - div.setAttribute( eventName, "t" ); - support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; - } - } +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; - // Null elements to avoid leaks in IE. - div = null; -})(); +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); -var rformElems = /^(?:input|select|textarea)$/i, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { - return true; + return true; } function returnFalse() { - return false; + return false; } +// Support: IE <=9 only +// See #13393 for more info function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); } /* @@ -4272,4159 +4946,3201 @@ function safeActiveElement() { */ jQuery.event = { - global: {}, - - add: function( elem, types, handler, data, selector ) { - var tmp, events, t, handleObjIn, - special, eventHandle, handleObj, - handlers, type, namespaces, origType, - elemData = jQuery._data( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - var j, handleObj, tmp, - origCount, t, events, - special, handlers, type, - namespaces, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - var handle, ontype, cur, - bubbleType, special, tmp, i, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - try { - elem[ type ](); - } catch ( e ) { - // IE<9 dies on focus/blur to hidden element (#1486,#12518) - // only reproducible on winXP IE8 native, not IE9 in IE8 mode - } - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, ret, handleObj, matched, j, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var sel, handleObj, matches, i, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - /* jshint eqeqeq: false */ - for ( ; cur != this; cur = cur.parentNode || this ) { - /* jshint eqeqeq: true */ - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: IE<9 - // Fix target property (#1925) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Support: Chrome 23+, Safari? - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Support: IE<9 - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) - event.metaKey = !!event.metaKey; - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var body, eventDoc, doc, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - try { - this.focus(); - return false; - } catch ( e ) { - // Support: IE<9 - // If we error on focus to hidden element (#1486, #12518), - // let .trigger() run the handlers - } - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } }; -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; +jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === strundefined ) { - elem[ name ] = null; - } - - elem.detachEvent( name, handle ); - } - }; + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - // Support: IE < 9, Android < 4.0 - src.returnValue === false ? - returnTrue : - returnFalse; + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; - // Event type - } else { - this.type = src; - } + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; - // Mark it as fixed - this[ jQuery.expando ] = true; + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, - preventDefault: function() { - var e = this.originalEvent; + preventDefault: function() { + var e = this.originalEvent; - this.isDefaultPrevented = returnTrue; - if ( !e ) { - return; - } + this.isDefaultPrevented = returnTrue; - // If preventDefault exists, run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; - // Support: IE - // Otherwise set the returnValue property of the original event to false - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - var e = this.originalEvent; + this.isPropagationStopped = returnTrue; - this.isPropagationStopped = returnTrue; - if ( !e ) { - return; - } - // If stopPropagation exists, run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; - // Support: IE - // Set the cancelBubble property of the original event to true - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; + this.isImmediatePropagationStopped = returnTrue; - this.isImmediatePropagationStopped = returnTrue; + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } + this.stopPropagation(); + } }; +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + // Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" }, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); -// IE submit delegation -if ( !support.submitBubbles ) { +jQuery.fn.extend( { - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "submitBubbles" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "submitBubbles", true ); - } - }); - // return undefined since we don't need an event listener - }, + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "changeBubbles", true ); - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - jQuery._removeData( doc, fix ); - } else { - jQuery._data( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var type, origFn; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); +var - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} + /* eslint-disable max-len */ -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /\s*$/g, + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, - // We have to close these tags to support XHTML (#13200) - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
", "
" ], - area: [ 1, "", "" ], - param: [ 1, "", "" ], - thead: [ 1, "", "
" ], - tr: [ 2, "", "
" ], - col: [ 2, "", "
" ], - td: [ 3, "", "
" ], + /* eslint-enable */ - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; -function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : - undefined; - - if ( !found ) { - for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); - } - } - } - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; -} - -// Used in buildFragment, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} - -// Support: IE<8 -// Manipulating tables requires a tbody +// Prefer a tbody over its parent table for containing new rows function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { - elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; - return elem; + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; } function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - if ( match ) { - elem.type = match[1]; - } else { - elem.removeAttribute("type"); - } - return elem; -} + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; (elem = elems[i]) != null; i++ ) { - jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); - } + return elem; } function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } + if ( dest.nodeType !== 1 ) { + return; + } - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; - if ( events ) { - delete curData.handle; - curData.events = {}; + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } } -function fixCloneNodeIssues( src, dest ) { - var nodeName, e, data; +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - nodeName = dest.nodeName.toLowerCase(); - - // IE6-8 copies events bound via attachEvent when using cloneNode. - if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { - data = jQuery._data( dest ); - - for ( e in data.events ) { - jQuery.removeEvent( dest, e, data.handle ); - } - - // Event data gets referenced instead of copied if the expando gets copied too - dest.removeAttribute( jQuery.expando ); - } - - // IE blanks contents when cloning scripts, and tries to evaluate newly-set text - if ( nodeName === "script" && dest.text !== src.text ) { - disableScript( dest ).text = src.text; - restoreScript( dest ); - - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - } else if ( nodeName === "object" ) { - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } - - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { - dest.innerHTML = src.innerHTML; - } - - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.defaultSelected = dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } } -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, node, clone, i, srcElements, - inPage = jQuery.contains( elem.ownerDocument, elem ); - - if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); - - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } - - if ( (!support.noCloneEvent || !support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - // Fix all IE cloning issues - for ( i = 0; (node = srcElements[i]) != null; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - fixCloneNodeIssues( node, destElements[i] ); - } - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0; (node = srcElements[i]) != null; i++ ) { - cloneCopyEvent( node, destElements[i] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - destElements = srcElements = node = null; - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var j, elem, contains, - tmp, tag, tbody, wrap, - l = elems.length, - - // Ensure a safe fragment - safe = createSafeFragment( context ), - - nodes = [], - i = 0; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || safe.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; - - // Descend through wrappers to the right content - j = wrap[0]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Manually add leading whitespace removed by IE - if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); - } - - // Remove IE's autoinserted from table fragments - if ( !support.tbody ) { - - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : - - // String was a bare or - wrap[1] === "
" && !rtbody.test( elem ) ? - tmp : - 0; - - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { - elem.removeChild( tbody ); - } - } - } - - jQuery.merge( nodes, tmp.childNodes ); - - // Fix #12392 for WebKit and IE > 9 - tmp.textContent = ""; - - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); - } - - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; - } - } - } - - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); - } - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - tmp = null; - - return safe; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var elem, type, id, data, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = support.deleteExpando, - special = jQuery.event.special; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( acceptData || jQuery.acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { - - delete cache[ id ]; - - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( typeof elem.removeAttribute !== strundefined ) { - elem.removeAttribute( internalKey ); - - } else { - elem[ internalKey ] = null; - } - - deletedIds.push( id ); - } - } - } - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - } - - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - - // If this is a select, ensure that it displays empty (#12336) - // Support: IE<9 - if ( elem.options && jQuery.nodeName( elem, "select" ) ) { - elem.options.length = 0; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for (; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - elem = this[i] || {}; - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch(e) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var arg = arguments[ 0 ]; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; - - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var first, node, hasScripts, - scripts, doc, fragment, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[0], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[0] = value.call( this, index, self.html() ); - } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[i], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - } - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" }, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - i = 0, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1; + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone(true); - jQuery( insert[i] )[ original ]( elems ); + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); - // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - - -var iframe, - elemdisplay = {}; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "


', - check: function () { + check: async function () { assert.strictEqual($('.note-video-popover').css('display'), 'block', testName + ' (popover)'); }, }, @@ -3752,14 +3590,14 @@ QUnit.test('Video', function (assert) { async: true, content: '

tata yoyo

', start: "p:contents()[0]->4", - do: function () { - _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); + do: async function () { + await _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); }, test: { content: '

tata

' + '
 
 
' + '

yoyo

', - check: function () { + check: async function () { assert.strictEqual($('.note-video-popover').css('display'), 'block', testName + ' (popover)'); }, }, @@ -3770,13 +3608,13 @@ QUnit.test('Video', function (assert) { async: true, content: '


', start: "p->0", - do: function () { - _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); + do: async function () { + await _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); }, test: { content: '


', - check: function () { - $('.note-image-popover .note-btn .note-icon-trash').mousedown().click(); + check: async function () { + await testUtils.dom.triggerEvents($('.note-image-popover .note-btn .note-icon-trash'), ['mousedown', 'click']); }, }, }, @@ -3787,14 +3625,14 @@ QUnit.test('Video', function (assert) { async: true, content: '


', start: "p->0", - do: function () { - _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); + do: async function () { + await _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); }, test: { content: '


 
 


', - check: function () { - $('.note-image-popover .note-float .note-icon-align-center').mousedown().click(); - $('.note-image-popover .note-float .note-icon-align-left').mousedown().click(); + check: async function () { + await testUtils.dom.triggerEvents($('.note-image-popover .note-float .note-icon-align-center'), ['mousedown', 'click']); + await testUtils.dom.triggerEvents($('.note-image-popover .note-float .note-icon-align-left'), ['mousedown', 'click']); }, }, }, @@ -3804,11 +3642,11 @@ QUnit.test('Video', function (assert) { async: true, content: '

', start: "img->0", - do: function () { - _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); + do: async function () { + await _insertVideo(wysiwyg, assert, 'https://www.youtube.com/watch?v=xxxxxxxxxxx'); }, test: { - check: function () { + check: async function () { assert.deepEqual(wysiwyg.getValue(), '


 
 


', testName); @@ -3821,11 +3659,11 @@ QUnit.test('Video', function (assert) { async: true, content: '


 
 


', start: "div->0", - do: function () { - _insertPictogram('fa-glass'); + do: async function () { + await _insertPictogram('fa-glass'); }, test: { - check: function () { + check: async function () { assert.deepEqual(wysiwyg.getValue(), '

\u200B\u200B

', testName); @@ -3837,11 +3675,11 @@ QUnit.test('Video', function (assert) { async: true, content: '

aaa

 
 

bbb

', start: "div->0", - do: function () { - _insertPictogram('fa-glass'); + do: async function () { + await _insertPictogram('fa-glass'); }, test: { - check: function () { + check: async function () { assert.deepEqual(wysiwyg.getValue(), '

aaabbb

', testName); @@ -3850,20 +3688,19 @@ QUnit.test('Video', function (assert) { }, ]; - var def = $.when(); + var def = Promise.resolve(); _.each(mediaTests, function (test) { - def = def.then(function () { + def = def.then(async function () { testName = test.name; wysiwyg.setValue(test.content); var range = weTestUtils.select(test.start, test.end, $editable); - $(range.sc).mousedown().click(); + await testUtils.dom.triggerEvents($(range.sc), ['mousedown', 'click']); Wysiwyg.setRange(range.sc, range.so, range.ec, range.eo); return _clickMedia(wysiwyg, assert, test.do, test.test); }); }); - def.then(function () { + return def.then(function () { wysiwyg.destroy(); - done(); }); }); }); From 7bc550fb8d3fba2400db345818de321a08537a39 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 53/78] [REF] web_kanban_gauge: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/web_kanban_gauge/static/tests/gauge_tests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web_kanban_gauge/static/tests/gauge_tests.js b/addons/web_kanban_gauge/static/tests/gauge_tests.js index 55f00d5fda1..3dce14fd59b 100644 --- a/addons/web_kanban_gauge/static/tests/gauge_tests.js +++ b/addons/web_kanban_gauge/static/tests/gauge_tests.js @@ -26,10 +26,10 @@ QUnit.module('basic_fields', { QUnit.module('gauge widget'); - QUnit.test('basic rendering', function (assert) { + QUnit.test('basic rendering', async function (assert) { assert.expect(1); - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, From 8356a00a68ebdc97829d6b4b2896bdcec9a6b67f Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 54/78] [REF] web_settings_dashboard: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/dashboard.js | 16 ++-- .../static/tests/dashboard_tests.js | 90 +++++++++---------- 2 files changed, 49 insertions(+), 57 deletions(-) diff --git a/addons/web_settings_dashboard/static/src/js/dashboard.js b/addons/web_settings_dashboard/static/src/js/dashboard.js index a51b5c173b5..9807a00d001 100644 --- a/addons/web_settings_dashboard/static/src/js/dashboard.js +++ b/addons/web_settings_dashboard/static/src/js/dashboard.js @@ -20,13 +20,13 @@ var Dashboard = AbstractAction.extend({ start: function(){ var superDef = this._super.apply(this, arguments); - return $.when(superDef, this.load(this.all_dashboards)); + return Promise.all([superDef, this.load(this.all_dashboards)]); }, load: function(dashboards){ var self = this; - var loading_done = new $.Deferred(); - this._rpc({route: '/web_settings_dashboard/data'}) + return new Promise(function (resolve, reject) { + self._rpc({route: '/web_settings_dashboard/data'}) .then(function (data) { // Load each dashboard var all_dashboards_defs = []; @@ -37,12 +37,12 @@ var Dashboard = AbstractAction.extend({ } }); - // Resolve loading_done when all dashboards defs are resolved - $.when.apply($, all_dashboards_defs).then(function() { - loading_done.resolve(); + // Resolve the promise when all dashboards defs are resolved + Promise.all(all_dashboards_defs).then(function() { + resolve(); }); }); - return loading_done; + }); }, load_apps: function(data){ @@ -220,7 +220,7 @@ var DashboardInvitations = Widget.extend({ .then(function () { self.reload(); }) - .fail(function () { + .guardedCatch(function () { $button.button('reset'); }); } diff --git a/addons/web_settings_dashboard/static/tests/dashboard_tests.js b/addons/web_settings_dashboard/static/tests/dashboard_tests.js index 46c85126541..8f149521e30 100644 --- a/addons/web_settings_dashboard/static/tests/dashboard_tests.js +++ b/addons/web_settings_dashboard/static/tests/dashboard_tests.js @@ -8,7 +8,7 @@ var Widget = require('web.Widget'); var Dashboard = webSettingsDashboard.Dashboard; -function createDashboard(params) { +async function createDashboard (params) { var widget = new Widget(); // action content not used in tests apparently var action = {}; @@ -23,17 +23,14 @@ function createDashboard(params) { widget.destroy(); }; - if (params.debug) { - dashboard.appendTo($('body')); - } else { - dashboard.appendTo($('#qunit-fixture')); - } + var target = params.debug ? $('body') : $('#qunit-fixture'); + await dashboard.appendTo(target); return dashboard; } QUnit.module('settings_dashboard', function () { - QUnit.test('Dashboard: Invite new user', function (assert) { + QUnit.test('Dashboard: Invite new user', async function (assert) { assert.expect(4); var dashboardData = { @@ -41,10 +38,10 @@ QUnit.module('settings_dashboard', function () { pending_counts: 0, pending_users: [], }; - var dashboard = createDashboard({ + var dashboard = await createDashboard({ mockRPC: function (route, args) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ share: {}, users_info: dashboardData, }); @@ -52,23 +49,22 @@ QUnit.module('settings_dashboard', function () { if (route === '/web/dataset/call_kw/res.users/web_dashboard_create_users') { dashboardData.pending_counts++; dashboardData.pending_users.push([5, args.args[0][0]]); - return $.when(true); + return Promise.resolve(true); } return this._super.apply(this, arguments); }, }); // add email to invite - dashboard.$('.o_user_emails').val('lagan@odoo.com').trigger($.Event('keydown', { - which: $.ui.keyCode.ENTER, - })); + await testUtils.fields.editInput(dashboard.$('.o_user_emails'), 'lagan@odoo.com'); + await testUtils.fields.triggerKeydown(dashboard.$('.o_user_emails'), 'enter'); assert.strictEqual(dashboard.$('.o_badge_text').text().trim(), 'lagan@odoo.com', 'should generate a badge with provided email'); assert.strictEqual(dashboard.$('.o_user_emails').val(), '', 'input should have been cleared'); // send invitation - testUtils.dom.click(dashboard.$('.o_web_settings_dashboard_invite')); + await testUtils.dom.click(dashboard.$('.o_web_settings_dashboard_invite')); assert.strictEqual(dashboard.$('.o_web_settings_dashboard_user').text().trim(), 'lagan@odoo.com', 'should have created a badge in pending invitations'); assert.containsNone(dashboard, '.o_badge_text', @@ -77,13 +73,13 @@ QUnit.module('settings_dashboard', function () { dashboard.destroy(); }); - QUnit.test('Dashboard: Invite new user (warnings)', function (assert) { + QUnit.test('Dashboard: Invite new user (warnings)', async function (assert) { assert.expect(8); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ mockRPC: function (route) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ share: {}, users_info: { active_users: 4, @@ -104,9 +100,8 @@ QUnit.module('settings_dashboard', function () { }); // enter an invalid email address to invite - dashboard.$('.o_user_emails').val('x@y').trigger($.Event('keydown', { - which: $.ui.keyCode.ENTER, - })); + await testUtils.fields.editInput(dashboard.$('.o_user_emails'), 'x@y'); + await testUtils.fields.triggerKeydown(dashboard.$('.o_user_emails'), 'enter'); assert.containsNone(dashboard, '.o_badge_text', 'should not have generated any badge'); assert.strictEqual(dashboard.$('.o_user_emails').val(), 'x@y', @@ -114,25 +109,24 @@ QUnit.module('settings_dashboard', function () { assert.verifySteps(['warning']); // enter an already pending address - dashboard.$('.o_user_emails').val('xyz@odoo.com').trigger($.Event('keydown', { - which: $.ui.keyCode.ENTER, - })); + await testUtils.fields.editInput(dashboard.$('.o_user_emails'), 'xyz@odoo.com'); + await testUtils.fields.triggerKeydown(dashboard.$('.o_user_emails'), 'enter'); assert.containsNone(dashboard, '.o_badge_text', 'should not have generated any badge'); assert.strictEqual(dashboard.$('.o_user_emails').val(), 'xyz@odoo.com', 'input should not have been cleared'); - assert.verifySteps(['warning', 'warning']); + assert.verifySteps(['warning']); dashboard.destroy(); }); - QUnit.test('Dashboard: Invite a list of users', function (assert) { + QUnit.test('Dashboard: Invite a list of users', async function (assert) { assert.expect(2); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ mockRPC: function (route) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ share: {}, users_info: { active_users: 4, @@ -147,9 +141,8 @@ QUnit.module('settings_dashboard', function () { // simulate a copy paste of multiple email addresses var emails = ['a@odoo.com', 'b@odoo.com', 'c@odoo.com', 'd@odoo.com']; - dashboard.$('.o_user_emails').val(emails.join(' ')).trigger($.Event('keydown', { - which: $.ui.keyCode.ENTER, - })); + await testUtils.fields.editInput(dashboard.$('.o_user_emails'), emails.join(' ')); + await testUtils.fields.triggerKeydown(dashboard.$('.o_user_emails'), 'enter'); assert.containsN(dashboard, '.o_badge_text', 4, 'should have generated 4 badges'); assert.strictEqual(dashboard.$('.o_user_emails').val(), '', @@ -158,13 +151,13 @@ QUnit.module('settings_dashboard', function () { dashboard.destroy(); }); - QUnit.test('Dashboard: Invite a list of users (with warnings)', function (assert) { + QUnit.test('Dashboard: Invite a list of users (with warnings)', async function (assert) { assert.expect(5); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ mockRPC: function (route) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ share: {}, users_info: { active_users: 4, @@ -186,9 +179,8 @@ QUnit.module('settings_dashboard', function () { // simulate a copy paste of multiple email addresses var emails = ['a@odoo.com', 'b@odoo.com', 'x@y', 'd@odoo.com']; - dashboard.$('.o_user_emails').val(emails.join(' ')).trigger($.Event('keydown', { - which: $.ui.keyCode.ENTER, - })); + await testUtils.fields.editInput(dashboard.$('.o_user_emails'), emails.join(' ')); + await testUtils.fields.triggerKeydown(dashboard.$('.o_user_emails'), 'enter'); assert.containsOnce(dashboard, '.o_badge_text', 'should have generated 1 badge'); assert.strictEqual(dashboard.$('.o_user_emails').val(), '', @@ -198,14 +190,14 @@ QUnit.module('settings_dashboard', function () { dashboard.destroy(); }); - QUnit.test('Prevent default behaviour when clicking on load translation', function (assert) { + QUnit.test('Prevent default behaviour when clicking on load translation', async function (assert) { assert.expect(3); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ dashboards: ['translations'], mockRPC: function (route, args) { if (route === '/web_settings_dashboard/data') { - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -227,21 +219,21 @@ QUnit.module('settings_dashboard', function () { ev.preventDefault(); }); - testUtils.dom.click($loadTranslation); + await testUtils.dom.click($loadTranslation); $(document.body).off('click.o_test'); dashboard.destroy(); }); - QUnit.test('Prevent default behaviour when clicking on set up company', function (assert) { + QUnit.test('Prevent default behaviour when clicking on set up company', async function (assert) { assert.expect(3); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ dashboards: ['company'], mockRPC: function (route, args) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ company: { company_name: 'MyCompany' } @@ -267,26 +259,26 @@ QUnit.module('settings_dashboard', function () { ev.preventDefault(); }); - testUtils.dom.click($setupCompany); + await testUtils.dom.click($setupCompany); $(document.body).off('click.o_test'); dashboard.destroy(); }); - QUnit.test('Prevent default behaviour when clicking on browse apps', function (assert) { + QUnit.test('Prevent default behaviour when clicking on browse apps', async function (assert) { assert.expect(3); - var dashboard = createDashboard({ + var dashboard = await createDashboard({ dashboards: ['apps'], mockRPC: function (route, args) { if (route === '/web_settings_dashboard/data') { - return $.when({ + return Promise.resolve({ apps: {}, }); } if (args.method === 'get_account_url') { - return $.when('fakeURL'); + return Promise.resolve('fakeURL'); } return this._super.apply(this, arguments); }, @@ -308,7 +300,7 @@ QUnit.module('settings_dashboard', function () { assert.strictEqual($browseAppsButton.length, 1, "should have button to browse apps"); - testUtils.dom.click($browseAppsButton); + await testUtils.dom.click($browseAppsButton); $(document.body).off('click.o_test'); dashboard.destroy(); From 61b59c031f574475a7c4ebbc7bd1c952306aeb7a Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 55/78] [REF] web_tour: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/web_tour/static/src/js/tour_manager.js | 6 +-- addons/web_tour/static/src/js/tour_service.js | 46 +++++++++---------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index e622d3929d7..18b2c47d6b3 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -49,7 +49,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { * the url to load when manually running the tour * @param {boolean} [options.rainbowMan=true] * whether or not the rainbowman must be shown at the end of the tour - * @param {Deferred} [options.wait_for] + * @param {Promise} [options.wait_for] * indicates when the tour can be started * @param {Object[]} steps - steps' descriptions, each step being an object * containing a tip description @@ -70,7 +70,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { url: options.url, rainbowMan: options.rainbowMan === undefined ? true : !!options.rainbowMan, test: options.test, - wait_for: options.wait_for || $.when(), + wait_for: options.wait_for || Promise.resolve(), }; if (options.skip_enabled) { tour.skip_link = '

' + _t('Skip tour') + '

'; @@ -88,7 +88,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { _.each(this.tours, this._register.bind(this, do_update)); }, _register: function (do_update, tour, name) { - if (tour.ready) return $.when(); + if (tour.ready) return Promise.resolve(); var tour_is_consumed = _.contains(this.consumed_tours, name); diff --git a/addons/web_tour/static/src/js/tour_service.js b/addons/web_tour/static/src/js/tour_service.js index b0507d22e43..6f2300853f8 100644 --- a/addons/web_tour/static/src/js/tour_service.js +++ b/addons/web_tour/static/src/js/tour_service.js @@ -1,20 +1,15 @@ odoo.define('web_tour.tour', function (require) { "use strict"; -var ajax = require('web.ajax'); var config = require('web.config'); -var core = require('web.core'); var rootWidget = require('root.widget'); var rpc = require('web.rpc'); var session = require('web.session'); var TourManager = require('web_tour.TourManager'); -var QWeb = core.qweb; - if (config.device.isMobile) { - return $.Deferred().reject(); + return Promise.reject(); } - /** * @namespace * @property {Object} active_tooltips @@ -37,8 +32,8 @@ return session.is_bound.then(function () { }); defs.push(def); } - return $.when.apply($, defs).then(function (consumed_tours) { - consumed_tours = session.is_frontend ? consumed_tours : session.web_tours; + return Promise.all(defs).then(function (results) { + var consumed_tours = session.is_frontend ? results[0] : session.web_tours; var tour_manager = new TourManager(rootWidget, consumed_tours); // Use a MutationObserver to detect DOM changes @@ -64,25 +59,26 @@ return session.is_bound.then(function () { return function (observe) { - var def = $.Deferred(); - $(function () { - /** - * Once the DOM is ready, we still have to wait all the modules are loaded before completing the tours - * registration and starting listening for DOM mutations. - */ - _.defer(function () { - tour_manager._register_all(observe); - if (observe) { - observer.observe(document.body, { - attributes: true, - childList: true, - subtree: true, - }); - } - def.resolve(); + var promise = new Promise(function (resolve, reject) { + $(function () { + /** + * Once the DOM is ready, we still have to wait all the modules are loaded before completing the tours + * registration and starting listening for DOM mutations. + */ + _.defer(function () { + tour_manager._register_all(observe); + if (observe) { + observer.observe(document.body, { + attributes: true, + childList: true, + subtree: true, + }); + } + resolve(); + }); }); }); - return def; + return promise; }; })(); From c39033bc1cff578626b6925ed542cfc8f709e15c Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 56/78] [REF] web_unsplash: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/unsplash_image_widget.js | 14 ++++++++------ addons/web_unsplash/static/src/js/unsplashapi.js | 9 +++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/addons/web_unsplash/static/src/js/unsplash_image_widget.js b/addons/web_unsplash/static/src/js/unsplash_image_widget.js index 61a698ed190..01785fc12d9 100644 --- a/addons/web_unsplash/static/src/js/unsplash_image_widget.js +++ b/addons/web_unsplash/static/src/js/unsplash_image_widget.js @@ -112,18 +112,20 @@ widgetsMedia.ImageWidget.include({ } this._unsplash.query = needle; + + var always = function () { + if (!noRender) { + self._renderImages(); + self._adaptLoadMore(); + } + }; return this.unsplashAPI.getImages(needle, this.IMAGES_DISPLAYED_TOTAL).then(function (res) { self._unsplash.isMaxed = res.isMaxed; self._unsplash.records = res.images; self._unsplash.error = false; }, function (err) { self._unsplash.error = err; - }).always(function () { - if (!noRender) { - self._renderImages(); - self._adaptLoadMore(); - } - }); + }).then(always).guardedCatch(always); }, //-------------------------------------------------------------------------- diff --git a/addons/web_unsplash/static/src/js/unsplashapi.js b/addons/web_unsplash/static/src/js/unsplashapi.js index 3055ec306e8..8156be6c4bc 100644 --- a/addons/web_unsplash/static/src/js/unsplashapi.js +++ b/addons/web_unsplash/static/src/js/unsplashapi.js @@ -27,17 +27,17 @@ var UnsplashCore = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { * * @param {String} query search terms * @param {Integer} pageSize number of image to display per page + * @returns {Promise} */ getImages: function (query, pageSize) { - var self = this; var from = 0; var to = pageSize; var cachedData = this._cache[query]; if (cachedData && (cachedData.images.length >= to || (cachedData.totalImages !== 0 && cachedData.totalImages < to))) { - return $.when({ images: cachedData.images.slice(from, to), isMaxed: to > cachedData.totalImages }); + return Promise.resolve({ images: cachedData.images.slice(from, to), isMaxed: to > cachedData.totalImages }); } - return self._fetchImages(query).then(function (cachedData) { + return this._fetchImages(query).then(function (cachedData) { return { images: cachedData.images.slice(from, to), isMaxed: to > cachedData.totalImages }; }); }, @@ -50,6 +50,7 @@ var UnsplashCore = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { * Fetches images from unsplash and stores it in cache * * @param {String} query search terms + * @returns {Promise} * @private */ _fetchImages: function (query) { @@ -72,7 +73,7 @@ var UnsplashCore = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { params: payload, }).then(function (result) { if (result.error) { - return $.Deferred().reject(result.error); + return Promise.reject(result.error); } cachedData.pageCached++; cachedData.images.push.apply(cachedData.images, result.results); From 226a6a9e677b1ebded9240e181cc2ea83fa49d72 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 57/78] [REF] website: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/backend/dashboard.js | 12 +- .../src/js/content/lazy_template_call.js | 2 +- addons/website/static/src/js/content/menu.js | 2 +- .../static/src/js/content/website_root.js | 4 +- .../static/src/js/editor/editor_menu.js | 26 +-- .../static/src/js/editor/snippets.options.js | 19 +- .../static/src/js/editor/wysiwyg_multizone.js | 14 +- .../js/editor/wysiwyg_multizone_translate.js | 2 +- addons/website/static/src/js/menu/content.js | 170 ++++++++-------- .../website/static/src/js/menu/customize.js | 36 ++-- addons/website/static/src/js/menu/edit.js | 16 +- addons/website/static/src/js/menu/navbar.js | 29 +-- .../website/static/src/js/menu/new_content.js | 35 ++-- addons/website/static/src/js/menu/seo.js | 75 ++++--- .../website/static/src/js/menu/translate.js | 8 +- addons/website/static/src/js/tours/rte.js | 7 +- .../js/tours/website_tour_reset_password.js | 2 +- addons/website/static/src/js/utils.js | 95 ++++----- addons/website/static/src/js/widgets/ace.js | 6 +- addons/website/static/src/js/widgets/theme.js | 185 +++++++++--------- addons/website/static/tests/website_tests.js | 8 +- 21 files changed, 393 insertions(+), 360 deletions(-) diff --git a/addons/website/static/src/js/backend/dashboard.js b/addons/website/static/src/js/backend/dashboard.js index 0ec306c10f7..9f539cbe03e 100644 --- a/addons/website/static/src/js/backend/dashboard.js +++ b/addons/website/static/src/js/backend/dashboard.js @@ -42,7 +42,7 @@ var Dashboard = AbstractAction.extend({ willStart: function() { var self = this; - return $.when(ajax.loadLibs(this), this._super()).then(function() { + return Promise.all([ajax.loadLibs(this), this._super()]).then(function() { return self.fetch_data(); }).then(function(){ var website = _.findWhere(self.websites, {selected: true}); @@ -63,20 +63,22 @@ var Dashboard = AbstractAction.extend({ */ fetch_data: function() { var self = this; - return this._rpc({ + var prom = this._rpc({ route: '/website/fetch_dashboard_data', params: { website_id: this.website_id || false, date_from: this.date_from.year()+'-'+(this.date_from.month()+1)+'-'+this.date_from.date(), date_to: this.date_to.year()+'-'+(this.date_to.month()+1)+'-'+this.date_to.date(), }, - }).done(function(result) { + }); + prom.then(function (result) { self.data = result; self.dashboards_data = result.dashboards; self.currency_id = result.currency_id; self.groups = result.groups; self.websites = result.websites; }); + return prom; }, on_link_analytics_settings: function(ev) { @@ -242,7 +244,7 @@ var Dashboard = AbstractAction.extend({ } var self = this; - $.when(this.fetch_data()).then(function() { + Promise.resolve(this.fetch_data()).then(function () { self.$('.o_website_dashboard').empty(); self.render_dashboards(); self.render_graphs(); @@ -253,7 +255,7 @@ var Dashboard = AbstractAction.extend({ on_website_button: function(website_id) { var self = this; this.website_id = website_id; - $.when(this.fetch_data()).then(function() { + Promise.resolve(this.fetch_data()).then(function () { self.$('.o_website_dashboard').empty(); self.render_dashboards(); self.render_graphs(); diff --git a/addons/website/static/src/js/content/lazy_template_call.js b/addons/website/static/src/js/content/lazy_template_call.js index 80f045cbb0f..f38aab40693 100644 --- a/addons/website/static/src/js/content/lazy_template_call.js +++ b/addons/website/static/src/js/content/lazy_template_call.js @@ -37,7 +37,7 @@ publicWidget.registry.LazyTemplateRenderer = publicWidget.Widget.extend({ }); }); - return $.when(def, renderDef); + return Promise.all([def, renderDef]); }, }); }); diff --git a/addons/website/static/src/js/content/menu.js b/addons/website/static/src/js/content/menu.js index e24ef22a530..913740098b6 100644 --- a/addons/website/static/src/js/content/menu.js +++ b/addons/website/static/src/js/content/menu.js @@ -126,7 +126,7 @@ publicWidget.registry.autohideMenu = publicWidget.Widget.extend({ $window.trigger('resize'); }); } - return $.when.apply($, defs).then(function () { + return Promise.all(defs).then(function () { if (!self.noAutohide) { dom.initAutoMoreMenu(self.$el, {unfoldable: '.divider, .divider ~ li'}); } diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js index e1a7be21487..9f16c94b38b 100644 --- a/addons/website/static/src/js/content/website_root.js +++ b/addons/website/static/src/js/content/website_root.js @@ -127,12 +127,12 @@ var WebsiteRoot = publicRootData.PublicRoot.extend({ object: $data.data('object'), }, }) - .done(function (result) { + .then(function (result) { $data.toggleClass("css_unpublished css_published"); $data.find('input').prop("checked", result); $data.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off'); }) - .fail(function (err, data) { + .guardedCatch(function (err, data) { return new Dialog(self, { title: data.data ? data.data.arguments[0] : "", $content: $('
', { diff --git a/addons/website/static/src/js/editor/editor_menu.js b/addons/website/static/src/js/editor/editor_menu.js index ee504413e2f..46c3fc0b6b1 100644 --- a/addons/website/static/src/js/editor/editor_menu.js +++ b/addons/website/static/src/js/editor/editor_menu.js @@ -75,15 +75,17 @@ var EditorMenu = Widget.extend({ */ cancel: function (reload) { var self = this; - var def = $.Deferred(); - if (!this.wysiwyg.isDirty()) { - def.resolve(); - } else { - var confirm = Dialog.confirm(this, _t("If you discard the current edition, all unsaved changes will be lost. You can cancel to return to the edition mode."), { - confirm_callback: def.resolve.bind(def), - }); - confirm.on('closed', def, def.reject); - } + var def = new Promise(function (resolve, reject) { + if (!self.wysiwyg.isDirty()) { + resolve(); + } else { + var confirm = Dialog.confirm(self, _t("If you discard the current edition, all unsaved changes will be lost. You can cancel to return to the edition mode."), { + confirm_callback: resolve, + }); + confirm.on('closed', self, reject); + } + }); + return def.then(function () { self.trigger_up('edition_will_stopped'); var $wrapwrap = $('#wrapwrap'); @@ -110,10 +112,10 @@ var EditorMenu = Widget.extend({ save: function (reload) { var self = this; this.trigger_up('edition_will_stopped'); - return this.wysiwyg.save().then(function (dirty) { + return this.wysiwyg.save().then(function (result) { var $wrapwrap = $('#wrapwrap'); self.editable($wrapwrap).removeClass('o_editable'); - if (dirty && reload !== false) { + if (result.isDirty && reload !== false) { // remove top padding because the connected bar is not visible $('body').removeClass('o_connected_user'); return self._reload(); @@ -180,7 +182,7 @@ var EditorMenu = Widget.extend({ this.$el.hide(); window.location.hash = 'scrollTop=' + window.document.body.scrollTop; window.location.reload(true); - return $.Deferred(); + return new Promise(function () {}); }, //-------------------------------------------------------------------------- diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index b90f1f91075..be97a42f96c 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -57,12 +57,13 @@ options.registry.menu_data = options.Class.extend({ actionName: 'edit_menu', params: [ function () { - var def = $.Deferred(); - self.trigger_up('request_save', { - onSuccess: def.resolve.bind(def), - onFailure: def.reject.bind(def), + var prom = new Promise(function (resolve, reject) { + self.trigger_up('request_save', { + onSuccess: resolve, + onFailure: reject, + }); }); - return def; + return prom; }, ], }); @@ -82,10 +83,10 @@ options.registry.company_data = options.Class.extend({ */ start: function () { var proto = options.registry.company_data.prototype; - var def; + var prom; var self = this; if (proto.__link === undefined) { - def = this._rpc({route: '/web/session/get_session_info'}).then(function (session) { + prom = this._rpc({route: '/web/session/get_session_info'}).then(function (session) { return self._rpc({ model: 'res.users', method: 'read', @@ -95,7 +96,7 @@ options.registry.company_data = options.Class.extend({ proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + (res && res[0] && res[0].company_id[0] || 1); }); } - return $.when(this._super.apply(this, arguments), def); + return Promise.all([this._super.apply(this, arguments), prom]); }, /** * When the users selects company data, opens a dialog to ask him if he @@ -737,7 +738,7 @@ options.registry.facebookPage = options.Class.extend({ })); } - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @override diff --git a/addons/website/static/src/js/editor/wysiwyg_multizone.js b/addons/website/static/src/js/editor/wysiwyg_multizone.js index 660afe80bdf..4d29e1f3567 100644 --- a/addons/website/static/src/js/editor/wysiwyg_multizone.js +++ b/addons/website/static/src/js/editor/wysiwyg_multizone.js @@ -238,20 +238,20 @@ var WysiwygMultizone = Wysiwyg.extend({ this.savingMutex.exec(this._saveCroppedImages.bind(this)); } var _super = this._super.bind(this); - return this.savingMutex.def.then(function () { + return this.savingMutex.lock.then(function () { return _super().then(function (_isDirty, html) { this._summernote.layoutInfo.editable.html(html); var $editable = this._getEditableArea(); var $areaDirty = $editable.filter('.o_dirty'); if (!$areaDirty.length) { - return false; + return { isDirty: false }; } $areaDirty.each(function (index, editable) { this.savingMutex.exec(this._saveEditable.bind(this, editable)); }.bind(this)); - return this.savingMutex.def.then(function () { - return true; + return this.savingMutex.lock.then(function () { + return { isDirty: true }; }); }.bind(this)); }.bind(this)); @@ -402,9 +402,9 @@ var WysiwygMultizone = Wysiwyg.extend({ var recordInfo = this._getRecordInfo({target: editable}); var outerHTML = this._getCleanedHtml(editable).prop('outerHTML'); var def = this._saveElement(outerHTML, recordInfo, editable); - def.done(function () { + def.then(function () { self.trigger_up('saved', recordInfo); - }).fail(function () { + }).guardedCatch(function () { self.trigger_up('canceled', recordInfo); }); return def; @@ -456,7 +456,7 @@ var WysiwygMultizone = Wysiwyg.extend({ }); } }).get(); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * Saves one (dirty) element of the page. diff --git a/addons/website/static/src/js/editor/wysiwyg_multizone_translate.js b/addons/website/static/src/js/editor/wysiwyg_multizone_translate.js index 5f68ff09e0f..85eecb1a85a 100644 --- a/addons/website/static/src/js/editor/wysiwyg_multizone_translate.js +++ b/addons/website/static/src/js/editor/wysiwyg_multizone_translate.js @@ -171,7 +171,7 @@ var WysiwygTranslate = WysiwygMultizone.extend({ kwargs: { context: recordInfo.context, }, - }).fail(function (error) { + }).guardedCatch(function (error) { console.error(error.data.message); webDialog.alert(null, error.data.message); }); diff --git a/addons/website/static/src/js/menu/content.js b/addons/website/static/src/js/menu/content.js index e18de94e084..d9f652cda3a 100644 --- a/addons/website/static/src/js/menu/content.js +++ b/addons/website/static/src/js/menu/content.js @@ -92,7 +92,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ self.fields = fields; })); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @override @@ -175,7 +175,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ } this.$('#date_publish_container').datetimepicker(datepickersOptions); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @override @@ -255,7 +255,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ * * @private * @param {integer} moID - * @returns {Deferred} + * @returns {Promise} */ _getPageDependencies: function (moID) { return this._rpc({ @@ -269,7 +269,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ * * @private * @param {integer} moID - * @returns {Deferred} + * @returns {Promise} */ _getPageKeyDependencies: function (moID) { return this._rpc({ @@ -282,7 +282,7 @@ var PagePropertiesDialog = weWidgets.Dialog.extend({ * Retrieves supported mimtype * * @private - * @returns {Deferred} + * @returns {Promise} */ _getSupportedMimetype: function () { return this._rpc({ @@ -488,7 +488,7 @@ var EditMenuDialog = weWidgets.Dialog.extend({ self.flat = self._flatenize(menu); self.to_delete = []; })); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @override @@ -735,63 +735,73 @@ var ContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * * @private * @param {function} [beforeReloadCallback] - * @returns {Deferred} + * @returns {Promise} * Unresolved if the menu is edited and saved as the page will be * reloaded. * Resolved otherwise. */ _editMenu: function (beforeReloadCallback) { var self = this; - var def = $.Deferred(); - - // If there is multiple menu on the page, ask the user which one he - // wants to edit - var selectDef = $.Deferred(); - if ($('[data-content_menu_id]').length) { - var select = new SelectEditMenuDialog(this); - select.on('save', selectDef, selectDef.resolve); - select.on('cancel', def, def.resolve); - select.open(); - } else { - selectDef.resolve(null); - } - selectDef.then(function (rootID) { - // Open the dialog to show the menu structure and allow its edition - var editDef = $.Deferred(); - var dialog = new EditMenuDialog(self, {}, rootID).open(); - dialog.on('save', editDef, editDef.resolve); - dialog.on('cancel', def, def.resolve); - return editDef; - }).then(function () { - // Before reloading the page after menu modification, does the - // given action to do. - return beforeReloadCallback && beforeReloadCallback(); - }).then(function () { - // Reload the page so that the menu modification are shown - window.location.reload(true); + return new Promise(function (resolve) { + function resolveWhenEditMenuDialogIsCancelled(rootID) { + return self._openEditMenuDialog(rootID, beforeReloadCallback).then(resolve); + } + if ($('[data-content_menu_id]').length) { + var select = new SelectEditMenuDialog(self); + select.on('save', self, resolveWhenEditMenuDialogIsCancelled); + select.on('cancel', self, resolve); + select.open(); + } else { + resolveWhenEditMenuDialogIsCancelled(null); + } }); - - return def; }, + /** + * + * @param {*} rootID + * @param {function|undefied} beforeReloadCallback function that returns a promise + * @returns {Promise} + */ + _openEditMenuDialog: function (rootID, beforeReloadCallback) { + var self = this; + return new Promise(function (resolve) { + var dialog = new EditMenuDialog(self, {}, rootID); + dialog.on('save', self, function () { + // Before reloading the page after menu modification, does the + // given action to do. + if (beforeReloadCallback) { + // Reload the page so that the menu modification are shown + beforeReloadCallback().then(function () { + window.location.reload(true); + }); + } else { + window.location.reload(true); + } + }); + dialog.on('cancel', self, resolve); + dialog.open(); + }); + }, + /** * Retrieves the value of a page option. * * @private * @param {string} name - * @returns {Deferred<*>} + * @returns {Promise<*>} */ _getPageOption: function (name) { var option = this.pageOptions[name]; if (!option) { - return $.Deferred().reject(); + return Promise.reject(); } - return $.when(option.value); + return Promise.resolve(option.value); }, /** * On save, simulated page options have to be server-saved. * * @private - * @returns {Deferred} + * @returns {Promise} */ _onSave: function () { var self = this; @@ -803,13 +813,13 @@ var ContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ }, true, true); } }); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * Opens the page properties dialog. * * @private - * @returns {Deferred} + * @returns {Promise} */ _pageProperties: function () { var mo; @@ -830,7 +840,7 @@ var ContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * @param {*} [params.value] (change value by default true -> false -> true) * @param {boolean} [forceSave=false] * @param {boolean} [noReload=false] - * @returns {Deferred} + * @returns {Promise} */ _togglePageOption: function (params, forceSave, noReload) { // First check it is a website page @@ -841,13 +851,13 @@ var ContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ }, }); if (mo.model !== 'website.page') { - return $.Deferred().reject(); + return Promise.reject(); } // Check if this is a valid option var option = this.pageOptions[params.name]; if (!option) { - return $.Deferred().reject(); + return Promise.reject(); } // Toggle the value @@ -856,23 +866,23 @@ var ContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ // If simulate is true, it means we want the option to be toggled but // not saved on the server yet if (!forceSave) { - return $.when(); + return Promise.resolve(); } // If not, write on the server page and reload the current location var vals = {}; vals[params.name] = option.value; - var def = this._rpc({ + var prom = this._rpc({ model: 'website.page', method: 'write', args: [[mo.id], vals], }); if (noReload) { - return def; + return prom; } - return def.then(function () { + return prom.then(function () { window.location.reload(); - return $.Deferred(); + return new Promise(function () {}); }); }, }); @@ -894,7 +904,7 @@ var PageManagement = Widget.extend({ * * @private * @param {integer} moID - * @returns {Deferred} + * @returns {Promise} */ _getPageDependencies: function (moID) { return this._rpc({ @@ -941,35 +951,35 @@ var PageManagement = Widget.extend({ // TODO: This function should be integrated in a widget in the future function _deletePage(pageId, fromPageManagement) { var self = this; - var def = $.Deferred(); - - // Search the page dependencies - this._getPageDependencies(pageId) - .then(function (dependencies) { - // Inform the user about those dependencies and ask him confirmation - var confirmDef = $.Deferred(); - Dialog.safeConfirm(self, "", { - title: _t("Delete Page"), - $content: $(qweb.render('website.delete_page', {dependencies: dependencies})), - confirm_callback: confirmDef.resolve.bind(confirmDef), - cancel_callback: def.resolve.bind(self), - }); - return confirmDef; - }).then(function () { - // Delete the page if the user confirmed - return self._rpc({ - model: 'website.page', - method: 'unlink', - args: [pageId], - }); - }).then(function () { - if (fromPageManagement) { - window.location.reload(true); - } - else { - window.location.href = '/'; - } - }, def.reject.bind(def)); + var prom = new Promise(function (resolve, reject) { + // Search the page dependencies + self._getPageDependencies(pageId) + .then(function (dependencies) { + // Inform the user about those dependencies and ask him confirmation + return new Promise(function (confirmResolve, confirmReject) { + Dialog.safeConfirm(self, "", { + title: _t("Delete Page"), + $content: $(qweb.render('website.delete_page', {dependencies: dependencies})), + confirm_callback: confirmResolve, + cancel_callback: resolve, + }); + }); + }).then(function () { + // Delete the page if the user confirmed + return self._rpc({ + model: 'website.page', + method: 'unlink', + args: [pageId], + }); + }).then(function () { + if (fromPageManagement) { + window.location.reload(true); + } + else { + window.location.href = '/'; + } + }, reject); + }); } websiteNavbarData.websiteNavbarRegistry.add(ContentMenu, '#content-menu'); diff --git a/addons/website/static/src/js/menu/customize.js b/addons/website/static/src/js/menu/customize.js index aced336de2c..59c4e865242 100644 --- a/addons/website/static/src/js/menu/customize.js +++ b/addons/website/static/src/js/menu/customize.js @@ -45,7 +45,7 @@ var CustomizeMenu = Widget.extend({ * * @private * @param {integer} viewID - * @returns {Deferred} + * @returns {Promise} * Unresolved if the customization succeeded as the page will be * reloaded. * Rejected otherwise. @@ -57,7 +57,7 @@ var CustomizeMenu = Widget.extend({ args: [[viewID]], }).then(function () { window.location.reload(); - return $.Deferred(); + return new Promise(function () {}); }); }, /** @@ -65,11 +65,11 @@ var CustomizeMenu = Widget.extend({ * the current page and shows them as switchable elements in the menu. * * @private - * @return {Deferred} + * @return {Promise} */ _loadCustomizeOptions: function () { if (this.__customizeOptionsLoaded) { - return $.when(); + return Promise.resolve(); } this.__customizeOptionsLoaded = true; @@ -169,18 +169,20 @@ var AceEditorMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * which are used by the current page. * * @private - * @returns {Deferred} + * @returns {Promise} */ _launchAce: function () { - var def = $.Deferred(); - this.trigger_up('action_demand', { - actionName: 'close_all_widgets', - onSuccess: def.resolve.bind(def), + var self = this; + var prom = new Promise(function (resolve, reject) { + self.trigger_up('action_demand', { + actionName: 'close_all_widgets', + onSuccess: resolve, + }); }); - return def.then((function () { - if (this.globalEditor) { - this.globalEditor.do_show(); - return $.when(); + prom.then(function () { + if (self.globalEditor) { + self.globalEditor.do_show(); + return Promise.resolve(); } else { var currentHash = window.location.hash; var indexOfView = currentHash.indexOf("?res="); @@ -193,16 +195,18 @@ var AceEditorMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ } } - this.globalEditor = new WebsiteAceEditor(this, $(document.documentElement).data('view-xmlid'), { + self.globalEditor = new WebsiteAceEditor(self, $(document.documentElement).data('view-xmlid'), { initialResID: initialResID, defaultBundlesRestriction: [ "web.assets_frontend", "website.assets_frontend", ], }); - return this.globalEditor.appendTo(document.body); + return self.globalEditor.appendTo(document.body); } - }).bind(this)); + }); + + return prom; }, }); diff --git a/addons/website/static/src/js/menu/edit.js b/addons/website/static/src/js/menu/edit.js index a9712be72bb..a08ca136131 100644 --- a/addons/website/static/src/js/menu/edit.js +++ b/addons/website/static/src/js/menu/edit.js @@ -53,7 +53,7 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ // If we auto start the editor, do not show a welcome message if (this._editorAutoStart) { - return $.when(def, this._startEditMode()); + return Promise.all([def, this._startEditMode()]); } // Check that the page is empty @@ -85,7 +85,7 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * welcome message if necessary. * * @private - * @returns {Deferred} + * @returns {Promise} */ _startEditMode: function () { var self = this; @@ -111,13 +111,13 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ .find('.oe_structure.oe_empty, [data-oe-type="html"]') .not('[data-editor-message]') .attr('data-editor-message', _t('DRAG BUILDING BLOCKS HERE')); - var def = $.Deferred(); - self.trigger_up('widgets_start_request', { - editableMode: true, - onSuccess: def.resolve.bind(def), - onFailure: def.reject.bind(def), + new Promise(function (resolve, reject) { + self.trigger_up('widgets_start_request', { + editableMode: true, + onSuccess: resolve, + onFailure: reject, + }); }); - return def; }); }, /** diff --git a/addons/website/static/src/js/menu/navbar.js b/addons/website/static/src/js/menu/navbar.js index b132b0e2d51..1809f75a3d7 100644 --- a/addons/website/static/src/js/menu/navbar.js +++ b/addons/website/static/src/js/menu/navbar.js @@ -26,7 +26,11 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ */ init: function () { this._super.apply(this, arguments); - this._widgetDefs = [$.Deferred()]; + var self = this; + var initPromise = new Promise(function (resolve) { + self.resolveInit = resolve; + }); + this._widgetDefs = [initPromise]; }, /** * @override @@ -34,7 +38,7 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ start: function () { var self = this; return this._super.apply(this, arguments).then(function () { - self._widgetDefs[0].resolve(); + self.resolveInit(); }); }, @@ -67,7 +71,7 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ * @private * @param {string} actionName * @param {Array} params - * @returns {Deferred} + * @returns {Promise} */ _handleAction: function (actionName, params, _i) { var self = this; @@ -88,20 +92,20 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ // instantiated yet (rare) -> retry some times to eventually abort if (_i > 50) { console.warn(_.str.sprintf("Action '%s' was not able to be handled.", actionName)); - return $.Deferred().reject(); + return Promise.reject(); } return concurrency.delay(100).then(function () { return self._handleAction(actionName, params, (_i || 0) + 1); }); } - return $.when.apply($, defs); + return Promise.all(defs); }); }, /** * @private */ _whenReadyForActions: function () { - return $.when.apply($, this._widgetDefs); + return Promise.all(this._widgetDefs); }, //-------------------------------------------------------------------------- @@ -118,9 +122,10 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ _onActionMenuClick: function (ev) { var $button = $(ev.currentTarget); $button.prop('disabled', true); - this._handleAction($button.data('action')).always(function () { + var always = function () { $button.prop('disabled', false); - }); + }; + this._handleAction($button.data('action')).then(always).guardedCatch(always); }, /** * Called when an action is asked to be executed from a child widget -> @@ -130,10 +135,10 @@ var WebsiteNavbar = publicWidget.RootWidget.extend({ _onActionDemand: function (ev) { var def = this._handleAction(ev.data.actionName, ev.data.params); if (ev.data.onSuccess) { - def.done(ev.data.onSuccess); + def.then(ev.data.onSuccess); } if (ev.data.onFailure) { - def.fail(ev.data.onFailure); + def.guardedCatch(ev.data.onFailure); } }, /** @@ -210,12 +215,12 @@ var WebsiteNavbarActionWidget = Widget.extend({ * * @param {string} actionName * @param {Array} params - * @returns {Deferred|null} action's deferred or null if no action was found + * @returns {Promise|null} action's promise or null if no action was found */ handleAction: function (actionName, params) { var action = this[this.actions[actionName]]; if (action) { - return $.when(action.apply(this, params || [])); + return Promise.resolve(action.apply(this, params || [])); } return null; }, diff --git a/addons/website/static/src/js/menu/new_content.js b/addons/website/static/src/js/menu/new_content.js index d414bc8a800..46a2ce7d7e2 100644 --- a/addons/website/static/src/js/menu/new_content.js +++ b/addons/website/static/src/js/menu/new_content.js @@ -70,7 +70,7 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * redirects the user to this new page. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewPage: function () { return wUtils.prompt({ @@ -87,14 +87,16 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ $add.find('input').prop('checked', true); $group.after($add); } - }).then(function (val, field, $dialog) { + }).then(function (result) { + var val = result.val; + var $dialog = result.dialog; if (!val) { return; } var url = '/website/add/' + encodeURIComponent(val); if ($dialog.find('input[type="checkbox"]').is(':checked')) url +='?add_menu=1'; document.location = url; - return $.Deferred(); + return new Promise(function () {}); }); }, /** @@ -138,7 +140,7 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * * @private * @param {number} moduleId: the module to install - * @return {Deferred} + * @return {Promise} */ _install: function (moduleId) { this.pendingInstall = true; @@ -147,7 +149,7 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ model: 'ir.module.module', method: 'button_immediate_install', args: [[moduleId]], - }).fail(function () { + }).guardedCatch(function () { $('body').css('pointer-events', ''); }); }, @@ -155,20 +157,21 @@ var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * Show the menu * * @private - * @returns Deferred + * @returns {Promise} */ _showMenu: function () { - var def = $.Deferred(); - this.trigger_up('action_demand', { - actionName: 'close_all_widgets', - onSuccess: def.resolve.bind(def), - }); - return def.then((function () { - this.firstTab = true; - this.$newContentMenuChoices.removeClass('o_hidden'); + var self = this; + return new Promise(function (resolve, reject) { + self.trigger_up('action_demand', { + actionName: 'close_all_widgets', + onSuccess: resolve, + }); + }).then(function () { + self.firstTab = true; + self.$newContentMenuChoices.removeClass('o_hidden'); $('body').addClass('o_new_content_open'); - this.$('> a').focus(); - }).bind(this)); + self.$('> a').focus(); + }); }, //-------------------------------------------------------------------------- diff --git a/addons/website/static/src/js/menu/seo.js b/addons/website/static/src/js/menu/seo.js index 425024539ec..792934a8375 100644 --- a/addons/website/static/src/js/menu/seo.js +++ b/addons/website/static/src/js/menu/seo.js @@ -105,6 +105,7 @@ var Keyword = Widget.extend({ this._super(parent); }, start: function () { + var self = this; this.$('.js_seo_keyword_suggestion').empty(); this.suggestionList = new SuggestionList(this, { root: this.keyword, @@ -114,12 +115,12 @@ var Keyword = Widget.extend({ this.suggestionList.on('selected', this, function (word, language) { this.trigger('selected', word, language); }); - this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')); - - this.htmlPage.on('title-changed', this, this._updateTitle); - this.htmlPage.on('description-changed', this, this._updateDescription); - this._updateTitle(); - this._updateDescription(); + return this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion')).then(function() { + self.htmlPage.on('title-changed', self, self._updateTitle); + self.htmlPage.on('description-changed', self, self._updateDescription); + self._updateTitle(); + self._updateDescription(); + }); }, destroy: function () { this.trigger('removed'); @@ -461,10 +462,10 @@ var MetaKeywords = Widget.extend({ this.keywordList.on('content-updated', this, function (removed) { self._updateTable(removed); }); - this.keywordList.insertAfter(this.$('.table thead')); - - this._getLanguages(); - this._updateTable(); + return this.keywordList.insertAfter(this.$('.table thead')).then(function() { + self._getLanguages(); + self._updateTable(); + }); }, _addKeyword: function () { var $language = this.$('select[name=seo_page_language]'); @@ -740,39 +741,37 @@ var SeoConfigurator = Dialog.extend({ }, loadMetaData: function () { var obj = this.getMainObject(); - var def = $.Deferred(); - if (!obj) { - // return $.Deferred().reject(new Error("No main_object was found.")); - def.resolve(null); - } else { - var fields = ['website_meta_title', 'website_meta_description', 'website_meta_keywords' - ,'website_meta_og_img']; - if (obj.model === 'website.page'){ - fields.push('website_indexed'); - fields.push('website_id'); - } - rpc.query({ - model: obj.model, - method: 'read', - args: [[obj.id], fields], - }).then(function (data) { - if (data.length) { - var meta = data[0]; - meta.model = obj.model; - def.resolve(meta); - } else { - def.resolve(null); + return new Promise(function (resolve, reject) { + if (!obj) { + // return Promise.reject(new Error("No main_object was found.")); + resolve(null); + } else { + var fields = ['website_meta_title', 'website_meta_description', 'website_meta_keywords' + ,'website_meta_og_img']; + if (obj.model === 'website.page'){ + fields.push('website_indexed'); + fields.push('website_id'); } - }).fail(function () { - def.reject(); - }); - } - return def; + rpc.query({ + model: obj.model, + method: 'read', + args: [[obj.id], fields], + }).then(function (data) { + if (data.length) { + var meta = data[0]; + meta.model = obj.model; + resolve(meta); + } else { + resolve(null); + } + }).guardedCatch(reject); + } + }); }, saveMetaData: function (data) { var obj = this.getMainObject(); if (!obj) { - return $.Deferred().reject(); + return Promise.reject(); } else { return this._rpc({ model: obj.model, diff --git a/addons/website/static/src/js/menu/translate.js b/addons/website/static/src/js/menu/translate.js index 559f9ef3a25..043a900fff6 100644 --- a/addons/website/static/src/js/menu/translate.js +++ b/addons/website/static/src/js/menu/translate.js @@ -43,7 +43,7 @@ var TranslatePageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * edit mode. * * @private - * @returns {Deferred} + * @returns {Promise} */ _goToMasterPage: function () { var lang = '/' + utils.get_cookie('frontend_lang'); @@ -60,19 +60,19 @@ var TranslatePageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ link.search += (link.search ? '&' : '?') + 'r=' + encodeURIComponent(current.pathname + current.search + current.hash); window.location = link.href; - return $.Deferred(); + return new Promise(function () {}); }, /** * Redirects the user to the same page in translation mode (or start the * translator is translation mode is already enabled). * * @private - * @returns {Deferred} + * @returns {Promise} */ _startTranslateMode: function () { if (!this._mustEditTranslations) { window.location.search += '&edit_translations'; - return $.Deferred(); + return new Promise(function () {}); } var translator = new TranslatorMenu(this); diff --git a/addons/website/static/src/js/tours/rte.js b/addons/website/static/src/js/tours/rte.js index 2fd7aab345c..906f16e2924 100644 --- a/addons/website/static/src/js/tours/rte.js +++ b/addons/website/static/src/js/tours/rte.js @@ -6,9 +6,10 @@ var session = require('web.session'); var tour = require('web_tour.tour'); var Wysiwyg = require('web_editor.wysiwyg.root'); -var domReady = $.Deferred(); -$(domReady.resolve.bind(domReady)); -var ready = $.when(domReady, session.is_bound, ajax.loadXML()); +var domReady = new Promise(function (resolve) { + $(resolve); +}); +var ready = Promise.all([domReady, session.is_bound, ajax.loadXML()]); tour.register('rte_translator', { test: true, diff --git a/addons/website/static/src/js/tours/website_tour_reset_password.js b/addons/website/static/src/js/tours/website_tour_reset_password.js index 307b741ceef..3239dc28f80 100644 --- a/addons/website/static/src/js/tours/website_tour_reset_password.js +++ b/addons/website/static/src/js/tours/website_tour_reset_password.js @@ -57,7 +57,7 @@ tour.register('website_reset_password', { }); }); - return $.when(def1, def2, def3).then(function () { + return Promise.all([def1, def2, def3]).then(function () { window.location.href = usersUrl; }); }, diff --git a/addons/website/static/src/js/utils.js b/addons/website/static/src/js/utils.js index 01f0e7797a4..e93a52897c0 100644 --- a/addons/website/static/src/js/utils.js +++ b/addons/website/static/src/js/utils.js @@ -17,7 +17,7 @@ function loadAnchors(url) { if (url !== window.location.pathname && url[0] !== '#') { def = $.get(window.location.origin + url); } else { - def = $.when(document.body.outerHTML); + def = Promise.resolve(document.body.outerHTML); } return def.then(function (response) { return _.map($(response).find('[id][data-anchor=true]'), function (el) { @@ -69,13 +69,14 @@ function onceAllImagesLoaded($element) { if (img.complete) { return; // Already loaded } - var def = $.Deferred(); - $(img).one('load', function () { - def.resolve(); + var def = new Promise(function (resolve, reject) { + $(img).one('load', function () { + resolve(); + }); }); return def; }); - return $.when.apply($, defs); + return Promise.all(defs); } /** @@ -109,7 +110,7 @@ function prompt(options, _qweb) { * @param {String} [options.textarea] tell the modal to use a textarea field, the given value will be the field title * @param {String} [options.select] tell the modal to use a select box, the given value will be the field title * @param {Object} [options.default=''] default value of the field - * @param {Function} [options.init] optional function that takes the `field` (enhanced with a fillWith() method) and the `dialog` as parameters [can return a deferred] + * @param {Function} [options.init] optional function that takes the `field` (enhanced with a fillWith() method) and the `dialog` as parameters [can return a promise] */ if (typeof options === 'string') { options = { @@ -133,51 +134,51 @@ function prompt(options, _qweb) { options.field_type = type; options.field_name = options.field_name || options[type]; - var def = $.Deferred(); - - $.when(xmlDef).then(function () { - var dialog = $(qweb.render(_qweb, options)).appendTo('body'); - options.$dialog = dialog; - var field = dialog.find(options.field_type).first(); - field.val(options['default']); // dict notation for IE<9 - field.fillWith = function (data) { - if (field.is('select')) { - var select = field[0]; - data.forEach(function (item) { - select.options[select.options.length] = new window.Option(item[1], item[0]); - }); - } else { - field.val(data); - } - }; - var init = options.init(field, dialog); - $.when(init).then(function (fill) { - if (fill) { - field.fillWith(fill); - } - dialog.modal('show'); - field.focus(); - dialog.on('click', '.btn-primary', function () { + var def = new Promise(function (resolve, reject) { + Promise.resolve(xmlDef).then(function () { + var dialog = $(qweb.render(_qweb, options)).appendTo('body'); + options.$dialog = dialog; + var field = dialog.find(options.field_type).first(); + field.val(options['default']); // dict notation for IE<9 + field.fillWith = function (data) { + if (field.is('select')) { + var select = field[0]; + data.forEach(function (item) { + select.options[select.options.length] = new window.Option(item[1], item[0]); + }); + } else { + field.val(data); + } + }; + var init = options.init(field, dialog); + Promise.resolve(init).then(function (fill) { + if (fill) { + field.fillWith(fill); + } + dialog.modal('show'); + field.focus(); + dialog.on('click', '.btn-primary', function () { var backdrop = $('.modal-backdrop'); - def.resolve(field.val(), field, dialog); - dialog.modal('hide').remove(); + resolve({ val: field.val(), field: field, dialog: dialog }); + dialog.modal('hide').remove(); + backdrop.remove(); + }); + }); + dialog.on('hidden.bs.modal', function () { + var backdrop = $('.modal-backdrop'); + reject(); + dialog.remove(); backdrop.remove(); }); + if (field.is('input[type="text"], select')) { + field.keypress(function (e) { + if (e.which === 13) { + e.preventDefault(); + dialog.find('.btn-primary').trigger('click'); + } + }); + } }); - dialog.on('hidden.bs.modal', function () { - var backdrop = $('.modal-backdrop'); - def.reject(); - dialog.remove(); - backdrop.remove(); - }); - if (field.is('input[type="text"], select')) { - field.keypress(function (e) { - if (e.which === 13) { - e.preventDefault(); - dialog.find('.btn-primary').trigger('click'); - } - }); - } }); return def; diff --git a/addons/website/static/src/js/widgets/ace.js b/addons/website/static/src/js/widgets/ace.js index d9440be7b64..f14f38cd911 100644 --- a/addons/website/static/src/js/widgets/ace.js +++ b/addons/website/static/src/js/widgets/ace.js @@ -63,9 +63,9 @@ var WebsiteAceEditor = AceEditor.extend({ } }).bind(this))); } - return $.when.apply($, defs).then((function () { + return Promise.all(defs).then((function () { window.location.reload(); - return $.Deferred(); + return new Promise(function() { }); })); }).bind(this)); }, @@ -75,7 +75,7 @@ var WebsiteAceEditor = AceEditor.extend({ _resetResource: function () { return this._super.apply(this, arguments).then((function () { window.location.reload(); - return $.Deferred(); + return new Promise(function () {}); }).bind(this)); }, /** diff --git a/addons/website/static/src/js/widgets/theme.js b/addons/website/static/src/js/widgets/theme.js index f35769a3748..0b41f844b7e 100644 --- a/addons/website/static/src/js/widgets/theme.js +++ b/addons/website/static/src/js/widgets/theme.js @@ -143,7 +143,7 @@ var ThemeCustomizeDialog = Dialog.extend({ return core.qweb.add_template(data); }); } - return $.when(this._super.apply(this, arguments), templateDef); + return Promise.all([this._super.apply(this, arguments), templateDef]); }, /** * @override @@ -204,7 +204,7 @@ var ThemeCustomizeDialog = Dialog.extend({ } }); - return $.when(this._super.apply(this, arguments), loadDef); + return Promise.all([this._super.apply(this, arguments), loadDef]); }, //-------------------------------------------------------------------------- @@ -216,36 +216,37 @@ var ThemeCustomizeDialog = Dialog.extend({ */ _chooseBodyCustomImage: function () { var self = this; - var def = $.Deferred(); - var $image = $(''); - var editor = new weWidgets.MediaDialog(this, { - onlyImages: true, - firstFilters: ['background'], - }, $image[0]); + var def = new Promise(function (resolve, reject) { + var $image = $(''); + var editor = new weWidgets.MediaDialog(this, { + onlyImages: true, + firstFilters: ['background'], + }, $image[0]); - editor.on('save', this, function (media) { // TODO use scss customization instead (like for user colors) - var src = $(media).attr('src'); - self._rpc({ - model: 'ir.model.data', - method: 'get_object_reference', - args: ['website', this.CUSTOM_BODY_IMAGE_XML_ID], - }).then(function (data) { - return self._rpc({ - model: 'ir.ui.view', - method: 'save', - args: [ - data[1], - '#wrapwrap { background-image: url("' + src + '"); }', - '//style', - ], - }); - }).always(def.resolve.bind(def)); - }); - editor.on('cancel', this, function () { - def.resolve(); - }); + editor.on('save', this, function (media) { // TODO use scss customization instead (like for user colors) + var src = $(media).attr('src'); + self._rpc({ + model: 'ir.model.data', + method: 'get_object_reference', + args: ['website', this.CUSTOM_BODY_IMAGE_XML_ID], + }).then(function (data) { + return self._rpc({ + model: 'ir.ui.view', + method: 'save', + args: [ + data[1], + '#wrapwrap { background-image: url("' + src + '"); }', + '//style', + ], + }); + }).then(resolve).guardedCatch(resolve); + }); + editor.on('cancel', this, function () { + resolve(); + }); - editor.open(); + editor.open(); + }); return def; }, @@ -433,39 +434,37 @@ var ThemeCustomizeDialog = Dialog.extend({ var colorName = $color.data('color'); var colorType = $color.data('colorType'); - var def = $.Deferred(); + return new Promise(function (resolve, reject) { + var colorpicker = new ColorpickerDialog(this, { + defaultColor: $color.css('background-color'), + }); + var chosenColor = undefined; + colorpicker.on('colorpicker:saved', this, function (ev) { + ev.stopPropagation(); + chosenColor = ev.data.cssColor; + }); + colorpicker.on('closed', this, function (ev) { + if (chosenColor === undefined) { + resolve(); + return; + } - var colorpicker = new ColorpickerDialog(this, { - defaultColor: $color.css('background-color'), + var baseURL = '/website/static/src/scss/options/colors/'; + var url = _.str.sprintf('%suser_%scolor_palette.scss', baseURL, (colorType ? (colorType + '_') : '')); + + var colors = {}; + colors[colorName] = chosenColor; + if (colorName === 'alpha') { + colors['beta'] = 'null'; + colors['gamma'] = 'null'; + colors['delta'] = 'null'; + colors['epsilon'] = 'null'; + } + + self._makeSCSSCusto(url, colors).always(resolve); + }); + colorpicker.open(); }); - var chosenColor = undefined; - colorpicker.on('colorpicker:saved', this, function (ev) { - ev.stopPropagation(); - chosenColor = ev.data.cssColor; - }); - colorpicker.on('closed', this, function (ev) { - if (chosenColor === undefined) { - def.resolve(); - return; - } - - var baseURL = '/website/static/src/scss/options/colors/'; - var url = _.str.sprintf('%suser_%scolor_palette.scss', baseURL, (colorType ? (colorType + '_') : '')); - - var colors = {}; - colors[colorName] = chosenColor; - if (colorName === 'alpha') { - colors['beta'] = 'null'; - colors['gamma'] = 'null'; - colors['delta'] = 'null'; - colors['epsilon'] = 'null'; - } - - self._makeSCSSCusto(url, colors).always(def.resolve.bind(def)); - }); - colorpicker.open(); - - return def; }, /** * @private @@ -494,7 +493,7 @@ var ThemeCustomizeDialog = Dialog.extend({ return self._quickEdit($(inputData)); })); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @private @@ -504,23 +503,25 @@ var ThemeCustomizeDialog = Dialog.extend({ var value = parseFloat(text) || ''; var unit = text.match(/([^\s\d]+)$/)[1]; - var def = $.Deferred(); - var qEdit = new QuickEdit(this, value, unit); - qEdit.on('QuickEdit:save', this, function (ev) { - ev.stopPropagation(); + var def = new Promise(function (resolve, reject) { + var qEdit = new QuickEdit(this, value, unit); + qEdit.on('QuickEdit:save', this, function (ev) { + ev.stopPropagation(); - var value = ev.data.value; - // Convert back to rem if needed - if ($inputData.data('unit') === 'rem' && unit === 'px' && value !== 'null') { - value = parseFloat(value) / this.PX_BY_REM + 'rem'; - } + var value = ev.data.value; + // Convert back to rem if needed + if ($inputData.data('unit') === 'rem' && unit === 'px' && value !== 'null') { + value = parseFloat(value) / this.PX_BY_REM + 'rem'; + } - var values = {}; - values[$inputData.data('value')] = value; - this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', values) - .always(def.resolve.bind(def)); + var values = {}; + values[$inputData.data('value')] = value; + this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', values) + .always(resolve); + }); + qEdit.appendTo($inputData.closest('.o_theme_customize_option')); }); - qEdit.appendTo($inputData.closest('.o_theme_customize_option')); + return def; }, /** @@ -581,7 +582,7 @@ var ThemeCustomizeDialog = Dialog.extend({ disable: (disable || []).join(','), tab: this.$('.nav-link.active').parent().index(), }); - return $.Deferred(); + return Promise.resolve(); } return this._rpc({ @@ -599,22 +600,26 @@ var ThemeCustomizeDialog = Dialog.extend({ $allLinks = $allLinks.add($links); var $newLinks = $(bundleContent).filter(linkSelector); - var linksLoaded = $.Deferred(); - var nbLoaded = 0; - $newLinks.on('load', function (e) { - if (++nbLoaded >= $newLinks.length) { - linksLoaded.resolve(); - } - }); - $newLinks.on('error', function (e) { - linksLoaded.reject(); - window.location.hash = 'theme=true'; - window.location.reload(); + var linksLoaded = new Promise(function (resolve, reject) { + var nbLoaded = 0; + $newLinks.on('load', function () { + if (++nbLoaded >= $newLinks.length) { + resolve(); + } + }); + $newLinks.on('error', function () { + reject(); + window.location.hash = 'theme=true'; + window.location.reload(); + }); }); $links.last().after($newLinks); return linksLoaded; }); - return $.when.apply($, defs).always(function () { + return Promise.all(defs).then(function () { + $loading.remove(); + $allLinks.remove(); + }).guardedCatch(function () { $loading.remove(); $allLinks.remove(); }); @@ -750,7 +755,7 @@ var ThemeCustomizeMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({ * * @private * @param {string} tab - * @returns {Deferred} + * @returns {Promise} */ _openThemeCustomizeDialog: function (tab) { return new ThemeCustomizeDialog(this, {tab: tab}).open(); diff --git a/addons/website/static/tests/website_tests.js b/addons/website/static/tests/website_tests.js index e2dbe53b1b1..cd7d14a68a3 100644 --- a/addons/website/static/tests/website_tests.js +++ b/addons/website/static/tests/website_tests.js @@ -24,10 +24,10 @@ QUnit.module('website', { }; }, }, function () { - QUnit.test("widget website button: display false value", function (assert) { + QUnit.test("widget website button: display false value", async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'blog_post', data: this.data, @@ -49,10 +49,10 @@ QUnit.module('website', { assert.containsOnce(form, selector, "there should be one text in red"); form.destroy(); }); - QUnit.test("widget website button: display true value", function (assert) { + QUnit.test("widget website button: display true value", async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'blog_post', data: this.data, From 648e3326a0bcc7fd6240a0a91ff5e672035439c7 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:33 +0100 Subject: [PATCH 58/78] [REF] website_blog: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/s_latest_posts_editor.js | 2 +- .../static/src/js/s_latest_posts_frontend.js | 63 ++++++++++--------- .../static/src/js/website_blog.editor.js | 13 ++-- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/addons/website_blog/static/src/js/s_latest_posts_editor.js b/addons/website_blog/static/src/js/s_latest_posts_editor.js index 0528aa522fc..441c1602cb4 100644 --- a/addons/website_blog/static/src/js/s_latest_posts_editor.js +++ b/addons/website_blog/static/src/js/s_latest_posts_editor.js @@ -66,7 +66,7 @@ sOptions.registry.js_get_posts_selectBlog = sOptions.Class.extend({ }); }); - return $.when(this._super.apply(this, arguments), def); + return Promise.all([this._super.apply(this, arguments), def]); }, //-------------------------------------------------------------------------- diff --git a/addons/website_blog/static/src/js/s_latest_posts_frontend.js b/addons/website_blog/static/src/js/s_latest_posts_frontend.js index f578852d7a6..324f0cd51ca 100644 --- a/addons/website_blog/static/src/js/s_latest_posts_frontend.js +++ b/addons/website_blog/static/src/js/s_latest_posts_frontend.js @@ -28,40 +28,43 @@ publicWidget.registry.js_get_posts = publicWidget.Widget.extend({ domain.push(['blog_id', '=', parseInt(blogID)]); } - var def = $.Deferred(); - this._rpc({ - route: '/blog/render_latest_posts', - params: { - template: template, - domain: domain, - limit: limit, - }, - }).then(function (posts) { - var $posts = $(posts).filter('.s_latest_posts_post'); - if (!$posts.length) { - self.$target.append($('
', {class: 'col-md-6 offset-md-3'}) + var prom = new Promise(function (resolve) { + self._rpc({ + route: '/blog/render_latest_posts', + params: { + template: template, + domain: domain, + limit: limit, + }, + }).then(function (posts) { + var $posts = $(posts).filter('.s_latest_posts_post'); + if (!$posts.length) { + self.$target.append($('
', {class: 'col-md-6 offset-md-3'}) .append($('
', { class: 'alert alert-warning alert-dismissible text-center', text: _t("No blog post was found. Make sure your posts are published."), }))); - return; - } + return; + } - if (loading && loading === true) { - // Perform an intro animation - self._showLoading($posts); - } else { - self.$target.html($posts); - } - }, function (e) { - if (self.editableMode) { - self.$target.append($('

', { - class: 'text-danger', - text: _t("An error occured with this latest posts block. If the problem persists, please consider deleting it and adding a new one"), - })); - } - }).always(def.resolve.bind(def)); - return $.when(this._super.apply(this, arguments), def); + if (loading && loading === true) { + // Perform an intro animation + self._showLoading($posts); + } else { + self.$target.html($posts); + } + resolve(); + }).guardedCatch(function () { + if (self.editableMode) { + self.$target.append($('

', { + class: 'text-danger', + text: _t("An error occured with this latest posts block. If the problem persists, please consider deleting it and adding a new one"), + })); + } + resolve(); + }); + }); + return Promise.all([this._super.apply(this, arguments), prom]); }, /** * @override @@ -126,7 +129,7 @@ publicWidget.registry.js_get_posts = publicWidget.Widget.extend({ $progress.hide(); }, 5000); - $dummyImg.load(function () { + $dummyImg.on('load', function () { $bar.css('width', '100%').attr('aria-valuenow', '100'); setTimeout(function () { $post.removeClass('js-loading'); diff --git a/addons/website_blog/static/src/js/website_blog.editor.js b/addons/website_blog/static/src/js/website_blog.editor.js index d367a1a5a2d..d4cdeb3e679 100644 --- a/addons/website_blog/static/src/js/website_blog.editor.js +++ b/addons/website_blog/static/src/js/website_blog.editor.js @@ -21,7 +21,7 @@ WebsiteNewMenu.include({ * it and redirects the user to this new post. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewBlogPost: function () { return this._rpc({ @@ -30,7 +30,7 @@ WebsiteNewMenu.include({ }).then(function (blog_ids) { if (blog_ids.length === 1) { document.location = '/blog/' + blog_ids[0][0] + '/post/new'; - return $.Deferred(); + return new Promise(function () {}); } else if (blog_ids.length > 1) { return wUtils.prompt({ id: 'editor_new_blog', @@ -39,12 +39,13 @@ WebsiteNewMenu.include({ init: function (field) { return blog_ids; }, - }).then(function (blog_id) { + }).then(function (result) { + var blog_id = result.val; if (!blog_id) { return; } document.location = '/blog/' + blog_id + '/post/new'; - return $.Deferred(); + return new Promise(function () {}); }); } }); @@ -63,7 +64,7 @@ var options = require('web_editor.snippets.options'); var WysiwygMultizone = require('web_editor.wysiwyg.multizone'); if (!$('.website_blog').length) { - return $.Deferred().reject("DOM doesn't contain '.website_blog'"); + return Promise.reject("DOM doesn't contain '.website_blog'"); } WysiwygMultizone.include({ @@ -104,7 +105,7 @@ WysiwygMultizone.include({ }, })); } - return $.when.apply($, defs); + return Promise.all(defs); }, }); From 7a899753c1bad167a4c32debd8a017125163d1ab Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:34 +0100 Subject: [PATCH 59/78] [REF] website_crm_partner_assign: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/crm_partner_assign.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index 3fc3bfc4fd2..49d0b8c3de4 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -25,18 +25,18 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @private * @param {jQuery} $btn * @param {function} callback - * @returns {Deferred} + * @returns {Promise} */ _buttonExec: function ($btn, callback) { // TODO remove once the automatic system which does this lands in master $btn.prop('disabled', true); - return callback.call(this).fail(function () { + return callback.call(this).guardedCatch(function () { $btn.prop('disabled', false); }); }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _confirmInterestedPartner: function () { return this._rpc({ @@ -52,7 +52,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _confirmDesinterestedPartner: function () { return this._rpc({ @@ -71,7 +71,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ /** * @private * @param {} - * @returns {Deferred} + * @returns {Promise} */ _changeOppStage: function (leadID, stageID) { return this._rpc({ @@ -87,7 +87,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _editContact: function () { return this._rpc({ @@ -111,7 +111,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _createOpportunity: function () { return this._rpc({ @@ -126,7 +126,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ if (response.errors) { $('#new-opp-dialog .alert').remove(); $('#new-opp-dialog div:first').prepend('

' + response.errors + '
'); - return $.Deferred().reject(response); + return Promise.reject(response); } else { window.location = '/my/opportunity/' + response.id; } @@ -134,7 +134,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _editOpportunity: function () { return this._rpc({ From a8e8184048e56fb3243c15e33d927c2afe25dd97 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:34 +0100 Subject: [PATCH 60/78] [REF] website_event: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_event/static/src/js/website_event.editor.js | 7 ++++--- addons/website_event/static/src/js/website_event.js | 4 ++-- addons/website_event/static/src/js/website_geolocation.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/website_event/static/src/js/website_event.editor.js b/addons/website_event/static/src/js/website_event.editor.js index 90b1c07451d..48f3a995d08 100644 --- a/addons/website_event/static/src/js/website_event.editor.js +++ b/addons/website_event/static/src/js/website_event.editor.js @@ -21,7 +21,7 @@ WebsiteNewMenu.include({ * and redirects the user to this new event. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewEvent: function () { var self = this; @@ -29,7 +29,8 @@ WebsiteNewMenu.include({ id: "editor_new_event", window_title: _t("New Event"), input: _t("Event Name"), - }).then(function (eventName) { + }).then(function (result) { + var eventName = result.val; if (!eventName) { return; } @@ -40,7 +41,7 @@ WebsiteNewMenu.include({ }, }).then(function (url) { window.location.href = url; - return $.Deferred(); + return new Promise(function () {}); }); }); }, diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js index 4c7d910ee5d..39bc211abc7 100644 --- a/addons/website_event/static/src/js/website_event.js +++ b/addons/website_event/static/src/js/website_event.js @@ -36,7 +36,7 @@ var EventRegistrationForm = Widget.extend({ $('
') .text(_t('Please select at least one ticket.')) .insertAfter('#registration_form table'); - return $.Deferred(); + return new Promise(function () {}); } else { $button.attr('disabled', true); return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) { @@ -65,7 +65,7 @@ publicWidget.registry.EventRegistrationFormInstance = publicWidget.Widget.extend start: function () { var def = this._super.apply(this, arguments); var instance = new EventRegistrationForm(this); - return $.when(def, instance.appendTo(this.$el)); + return Promise.all([def, instance.appendTo(this.$el)]); }, }); diff --git a/addons/website_event/static/src/js/website_geolocation.js b/addons/website_event/static/src/js/website_geolocation.js index 6de88dd32a2..19ee2f87033 100644 --- a/addons/website_event/static/src/js/website_geolocation.js +++ b/addons/website_event/static/src/js/website_geolocation.js @@ -17,7 +17,7 @@ publicWidget.registry.visitor = publicWidget.Widget.extend({ self.$('.country_events_list').replaceWith(data); } })); - return $.when.apply($, defs); + return Promise.all(defs); }, }); }); From f7917b1502e361701c22899227c5bdbb1cda4565 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:35 +0100 Subject: [PATCH 61/78] [REF] website_form: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_form/static/src/js/website_form.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/website_form/static/src/js/website_form.js b/addons/website_form/static/src/js/website_form.js index a7dcf8446b2..28e7da99046 100644 --- a/addons/website_form/static/src/js/website_form.js +++ b/addons/website_form/static/src/js/website_form.js @@ -13,11 +13,11 @@ odoo.define('website_form.animation', function (require) { selector: '.s_website_form', willStart: function () { - var def; + var prom; if (!$.fn.datetimepicker) { - def = ajax.loadJS("/web/static/lib/tempusdominus/tempusdominus.js"); + prom = ajax.loadJS("/web/static/lib/tempusdominus/tempusdominus.js"); } - return $.when(this._super.apply(this, arguments), def); + return Promise.all([this._super.apply(this, arguments), prom]); }, start: function (editable_mode) { @@ -117,7 +117,7 @@ odoo.define('website_form.animation', function (require) { // Post form and handle result ajax.post(this.$target.attr('action') + (this.$target.data('force_action')||this.$target.data('model_name')), form_values) .then(function (result_data) { - result_data = $.parseJSON(result_data); + result_data = JSON.parse(result_data); if (!result_data.id) { // Failure, the server didn't return the created record ID self.update_status('error'); @@ -139,7 +139,7 @@ odoo.define('website_form.animation', function (require) { self.$target[0].reset(); } }) - .fail(function (result_data){ + .guardedCatch(function (){ self.update_status('error'); }); }, @@ -238,7 +238,7 @@ odoo.define('website_form.animation', function (require) { this.$target.find('.o_website_form_send').on('click',function (e) {self.send(e);}).removeClass('disabled'); } var $result = this.$('#o_website_form_result'); - this.templates_loaded.done(function () { + this.templates_loaded.then(function () { $result.replaceWith(qweb.render("website_form.status_" + status)); }); }, From 3482e3a5a79fa3a950e506fb7afb9d1522c33ff8 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:35 +0100 Subject: [PATCH 62/78] [REF] website_forum: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../website_forum/static/src/js/website_forum.editor.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/website_forum/static/src/js/website_forum.editor.js b/addons/website_forum/static/src/js/website_forum.editor.js index 870878f0331..e2b19ba681f 100644 --- a/addons/website_forum/static/src/js/website_forum.editor.js +++ b/addons/website_forum/static/src/js/website_forum.editor.js @@ -21,7 +21,7 @@ WebsiteNewMenu.include({ * and redirects the user to this new forum. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewForum: function () { var self = this; @@ -42,7 +42,9 @@ WebsiteNewMenu.include({ $add.find('label').append(_t("Add to menu")); $group.after($add); } - }).then(function (forum_name, field, $dialog) { + }).then(function (result) { + var forum_name = result.val; + var $dialog = result.dialog; if (!forum_name) { return; } @@ -55,7 +57,7 @@ WebsiteNewMenu.include({ }, }).then(function (url) { window.location.href = url; - return $.Deferred(); + return new Promise(function () {}); }); }); }, From f0b527591a92e0e0c2adad6c435cb58528169fd3 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:35 +0100 Subject: [PATCH 63/78] [REF] website_gengo: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_gengo/static/src/js/website_gengo.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/website_gengo/static/src/js/website_gengo.js b/addons/website_gengo/static/src/js/website_gengo.js index 9db65555e83..4cbc38e8014 100644 --- a/addons/website_gengo/static/src/js/website_gengo.js +++ b/addons/website_gengo/static/src/js/website_gengo.js @@ -86,7 +86,7 @@ WysiwygTranslate.include({ }).then(function () { ajax.jsonRpc('/website/post_gengo_jobs', 'call', {}); self._save(); - }).fail(function () { + }).guardedCatch(function () { Dialog.alert(null, _t("Could not Post translation")); }); }); @@ -113,7 +113,7 @@ WysiwygTranslate.include({ ajax.jsonRpc('/website/get_translated_length', 'call', { 'translated_ids': translated_ids, 'lang': context.lang, - }).done(function (res){ + }).then(function (res){ var dialog = new GengoTranslatorStatisticDialog(res); dialog.appendTo($(document.body)); }); @@ -202,7 +202,7 @@ var GengoApiConfigDialog = Widget.extend({ 'config': {'gengo_public_key':public_key,'gengo_private_key':private_key,'gengo_auto_approve':auto_approve,'gengo_sandbox':sandbox}, }).then(function () { self.trigger('set_config'); - }).fail(function () { + }).guardedCatch(function () { Dialog.alert(null, _t("Could not submit ! Try Again")); }); } From 6928bfb6c7e7bffff5fc8855838a8b133c324c68 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:36 +0100 Subject: [PATCH 64/78] [REF] website_links: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_links/static/src/js/website_links.js | 4 ++-- .../website_links/static/src/js/website_links_charts.js | 8 +++++++- .../static/src/js/website_links_code_editor.js | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/addons/website_links/static/src/js/website_links.js b/addons/website_links/static/src/js/website_links.js index 5f42892919b..a7c49dfa86c 100644 --- a/addons/website_links/static/src/js/website_links.js +++ b/addons/website_links/static/src/js/website_links.js @@ -37,7 +37,7 @@ var SelectBox = publicWidget.Widget.extend({ return {id: val.id, text: val.name}; }); })); - return $.when.apply($, defs); + return Promise.all(defs); }, /** * @override @@ -376,7 +376,7 @@ publicWidget.registry.websiteLinks = publicWidget.Widget.extend({ $('[data-toggle="tooltip"]').tooltip(); - return $.when.apply($, defs); + return Promise.all(defs); }, //-------------------------------------------------------------------------- diff --git a/addons/website_links/static/src/js/website_links_charts.js b/addons/website_links/static/src/js/website_links_charts.js index c74191d36bf..d4604f1718e 100644 --- a/addons/website_links/static/src/js/website_links_charts.js +++ b/addons/website_links/static/src/js/website_links_charts.js @@ -177,7 +177,13 @@ publicWidget.registry.websiteLinksCharts = publicWidget.Widget.extend({ this.animating_copy = false; - return $.when.apply($, defs).then(function (_totalClicks, _clicksByDay, _clicksByCountry, _lastWeekClicksByCountry, _lastMonthClicksByCountry) { + return Promise.all(defs).then(function (results) { + var _totalClicks = results[0]; + var _clicksByDay = results[1]; + var _clicksByCountry = results[2]; + var _lastWeekClicksByCountry = results[3]; + var _lastMonthClicksByCountry = results[4]; + if (!_totalClicks) { $('#all_time_charts').prepend(_t("There is no data to show")); $('#last_month_charts').prepend(_t("There is no data to show")); diff --git a/addons/website_links/static/src/js/website_links_code_editor.js b/addons/website_links/static/src/js/website_links_code_editor.js index e5ffda16892..b3c05bbf1a6 100644 --- a/addons/website_links/static/src/js/website_links_code_editor.js +++ b/addons/website_links/static/src/js/website_links_code_editor.js @@ -43,7 +43,7 @@ publicWidget.registry.websiteLinksCodeEditor = publicWidget.Widget.extend({ }, /** * @private - * @returns {Deferred} + * @returns {Promise} */ _submitCode: function () { var initCode = $('#edit-code-form #init_code').val(); @@ -75,7 +75,7 @@ publicWidget.registry.websiteLinksCodeEditor = publicWidget.Widget.extend({ }); } - return $.when(); + return Promise.resolve(); }, //-------------------------------------------------------------------------- From bd88240d3fc981913a81f193882afc2e7c86bc2a Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:36 +0100 Subject: [PATCH 65/78] [REF] website_livechat: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/website_livechat.editor.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/website_livechat/static/src/js/website_livechat.editor.js b/addons/website_livechat/static/src/js/website_livechat.editor.js index 2ec5bc2a793..288f2af0e97 100644 --- a/addons/website_livechat/static/src/js/website_livechat.editor.js +++ b/addons/website_livechat/static/src/js/website_livechat.editor.js @@ -21,14 +21,15 @@ WebsiteNewMenu.include({ * and redirects the user to this new channel. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewChannel: function () { var self = this; return wUtils.prompt({ window_title: _t("New Channel"), input: _t("Name"), - }).then(function (name) { + }).then(function (result) { + var name = result.val; if (!name) { return; } @@ -41,7 +42,7 @@ WebsiteNewMenu.include({ }, }).then(function (url) { window.location.href = url; - return $.Deferred(); + return new Promise(function () {}); }); }); }, From 5413686837e810925234e339724da13fba684790 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:36 +0100 Subject: [PATCH 66/78] [REF] website_mail: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_mail/static/src/js/follow.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/addons/website_mail/static/src/js/follow.js b/addons/website_mail/static/src/js/follow.js index 8e6f18e8139..a6b4dcc6777 100644 --- a/addons/website_mail/static/src/js/follow.js +++ b/addons/website_mail/static/src/js/follow.js @@ -10,18 +10,21 @@ publicWidget.registry.follow = publicWidget.Widget.extend({ start: function () { var self = this; this.is_user = false; + + var always = function (data) { + self.is_user = data.is_user; + self.email = data.email; + self.toggle_subscription(data.is_follower, data.email); + self.$target.removeClass('d-none'); + }; + this._rpc({ route: '/website_mail/is_follower', params: { model: this.$target.data('object'), res_id: this.$target.data('id'), }, - }).always(function (data) { - self.is_user = data.is_user; - self.email = data.email; - self.toggle_subscription(data.is_follower, data.email); - self.$target.removeClass('d-none'); - }); + }).then(always).guardedCatch(always); // not if editable mode to allow designer to edit if (!this.editableMode) { From ff1c735e1e5943f1dad255aedd304628999e7830 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:36 +0100 Subject: [PATCH 67/78] [REF] website_mail_channel: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../src/js/website_mail_channel.editor.js | 6 ++--- .../src/js/website_mail_channel.snippet.js | 25 +++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/addons/website_mail_channel/static/src/js/website_mail_channel.editor.js b/addons/website_mail_channel/static/src/js/website_mail_channel.editor.js index 622451f2857..1ad5be9f8f0 100644 --- a/addons/website_mail_channel/static/src/js/website_mail_channel.editor.js +++ b/addons/website_mail_channel/static/src/js/website_mail_channel.editor.js @@ -21,14 +21,14 @@ options.registry.subscribe = options.Class.extend({ args: ['', [['public', '=', 'public']]], }); }, - }).then(function (mail_channel_id) { - self.$target.attr("data-id", mail_channel_id); + }).then(function (result) { + self.$target.attr("data-id", result.val); }); }, onBuilt: function () { var self = this; this._super(); - this.select_mailing_list("click").fail(function () { + this.select_mailing_list("click").guardedCatch(function () { self.getParent().removeSnippet(); }); }, diff --git a/addons/website_mail_channel/static/src/js/website_mail_channel.snippet.js b/addons/website_mail_channel/static/src/js/website_mail_channel.snippet.js index cb9e32f62b2..1b7635a10bc 100644 --- a/addons/website_mail_channel/static/src/js/website_mail_channel.snippet.js +++ b/addons/website_mail_channel/static/src/js/website_mail_channel.snippet.js @@ -10,14 +10,8 @@ publicWidget.registry.follow_alias = publicWidget.Widget.extend({ var self = this; this.is_user = false; var unsubscribePage = window.location.search.slice(1).split('&').indexOf("unsubscribe") >= 0; - this._rpc({ - route: '/groups/is_member', - params: { - model: this.$target.data('object'), - channel_id: this.$target.data('id'), - get_alias_info: true, - }, - }).always(function (data) { + + var always = function (data) { self.is_user = data.is_user; self.email = data.email; self.$target.find('.js_mg_link').attr('href', '/groups/' + self.$target.data('id')); @@ -26,7 +20,16 @@ publicWidget.registry.follow_alias = publicWidget.Widget.extend({ } self.toggle_subscription(data.is_member ? 'on' : 'off', data.email); self.$target.removeClass('d-none'); - }); + }; + + this._rpc({ + route: '/groups/is_member', + params: { + model: this.$target.data('object'), + channel_id: this.$target.data('id'), + get_alias_info: true, + }, + }).then(always).guardedCatch(always); // not if editable mode to allow designer to edit alert field if (!this.editableMode) { @@ -99,12 +102,12 @@ publicWidget.registry.follow_alias = publicWidget.Widget.extend({ .val(email ? email : "") .attr("disabled", follow === "on" || (email.length && this.is_user) ? "disabled" : false); this.$target.attr("data-follow", follow); - return $.when(alias_done); + return Promise.resolve(alias_done); }, get_alias_info: function () { var self = this; if (! this.$target.data('id')) { - return $.Deferred().resolve(); + return Promise.resolve(); } return this._rpc({route: '/groups/' + this.$target.data('id') + '/get_alias_info'}).then(function (data) { if (data.alias_name) { From fdaa417165354b8173a895b28c0d4dd5c699ce53 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:36 +0100 Subject: [PATCH 68/78] [REF] website_mass_mailing: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../src/js/website_mass_mailing.editor.js | 6 +++--- .../static/src/js/website_mass_mailing.js | 20 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js index e980cd0b048..3506981e835 100644 --- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js @@ -28,15 +28,15 @@ var mass_mailing_common = options.Class.extend({ }); }, }); - def.then(function (mailing_list_id) { - self.$target.attr("data-list-id", mailing_list_id); + def.then(function (result) { + self.$target.attr("data-list-id", result.val); }); return def; }, onBuilt: function () { var self = this; this._super(); - this.select_mailing_list('click').fail(function () { + this.select_mailing_list('click').guardedCatch(function () { self.getParent()._onRemoveClick($.Event( "click" )); }); }, diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js index e11b8c822c5..abd7988821b 100644 --- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js @@ -11,14 +11,7 @@ publicWidget.registry.subscribe = publicWidget.Widget.extend({ start: function () { var self = this; - // set value and display button - self.$target.find("input").removeClass('d-none'); - this._rpc({ - route: '/website_mass_mailing/is_subscriber', - params: { - list_id: this.$target.data('list-id'), - }, - }).always(function (data) { + var always = function (data) { self.$target.find('input.js_subscribe_email') .val(data.email ? data.email : "") .attr("disabled", data.is_subscriber && data.email.length ? "disabled" : false); @@ -28,7 +21,16 @@ publicWidget.registry.subscribe = publicWidget.Widget.extend({ self.$target.removeClass('d-none'); self.$target.find('.js_subscribe_btn').toggleClass('d-none', !!data.is_subscriber); self.$target.find('.js_subscribed_btn').toggleClass('d-none', !data.is_subscriber); - }); + }; + + // set value and display button + self.$target.find("input").removeClass('d-none'); + this._rpc({ + route: '/website_mass_mailing/is_subscriber', + params: { + list_id: this.$target.data('list-id'), + }, + }).then(always).guardedCatch(always); // not if editable mode to allow designer to edit alert field if (!this.editableMode) { From b06dacd35899328f6a167f97118931ea47b21934 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:37 +0100 Subject: [PATCH 69/78] [REF] website_rating: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_rating/static/src/js/website_mail.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/website_rating/static/src/js/website_mail.js b/addons/website_rating/static/src/js/website_mail.js index 15b9b4ce503..4faa0fbb306 100644 --- a/addons/website_rating/static/src/js/website_mail.js +++ b/addons/website_rating/static/src/js/website_mail.js @@ -332,7 +332,7 @@ var RatingPopupComposer = publicWidget.Widget.extend({ this._composer = new PortalComposer(this, this.options); defs.push(this._composer.replace(this.$('.o_portal_chatter_composer'))); - return $.when.apply($, defs); + return Promise.all(defs); }, }); @@ -344,10 +344,10 @@ publicWidget.registry.RatingPopupComposer = publicWidget.Widget.extend({ */ start: function () { var ratingPopup = new RatingPopupComposer(this, this.$el.data()); - return $.when( + return Promise.all([ this._super.apply(this, arguments), ratingPopup.appendTo(this.$el) - ); + ]); }, }); }); From d093d1aebae74706a0e68bbb41b8a1be93e008ac Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:37 +0100 Subject: [PATCH 70/78] [REF] website_sale: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/website_sale.editor.js | 12 ++++++------ .../static/src/js/website_sale_options.js | 4 +++- .../static/src/js/website_sale_tour_shop_mail.js | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index c935602d83e..8c6cd07d3ea 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -21,7 +21,7 @@ WebsiteNewMenu.include({ * and redirects the user to this new product. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewProduct: function () { var self = this; @@ -29,18 +29,18 @@ WebsiteNewMenu.include({ id: "editor_new_product", window_title: _t("New Product"), input: _t("Name"), - }).then(function (name) { - if (!name) { + }).then(function (result) { + if (!result.val) { return; } return self._rpc({ route: '/shop/add_product', params: { - name: name, + name: result.val, }, }).then(function (url) { window.location.href = url; - return $.Deferred(); + return new Promise(function () {}); }); }); }, @@ -56,7 +56,7 @@ require('web.dom_ready'); var options = require('web_editor.snippets.options'); if (!$('.js_sale').length) { - return $.Deferred().reject("DOM doesn't contain '.js_sale'"); + return Promise.reject("DOM doesn't contain '.js_sale'"); } $('.oe_website_sale').on('click', '.oe_currency_value:o_editable', function (ev) { diff --git a/addons/website_sale/static/src/js/website_sale_options.js b/addons/website_sale/static/src/js/website_sale_options.js index c9c71053126..5e55afe0e18 100644 --- a/addons/website_sale/static/src/js/website_sale_options.js +++ b/addons/website_sale/static/src/js/website_sale_options.js @@ -69,7 +69,7 @@ publicWidget.registry.WebsiteSaleOptions = publicWidget.Widget.extend(ProductCon false ); - return productReady.done(function (productId) { + productReady.then(function (productId){ $form.find(productSelector.join(', ')).val(productId); self.rootProduct = { @@ -96,6 +96,8 @@ publicWidget.registry.WebsiteSaleOptions = publicWidget.Widget.extend(ProductCon return self.optionalProductsModal.opened(); }); + + return productReady; }, /** diff --git a/addons/website_sale/static/src/js/website_sale_tour_shop_mail.js b/addons/website_sale/static/src/js/website_sale_tour_shop_mail.js index 0d8fe0131f0..d699db43189 100644 --- a/addons/website_sale/static/src/js/website_sale_tour_shop_mail.js +++ b/addons/website_sale/static/src/js/website_sale_tour_shop_mail.js @@ -34,7 +34,7 @@ tour.register('shop_mail', { 'domain': "https://domain-not-used.fr", }], }); - $.when(def1, def2).then(function (data) { + Promise.all([def1, def2]).then(function (data) { window.location.href = '/shop?search=Acoustic Bloc Screens'; }); }, From 4032be506a0b385e9c198d64f79ca02cf03238e8 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:37 +0100 Subject: [PATCH 71/78] [REF] website_sale_comparison: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/website_sale_comparison.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js index f92d517272e..9410db2c21a 100644 --- a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js +++ b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js @@ -95,14 +95,12 @@ var ProductComparison = publicWidget.Widget.extend(ProductConfiguratorMixin, { } } - var productReady = this.selectOrCreateProduct( + this.selectOrCreateProduct( $elem.closest('form'), productId, $elem.closest('form').find('.product_template_id').val(), false - ); - - productReady.done(function (productId) { + ).then(function (productId) { productId = parseInt(productId, 10); if (!productId) { @@ -247,7 +245,7 @@ publicWidget.registry.ProductComparison = publicWidget.Widget.extend({ start: function () { var def = this._super.apply(this, arguments); this.productComparison = new ProductComparison(this); - return $.when(def, this.productComparison.appendTo(this.$el)); + return Promise.all([def, this.productComparison.appendTo(this.$el)]); }, //-------------------------------------------------------------------------- From a74169bb09a7c3089fd36ebec866555cf04a2a75 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:38 +0100 Subject: [PATCH 72/78] [REF] website_sale_wishlist: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../static/src/js/website_sale_wishlist.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js index 59172ac6457..53f821c28c1 100644 --- a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js +++ b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js @@ -42,7 +42,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(ProductConfig self.wishlistProductIDs = JSON.parse(res); }); - return $.when(def, wishDef); + return Promise.all([def, wishDef]); }, /** * Updates the wishlist view (navbar) & the wishlist button (product page). @@ -95,7 +95,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(ProductConfig false ); - productReady.done(function (productId) { + productReady.then(function (productId) { productId = parseInt(productId, 10); if (productId && !_.contains(self.wishlistProductIDs, productId)) { @@ -108,11 +108,11 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(ProductConfig self.wishlistProductIDs.push(productId); self._updateWishlistView(); wSaleUtils.animateClone($('#my_wish'), $el.closest('form'), 25, 40); - }).fail(function () { + }).guardedCatch(function () { $el.prop("disabled", false).removeClass('disabled'); }); } - }).fail(function () { + }).guardedCatch(function () { $el.prop("disabled", false).removeClass('disabled'); }); }, @@ -138,16 +138,17 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(ProductConfig this._rpc({ route: '/shop/wishlist/remove/' + wish, - }).done(function () { + }).then(function () { $(tr).hide(); }); this.wishlistProductIDs = _.without(this.wishlistProductIDs, product); if (this.wishlistProductIDs.length === 0) { - deferred_redirect = deferred_redirect ? deferred_redirect : $.Deferred(); - deferred_redirect.then(function () { - self._redirectNoWish(); - }); + if (deferred_redirect) { + deferred_redirect.then(function () { + self._redirectNoWish(); + }); + } } this._updateWishlistView(); }, From 4d1927caef3e29b5f946a6ef4bd1214da42a9acc Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:38 +0100 Subject: [PATCH 73/78] [REF] website_slides: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/website_slides/static/src/js/slides.js | 4 ++-- .../static/src/js/slides_course_fullscreen_player.js | 9 +++------ .../static/src/js/slides_course_join.js | 6 +++--- addons/website_slides/static/src/js/slides_embed.js | 2 +- addons/website_slides/static/src/js/slides_share.js | 2 +- .../static/src/js/slides_slide_like.js | 2 +- addons/website_slides/static/src/js/slides_upload.js | 2 +- .../static/src/js/website_slides.editor.js | 12 ++++++------ 8 files changed, 18 insertions(+), 21 deletions(-) diff --git a/addons/website_slides/static/src/js/slides.js b/addons/website_slides/static/src/js/slides.js index ca4cb1dc609..4f081013de1 100644 --- a/addons/website_slides/static/src/js/slides.js +++ b/addons/website_slides/static/src/js/slides.js @@ -28,7 +28,7 @@ publicWidget.registry.websiteSlides = publicWidget.Widget.extend({ $(el).text(displayStr); }); - return $.when.apply($, defs); + return Promise.all(defs); }, }); @@ -101,7 +101,7 @@ publicWidget.registry.websiteSlidesEmbed = publicWidget.Widget.extend({ start: function (parent) { var defs = [this._super.apply(this, arguments)]; $('iframe.o_wslides_iframe_viewer').on('ready', this._onIframeViewerReady.bind(this)); - return $.when.apply($, defs); + return Promise.all(defs); }, //-------------------------------------------------------------------------- diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js index 5e6781e8f0a..fc37eece601 100644 --- a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js +++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js @@ -410,17 +410,14 @@ odoo.define('website_slides.fullscreen', function (require) { publicWidget.registry.websiteSlidesFullscreenPlayer = publicWidget.Widget.extend({ selector: '.o_wslides_fs_main', xmlDependencies: ['/website_slides/static/src/xml/website_slides_fullscreen.xml'], - init: function (el){ - this._super.apply(this, arguments); - }, start: function (){ - var defs = [this._super.apply(this, arguments)]; + var proms = [this._super.apply(this, arguments)]; var userId = this.$el.data('userId'); var courseId = this.$el.data('courseId'); var slideId = this.$el.data('slideId'); var fullscreen = new Fullscreen(this, courseId, slideId, userId); - defs.push(fullscreen.attachTo(this.$el)); - return $.when.apply($, defs); + proms.push(fullscreen.attachTo(this.$el)); + return Promise.all(proms); } }); diff --git a/addons/website_slides/static/src/js/slides_course_join.js b/addons/website_slides/static/src/js/slides_course_join.js index c45cb13ab1b..cdd8cc531cd 100644 --- a/addons/website_slides/static/src/js/slides_course_join.js +++ b/addons/website_slides/static/src/js/slides_course_join.js @@ -73,11 +73,11 @@ publicWidget.registry.websiteSlidesCourseJoin = publicWidget.Widget.extend({ */ start: function () { var self = this; - var defs = [this._super.apply(this, arguments)]; + var proms = [this._super.apply(this, arguments)]; $('.o_wslides_js_course_join').each(function () { - defs.push(new CourseJoinWidget(self).attachTo($(this))); + proms.push(new CourseJoinWidget(self).attachTo($(this))); }); - return $.when.apply($, defs); + return Promise.all(proms); }, }); diff --git a/addons/website_slides/static/src/js/slides_embed.js b/addons/website_slides/static/src/js/slides_embed.js index edcaca7eccb..b2b19b98e03 100644 --- a/addons/website_slides/static/src/js/slides_embed.js +++ b/addons/website_slides/static/src/js/slides_embed.js @@ -4,7 +4,7 @@ * a slide/video/document. This code can depend on pdf.js, JQuery and Bootstrap * (see website_slides.slide_embed_assets bundle, in website_slides_embed.xml) */ -$(document).ready(function () { +$(function () { if ($('#PDFViewer') && $('#PDFViewerCanvas')) { // check if presentation only diff --git a/addons/website_slides/static/src/js/slides_share.js b/addons/website_slides/static/src/js/slides_share.js index 73fd165545b..81ba1e4a806 100644 --- a/addons/website_slides/static/src/js/slides_share.js +++ b/addons/website_slides/static/src/js/slides_share.js @@ -67,7 +67,7 @@ publicWidget.registry.websiteSlidesShare = publicWidget.Widget.extend({ self._updateStatistics(key, self.slideURL); }); - return $.when.apply($, defs); + return Promise.all(defs); }, //-------------------------------------------------------------------------- diff --git a/addons/website_slides/static/src/js/slides_slide_like.js b/addons/website_slides/static/src/js/slides_slide_like.js index 406368ded25..5d343c72579 100644 --- a/addons/website_slides/static/src/js/slides_slide_like.js +++ b/addons/website_slides/static/src/js/slides_slide_like.js @@ -89,7 +89,7 @@ publicWidget.registry.websiteSlidesSlideLike = publicWidget.Widget.extend({ $('.o_wslides_js_slide_like').each(function () { defs.push(new SlideLikeWidget(self).attachTo($(this))); }); - return $.when.apply($, defs); + return Promise.all(defs); }, }); diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index 602b96992cb..ad1e024d2bf 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -241,7 +241,7 @@ var SlideUploadDialog = Dialog.extend({ * @private * @param {String} Placeholder for element. * @param {bool} true for multiple selection box, false for single selection - * @param {Function} Function to fetch data from remote location should return $.deferred object + * @param {Function} Function to fetch data from remote location should return a Promise * resolved data should be array of object with id and name. eg. [{'id': id, 'name': 'text'}, ...] * @param {String} [nameKey='name'] (optional) the name key of the returned record * ('name' if not provided) diff --git a/addons/website_slides/static/src/js/website_slides.editor.js b/addons/website_slides/static/src/js/website_slides.editor.js index 72a18bb93ee..0a997460d56 100644 --- a/addons/website_slides/static/src/js/website_slides.editor.js +++ b/addons/website_slides/static/src/js/website_slides.editor.js @@ -97,14 +97,14 @@ WebsiteNewMenu.include({ * and redirects the user to this channel. * * @private - * @returns {Deferred} Unresolved if there is a redirection + * @returns {Promise} Unresolved if there is a redirection */ _createNewSlideChannel: function () { - var def = $.Deferred(); - var dialog = new ChannelCreateDialog(this, {}); - dialog.open(); - dialog.on('closed', this, function() { - def.resolve(); + var self = this; + var def = new Promise(function (resolve) { + var dialog = new ChannelCreateDialog(self, {}); + dialog.open(); + dialog.on('closed', self, resolve); }); return def; }, From 4721de57df25eab1ebf590268f85c0c8cb5b2d2a Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:39 +0100 Subject: [PATCH 74/78] [REF] website_twitter: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- .../website_twitter/static/src/js/website.twitter.animation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_twitter/static/src/js/website.twitter.animation.js b/addons/website_twitter/static/src/js/website.twitter.animation.js index 26c4ca78927..e0c54bcb9d7 100644 --- a/addons/website_twitter/static/src/js/website.twitter.animation.js +++ b/addons/website_twitter/static/src/js/website.twitter.animation.js @@ -99,7 +99,7 @@ publicWidget.registry.twitter = publicWidget.Widget.extend({ self._startScrolling(); }); - return $.when(this._super.apply(this, arguments), def); + return Promise.all([this._super.apply(this, arguments), def]); }, /** * @override From 7cb64b5e93279b9c654d2cb035f895e59adbddda Mon Sep 17 00:00:00 2001 From: David Monjoie Date: Fri, 1 Mar 2019 15:55:43 +0100 Subject: [PATCH 75/78] [REF] doc: promisify doc examples Also remove a file on async stuff as its content is outdated, and actually not used. --- doc/_extensions/odoo_ext/static/doc.js | 2 +- doc/howtos/web.rst | 4 +- doc/reference/async.rst | 341 ------------------------- doc/reference/javascript_reference.rst | 14 +- 4 files changed, 10 insertions(+), 351 deletions(-) delete mode 100644 doc/reference/async.rst diff --git a/doc/_extensions/odoo_ext/static/doc.js b/doc/_extensions/odoo_ext/static/doc.js index 0424d8e0963..af0a1fe0f37 100644 --- a/doc/_extensions/odoo_ext/static/doc.js +++ b/doc/_extensions/odoo_ext/static/doc.js @@ -1,6 +1,6 @@ (function ($) { - $(document).ready(function () { + $(function () { // -- Initialize fallbacks for requestAnimationFrame window.requestAnimationFrame = window.requestAnimationFrame diff --git a/doc/howtos/web.rst b/doc/howtos/web.rst index df44ff25eba..550ffeffea1 100644 --- a/doc/howtos/web.rst +++ b/doc/howtos/web.rst @@ -1544,10 +1544,10 @@ Exercises local.HomePage = instance.Widget.extend({ template: "HomePage", start: function () { - return $.when( + return Promise.all([ new local.PetToysList(this).appendTo(this.$('.oe_petstore_homepage_left')), new local.MessageOfTheDay(this).appendTo(this.$('.oe_petstore_homepage_right')) - ); + ]); } }); instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage'); diff --git a/doc/reference/async.rst b/doc/reference/async.rst deleted file mode 100644 index d8d9c7db90f..00000000000 --- a/doc/reference/async.rst +++ /dev/null @@ -1,341 +0,0 @@ -:orphan: - -.. _reference/async: - -Asynchronous Operations -======================= - -As a language (and runtime), javascript is fundamentally -single-threaded. This means any blocking request or computation will -block the whole page (and, in older browsers, the software itself -even preventing users from switching to another tab): a javascript -environment can be seen as an event-based runloop where application -developers have no control over the runloop itself. - -As a result, performing long-running synchronous network requests or -other types of complex and expensive accesses is frowned upon and -asynchronous APIs are used instead. - -The goal of this guide is to provide some tools to deal with -asynchronous systems, and warn against systemic issues or dangers. - -Deferreds ---------- - -Deferreds are a form of `promises`_. OpenERP Web currently uses -`jQuery's deferred`_. - -The core idea of deferreds is that potentially asynchronous methods -will return a :js:class:`Deferred` object instead of an arbitrary -value or (most commonly) nothing. - -This object can then be used to track the end of the asynchronous -operation by adding callbacks onto it, either success callbacks or -error callbacks. - -A great advantage of deferreds over simply passing callback functions -directly to asynchronous methods is the ability to :ref:`compose them -`. - -Using deferreds -~~~~~~~~~~~~~~~ - -Deferreds's most important method is :js:func:`Deferred.then`. It is -used to attach new callbacks to the deferred object. - -* the first parameter attaches a success callback, called when the - deferred object is successfully resolved and provided with the - resolved value(s) for the asynchronous operation. - -* the second parameter attaches a failure callback, called when the - deferred object is rejected and provided with rejection values - (often some sort of error message). - -Callbacks attached to deferreds are never "lost": if a callback is -attached to an already resolved or rejected deferred, the callback -will be called (or ignored) immediately. A deferred is also only ever -resolved or rejected once, and is either resolved or rejected: a given -deferred can not call a single success callback twice, or call both a -success and a failure callbacks. - -:js:func:`~Deferred.then` should be the method you'll use most often -when interacting with deferred objects (and thus asynchronous APIs). - -Building deferreds -~~~~~~~~~~~~~~~~~~ - -After using asynchronous APIs may come the time to build them: for -mocks_, to compose deferreds from multiple source in a complex -manner, in order to let the current operations repaint the screen or -give other events the time to unfold, ... - -This is easy using jQuery's deferred objects. - -.. note:: this section is an implementation detail of jQuery Deferred - objects, the creation of promises is not part of any - standard (even tentative) that I know of. If you are using - deferred objects which are not jQuery's, their API may (and - often will) be completely different. - -Deferreds are created by invoking their constructor [#]_ without any -argument. This creates a :js:class:`Deferred` instance object with the -following methods: - -:js:func:`Deferred.resolve` - - As its name indicates, this method moves the deferred to the - "Resolved" state. It can be provided as many arguments as - necessary, these arguments will be provided to any pending success - callback. - -:js:func:`Deferred.reject` - - Similar to :js:func:`~Deferred.resolve`, but moves the deferred to - the "Rejected" state and calls pending failure handlers. - -:js:func:`Deferred.promise` - - Creates a readonly view of the deferred object. It is generally a - good idea to return a promise view of the deferred to prevent - callers from resolving or rejecting the deferred in your stead. - -:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used -to inform callers that the asynchronous operation has failed (or -succeeded). These methods should simply be called when the -asynchronous operation has ended, to notify anybody interested in its -result(s). - -.. _reference/async/composition: - -Composing deferreds -~~~~~~~~~~~~~~~~~~~ - -What we've seen so far is pretty nice, but mostly doable by passing -functions to other functions (well adding functions post-facto would -probably be a chore... still, doable). - -Deferreds truly shine when code needs to compose asynchronous -operations in some way or other, as they can be used as a basis for -such composition. - -There are two main forms of compositions over deferred: multiplexing -and piping/cascading. - -Deferred multiplexing -````````````````````` - -The most common reason for multiplexing deferred is simply performing -multiple asynchronous operations and wanting to wait until all of them are -done before moving on (and executing more stuff). - -The jQuery multiplexing function for promises is :js:func:`when`. - -.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an - (incompatible, mostly) extension of the behavior defined in - `CommonJS Promises/B`_. - -This function can take any number of promises [#]_ and will return a -promise. - -The returned promise will be resolved when *all* multiplexed promises -are resolved, and will be rejected as soon as one of the multiplexed -promises is rejected (it behaves like Python's ``all()``, but with -promise objects instead of boolean-ish). - -The resolved values of the various promises multiplexed via -:js:func:`when` are mapped to the arguments of :js:func:`when`'s -success callback, if they are needed. The resolved values of a promise -are at the same index in the callback's arguments as the promise in -the :js:func:`when` call so you will have: - -.. code-block:: javascript - - $.when(p0, p1, p2, p3).then( - function (results0, results1, results2, results3) { - // code - }); - -.. warning:: - - in a normal mapping, each parameter to the callback would be an - array: each promise is conceptually resolved with an array of 0..n - values and these values are passed to :js:func:`when`'s - callback. But jQuery treats deferreds resolving a single value - specially, and "unwraps" that value. - - For instance, in the code block above if the index of each promise - is the number of values it resolves (0 to 3), ``results0`` is an - empty array, ``results2`` is an array of 2 elements (a pair) but - ``results1`` is the actual value resolved by ``p1``, not an array. - -Deferred chaining -````````````````` - -A second useful composition is starting an asynchronous operation as -the result of an other asynchronous operation, and wanting the result -of both: with the tools described so far, handling e.g. OpenERP's -search/read sequence with this would require something along the lines -of: - -.. code-block:: javascript - - var result = $.Deferred(); - Model.search(condition).then(function (ids) { - Model.read(ids, fields).then(function (records) { - result.resolve(records); - }); - }); - return result.promise(); - -While it doesn't look too bad for trivial code, this quickly gets -unwieldy. - -But :js:func:`~Deferred.then` also allows handling this kind of -chains: it returns a new promise object, not the one it was called -with, and the return values of the callbacks is important to this behavior: -whichever callback is called, - -* If the callback is not set (not provided or left to null), the - resolution or rejection value(s) is simply forwarded to - :js:func:`~Deferred.then`'s promise (it's essentially a noop) - -* If the callback is set and does not return an observable object (a - deferred or a promise), the value it returns (``undefined`` if it - does not return anything) will replace the value it was given, e.g. - - .. code-block:: javascript - - promise.then(function () { - console.log('called'); - }); - - will resolve with the sole value ``undefined``. - -* If the callback is set and returns an observable object, that object - will be the actual resolution (and result) of the pipe. This means a - resolved promise from the failure callback will resolve the pipe, - and a failure promise from the success callback will reject the - pipe. - - This provides an easy way to chain operation successes, and the - previous piece of code can now be rewritten: - - .. code-block:: javascript - - return Model.search(condition).then(function (ids) { - return Model.read(ids, fields); - }); - - the result of the whole expression will encode failure if either - ``search`` or ``read`` fails (with the right rejection values), and - will be resolved with ``read``'s resolution values if the chain - executes correctly. - -:js:func:`~Deferred.then` is also useful to adapt third-party -promise-based APIs, in order to filter their resolution value counts -for instance (to take advantage of :js:func:`when` 's special -treatment of single-value promises). - -jQuery.Deferred API -~~~~~~~~~~~~~~~~~~~ - -.. js:function:: when(deferreds…) - - :param deferreds: deferred objects to multiplex - :returns: a multiplexed deferred - :rtype: :js:class:`Deferred` - -.. js:class:: Deferred - - .. js:function:: Deferred.then(doneCallback[, failCallback]) - - Attaches new callbacks to the resolution or rejection of the - deferred object. Callbacks are executed in the order they are - attached to the deferred. - - To provide only a failure callback, pass ``null`` as the - ``doneCallback``, to provide only a success callback the - second argument can just be ignored (and not passed at all). - - Returns a new deferred which resolves to the result of the - corresponding callback, if a callback returns a deferred - itself that new deferred will be used as the resolution of the - chain. - - :param doneCallback: function called when the deferred is resolved - :param failCallback: function called when the deferred is rejected - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.done(doneCallback) - - Attaches a new success callback to the deferred, shortcut for - ``deferred.then(doneCallback)``. - - .. note:: a difference is the result of :js:func:`Deferred.done`'s - is ignored rather than forwarded through the chain - - This is a jQuery extension to `CommonJS Promises/A`_ providing - little value over calling :js:func:`~Deferred.then` directly, - it should be avoided. - - :param doneCallback: function called when the deferred is resolved - :type doneCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.fail(failCallback) - - Attaches a new failure callback to the deferred, shortcut for - ``deferred.then(null, failCallback)``. - - A second jQuery extension to `Promises/A `_. Although it provides more value than - :js:func:`~Deferred.done`, it still is not much and should be - avoided as well. - - :param failCallback: function called when the deferred is rejected - :type failCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.promise() - - Returns a read-only view of the deferred object, with all - mutators (resolve and reject) methods removed. - - .. js:function:: Deferred.resolve(value…) - - Called to resolve a deferred, any value provided will be - passed onto the success handlers of the deferred object. - - Resolving a deferred which has already been resolved or - rejected has no effect. - - .. js:function:: Deferred.reject(value…) - - Called to reject (fail) a deferred, any value provided will be - passed onto the failure handler of the deferred object. - - Rejecting a deferred which has already been resolved or - rejected has no effect. - -.. [#] or simply calling :js:class:`Deferred` as a function, the - result is the same - -.. [#] or not-promises, the `CommonJS Promises/B`_ role of - :js:func:`when` is to be able to treat values and promises - uniformly: :js:func:`when` will pass promises through directly, - but non-promise values and objects will be transformed into a - resolved promise (resolving themselves with the value itself). - - jQuery's :js:func:`when` keeps this behavior making deferreds - easy to build from "static" values, or allowing defensive code - where expected promises are wrapped in :js:func:`when` just in - case. - -.. _promises: http://en.wikipedia.org/wiki/Promise_(programming) -.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/ -.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A -.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B -.. _mocks: http://en.wikipedia.org/wiki/Mock_object diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index 059b5796041..02c477905d5 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -879,7 +879,7 @@ Here is an example on how this event system could be used: this.counter = new Counter(this); this.counter.on('valuechange', this, this._onValueChange); var def = this.counter.appendTo(this.$el); - return $.when(def, this._super.apply(this, arguments); + return Promise.all([def, this._super.apply(this, arguments)]); }, _onValueChange: function (val) { // do something with val @@ -1500,15 +1500,15 @@ order. - type: setting the input type (*text* by default, can be set on *number*) - On edit mode, the field is rendered as an input with the HTML attribute type - setted on *number* (so user can benefit the native support, especially on + On edit mode, the field is rendered as an input with the HTML attribute type + setted on *number* (so user can benefit the native support, especially on mobile). In this case, the default formatting is disabled to avoid incompability. .. code-block:: xml - - step: set the step to the value up and down when the user click on buttons + - step: set the step to the value up and down when the user click on buttons (only for input of type number, 1 by default) .. code-block:: xml @@ -1532,15 +1532,15 @@ order. - type: setting the input type (*text* by default, can be set on *number*) - On edit mode, the field is rendered as an input with the HTML attribute type - setted on *number* (so user can benefit the native support, especially on + On edit mode, the field is rendered as an input with the HTML attribute type + setted on *number* (so user can benefit the native support, especially on mobile). In this case, the default formatting is disabled to avoid incompability. .. code-block:: xml - - step: set the step to the value up and down when the user click on buttons + - step: set the step to the value up and down when the user click on buttons (only for input of type number, 1 by default) .. code-block:: xml From 3d3db1f035c454ef7247f9a525bc93d2ba9ab540 Mon Sep 17 00:00:00 2001 From: David Monjoie Date: Wed, 27 Feb 2019 15:01:59 +0100 Subject: [PATCH 76/78] [FIX] web_editor: insert assets in head instead of body This made the test suite fail as there are leftovers in the body after tests. --- addons/web_editor/views/editor.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_editor/views/editor.xml b/addons/web_editor/views/editor.xml index 6bbf98f9b30..7f68d0be5d8 100644 --- a/addons/web_editor/views/editor.xml +++ b/addons/web_editor/views/editor.xml @@ -138,7 +138,7 @@