(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["OptimalSelect"] = factory(); else root["OptimalSelect"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 6); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertNodeList = convertNodeList; exports.escapeValue = escapeValue; /** * # Utilities * * Convenience helpers. */ /** * Create an array with the DOM nodes of the list * * @param {NodeList} nodes - [description] * @return {Array.} - [description] */ function convertNodeList(nodes) { var length = nodes.length; var arr = new Array(length); for (var i = 0; i < length; i++) { arr[i] = nodes[i]; } return arr; } /** * Escape special characters and line breaks as a simplified version of 'CSS.escape()' * * Description of valid characters: https://mathiasbynens.be/notes/css-escapes * * @param {String?} value - [description] * @return {String} - [description] */ function escapeValue(value) { return value && value.replace(/['"`\\/:\?&!#$%^()[\]{|}*+;,.<=>@~]/g, '\\$&').replace(/\n/g, '\A'); } /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCommonAncestor = getCommonAncestor; exports.getCommonProperties = getCommonProperties; /** * # Common * * Process collections for similarities. */ /** * Find the last common ancestor of elements * * @param {Array.} elements - [description] * @return {HTMLElement} - [description] */ function getCommonAncestor(elements) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _options$root = options.root, root = _options$root === undefined ? document : _options$root; var ancestors = []; elements.forEach(function (element, index) { var parents = []; while (element !== root) { element = element.parentNode; parents.unshift(element); } ancestors[index] = parents; }); ancestors.sort(function (curr, next) { return curr.length - next.length; }); var shallowAncestor = ancestors.shift(); var ancestor = null; var _loop = function _loop() { var parent = shallowAncestor[i]; var missing = ancestors.some(function (otherParents) { return !otherParents.some(function (otherParent) { return otherParent === parent; }); }); if (missing) { // TODO: find similar sub-parents, not the top root, e.g. sharing a class selector return 'break'; } ancestor = parent; }; for (var i = 0, l = shallowAncestor.length; i < l; i++) { var _ret = _loop(); if (_ret === 'break') break; } return ancestor; } /** * Get a set of common properties of elements * * @param {Array.} elements - [description] * @return {Object} - [description] */ function getCommonProperties(elements) { var commonProperties = { classes: [], attributes: {}, tag: null }; elements.forEach(function (element) { var commonClasses = commonProperties.classes, commonAttributes = commonProperties.attributes, commonTag = commonProperties.tag; // ~ classes if (commonClasses !== undefined) { var classes = element.getAttribute('class'); if (classes) { classes = classes.trim().split(' '); if (!commonClasses.length) { commonProperties.classes = classes; } else { commonClasses = commonClasses.filter(function (entry) { return classes.some(function (name) { return name === entry; }); }); if (commonClasses.length) { commonProperties.classes = commonClasses; } else { delete commonProperties.classes; } } } else { // TODO: restructure removal as 2x set / 2x delete, instead of modify always replacing with new collection delete commonProperties.classes; } } // ~ attributes if (commonAttributes !== undefined) { (function () { var elementAttributes = element.attributes; var attributes = Object.keys(elementAttributes).reduce(function (attributes, key) { var attribute = elementAttributes[key]; var attributeName = attribute.name; // NOTE: workaround detection for non-standard phantomjs NamedNodeMap behaviour // (issue: https://github.com/ariya/phantomjs/issues/14634) if (attribute && attributeName !== 'class') { attributes[attributeName] = attribute.value; } return attributes; }, {}); var attributesNames = Object.keys(attributes); var commonAttributesNames = Object.keys(commonAttributes); if (attributesNames.length) { if (!commonAttributesNames.length) { commonProperties.attributes = attributes; } else { commonAttributes = commonAttributesNames.reduce(function (nextCommonAttributes, name) { var value = commonAttributes[name]; if (value === attributes[name]) { nextCommonAttributes[name] = value; } return nextCommonAttributes; }, {}); if (Object.keys(commonAttributes).length) { commonProperties.attributes = commonAttributes; } else { delete commonProperties.attributes; } } } else { delete commonProperties.attributes; } })(); } // ~ tag if (commonTag !== undefined) { var tag = element.tagName.toLowerCase(); if (!commonTag) { commonProperties.tag = tag; } else if (tag !== commonTag) { delete commonProperties.tag; } } }); return commonProperties; } /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = optimize; var _adapt = __webpack_require__(3); var _adapt2 = _interopRequireDefault(_adapt); var _utilities = __webpack_require__(0); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Apply different optimization techniques * * @param {string} selector - [description] * @param {HTMLElement|Array.} element - [description] * @param {Object} options - [description] * @return {string} - [description] */ /** * # Optimize * * 1.) Improve efficiency through shorter selectors by removing redundancy * 2.) Improve robustness through selector transformation */ function optimize(selector, elements) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; // convert single entry and NodeList if (!Array.isArray(elements)) { elements = !elements.length ? [elements] : (0, _utilities.convertNodeList)(elements); } if (!elements.length || elements.some(function (element) { return element.nodeType !== 1; })) { throw new Error('Invalid input - to compare HTMLElements its necessary to provide a reference of the selected node(s)! (missing "elements")'); } var globalModified = (0, _adapt2.default)(elements[0], options); // chunk parts outside of quotes (http://stackoverflow.com/a/25663729) var path = selector.replace(/> /g, '>').split(/\s+(?=(?:(?:[^"]*"){2})*[^"]*$)/); if (path.length < 2) { return optimizePart('', selector, '', elements); } var shortened = [path.pop()]; while (path.length > 1) { var current = path.pop(); var prePart = path.join(' '); var postPart = shortened.join(' '); var pattern = prePart + ' ' + postPart; var matches = document.querySelectorAll(pattern); if (matches.length !== elements.length) { shortened.unshift(optimizePart(prePart, current, postPart, elements)); } } shortened.unshift(path[0]); path = shortened; // optimize start + end path[0] = optimizePart('', path[0], path.slice(1).join(' '), elements); path[path.length - 1] = optimizePart(path.slice(0, -1).join(' '), path[path.length - 1], '', elements); if (globalModified) { delete true; } return path.join(' ').replace(/>/g, '> ').trim(); } /** * Improve a chunk of the selector * * @param {string} prePart - [description] * @param {string} current - [description] * @param {string} postPart - [description] * @param {Array.} elements - [description] * @return {string} - [description] */ function optimizePart(prePart, current, postPart, elements) { if (prePart.length) prePart = prePart + ' '; if (postPart.length) postPart = ' ' + postPart; // robustness: attribute without value (generalization) if (/\[*\]/.test(current)) { var key = current.replace(/=.*$/, ']'); var pattern = '' + prePart + key + postPart; var matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = key; } else { // robustness: replace specific key-value with base tag (heuristic) var references = document.querySelectorAll('' + prePart + key); var _loop = function _loop() { var reference = references[i]; if (elements.some(function (element) { return reference.contains(element); })) { var description = reference.tagName.toLowerCase(); pattern = '' + prePart + description + postPart; matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = description; } return 'break'; } }; for (var i = 0, l = references.length; i < l; i++) { var pattern; var matches; var _ret = _loop(); if (_ret === 'break') break; } } } // robustness: descendant instead child (heuristic) if (/>/.test(current)) { var descendant = current.replace(/>/, ''); var pattern = '' + prePart + descendant + postPart; var matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = descendant; } } // robustness: 'nth-of-type' instead 'nth-child' (heuristic) if (/:nth-child/.test(current)) { // TODO: consider complete coverage of 'nth-of-type' replacement var type = current.replace(/nth-child/g, 'nth-of-type'); var pattern = '' + prePart + type + postPart; var matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = type; } } // efficiency: combinations of classname (partial permutations) if (/\.\S+\.\S+/.test(current)) { var names = current.trim().split('.').slice(1).map(function (name) { return '.' + name; }).sort(function (curr, next) { return curr.length - next.length; }); while (names.length) { var partial = current.replace(names.shift(), '').trim(); var pattern = ('' + prePart + partial + postPart).trim(); if (!pattern.length || pattern.charAt(0) === '>' || pattern.charAt(pattern.length - 1) === '>') { break; } var matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = partial; } } // robustness: degrade complex classname (heuristic) names = current && current.match(/\./g); if (names && names.length > 2) { var _references = document.querySelectorAll('' + prePart + current); var _loop2 = function _loop2() { var reference = _references[i]; if (elements.some(function (element) { return reference.contains(element); })) { // TODO: // - check using attributes + regard excludes var description = reference.tagName.toLowerCase(); pattern = '' + prePart + description + postPart; matches = document.querySelectorAll(pattern); if (compareResults(matches, elements)) { current = description; } return 'break'; } }; for (var i = 0, l = _references.length; i < l; i++) { var pattern; var matches; var _ret2 = _loop2(); if (_ret2 === 'break') break; } } } return current; } /** * Evaluate matches with expected elements * * @param {Array.} matches - [description] * @param {Array.} elements - [description] * @return {Boolean} - [description] */ function compareResults(matches, elements) { var length = matches.length; return length === elements.length && elements.every(function (element) { for (var i = 0; i < length; i++) { if (matches[i] === element) { return true; } } return false; }); } module.exports = exports['default']; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); exports.default = adapt; /** * # Adapt * * Check and extend the environment for universal usage. */ /** * Modify the context based on the environment * * @param {HTMLELement} element - [description] * @param {Object} options - [description] * @return {boolean} - [description] */ function adapt(element, options) { // detect environment setup if (true) { return false; } else { global.document = options.context || function () { var root = element; while (root.parent) { root = root.parent; } return root; }(); } // https://github.com/fb55/domhandler/blob/master/index.js#L75 var ElementPrototype = Object.getPrototypeOf(true); // alternative descriptor to access elements with filtering invalid elements (e.g. textnodes) if (!Object.getOwnPropertyDescriptor(ElementPrototype, 'childTags')) { Object.defineProperty(ElementPrototype, 'childTags', { enumerable: true, get: function get() { return this.children.filter(function (node) { // https://github.com/fb55/domelementtype/blob/master/index.js#L12 return node.type === 'tag' || node.type === 'script' || node.type === 'style'; }); } }); } if (!Object.getOwnPropertyDescriptor(ElementPrototype, 'attributes')) { // https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes // https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap Object.defineProperty(ElementPrototype, 'attributes', { enumerable: true, get: function get() { var attribs = this.attribs; var attributesNames = Object.keys(attribs); var NamedNodeMap = attributesNames.reduce(function (attributes, attributeName, index) { attributes[index] = { name: attributeName, value: attribs[attributeName] }; return attributes; }, {}); Object.defineProperty(NamedNodeMap, 'length', { enumerable: false, configurable: false, value: attributesNames.length }); return NamedNodeMap; } }); } if (!ElementPrototype.getAttribute) { // https://docs.webplatform.org/wiki/dom/Element/getAttribute // https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute ElementPrototype.getAttribute = function (name) { return this.attribs[name] || null; }; } if (!ElementPrototype.getElementsByTagName) { // https://docs.webplatform.org/wiki/dom/Document/getElementsByTagName // https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName ElementPrototype.getElementsByTagName = function (tagName) { var HTMLCollection = []; traverseDescendants(this.childTags, function (descendant) { if (descendant.name === tagName || tagName === '*') { HTMLCollection.push(descendant); } }); return HTMLCollection; }; } if (!ElementPrototype.getElementsByClassName) { // https://docs.webplatform.org/wiki/dom/Document/getElementsByClassName // https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName ElementPrototype.getElementsByClassName = function (className) { var names = className.trim().replace(/\s+/g, ' ').split(' '); var HTMLCollection = []; traverseDescendants([this], function (descendant) { var descendantClassName = descendant.attribs.class; if (descendantClassName && names.every(function (name) { return descendantClassName.indexOf(name) > -1; })) { HTMLCollection.push(descendant); } }); return HTMLCollection; }; } if (!ElementPrototype.querySelectorAll) { // https://docs.webplatform.org/wiki/css/selectors_api/querySelectorAll // https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll ElementPrototype.querySelectorAll = function (selectors) { var _this = this; selectors = selectors.replace(/(>)(\S)/g, '$1 $2').trim(); // add space for '>' selector // using right to left execution => https://github.com/fb55/css-select#how-does-it-work var instructions = getInstructions(selectors); var discover = instructions.shift(); var total = instructions.length; return discover(this).filter(function (node) { var step = 0; while (step < total) { node = instructions[step](node, _this); if (!node) { // hierarchy doesn't match return false; } step += 1; } return true; }); }; } if (!ElementPrototype.contains) { // https://developer.mozilla.org/en-US/docs/Web/API/Node/contains ElementPrototype.contains = function (element) { var inclusive = false; traverseDescendants([this], function (descendant, done) { if (descendant === element) { inclusive = true; done(); } }); return inclusive; }; } return true; } /** * Retrieve transformation steps * * @param {Array.} selectors - [description] * @return {Array.} - [description] */ function getInstructions(selectors) { return selectors.split(' ').reverse().map(function (selector, step) { var discover = step === 0; var _selector$split = selector.split(':'), _selector$split2 = _slicedToArray(_selector$split, 2), type = _selector$split2[0], pseudo = _selector$split2[1]; var validate = null; var instruction = null; (function () { switch (true) { // child: '>' case />/.test(type): instruction = function checkParent(node) { return function (validate) { return validate(node.parent) && node.parent; }; }; break; // class: '.' case /^\./.test(type): var names = type.substr(1).split('.'); validate = function validate(node) { var nodeClassName = node.attribs.class; return nodeClassName && names.every(function (name) { return nodeClassName.indexOf(name) > -1; }); }; instruction = function checkClass(node, root) { if (discover) { return node.getElementsByClassName(names.join(' ')); } return typeof node === 'function' ? node(validate) : getAncestor(node, root, validate); }; break; // attribute: '[key="value"]' case /^\[/.test(type): var _type$replace$split = type.replace(/\[|\]|"/g, '').split('='), _type$replace$split2 = _slicedToArray(_type$replace$split, 2), attributeKey = _type$replace$split2[0], attributeValue = _type$replace$split2[1]; validate = function validate(node) { var hasAttribute = Object.keys(node.attribs).indexOf(attributeKey) > -1; if (hasAttribute) { // regard optional attributeValue if (!attributeValue || node.attribs[attributeKey] === attributeValue) { return true; } } return false; }; instruction = function checkAttribute(node, root) { if (discover) { var _ret2 = function () { var NodeList = []; traverseDescendants([node], function (descendant) { if (validate(descendant)) { NodeList.push(descendant); } }); return { v: NodeList }; }(); if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object") return _ret2.v; } return typeof node === 'function' ? node(validate) : getAncestor(node, root, validate); }; break; // id: '#' case /^#/.test(type): var id = type.substr(1); validate = function validate(node) { return node.attribs.id === id; }; instruction = function checkId(node, root) { if (discover) { var _ret3 = function () { var NodeList = []; traverseDescendants([node], function (descendant, done) { if (validate(descendant)) { NodeList.push(descendant); done(); } }); return { v: NodeList }; }(); if ((typeof _ret3 === 'undefined' ? 'undefined' : _typeof(_ret3)) === "object") return _ret3.v; } return typeof node === 'function' ? node(validate) : getAncestor(node, root, validate); }; break; // universal: '*' case /\*/.test(type): validate = function validate(node) { return true; }; instruction = function checkUniversal(node, root) { if (discover) { var _ret4 = function () { var NodeList = []; traverseDescendants([node], function (descendant) { return NodeList.push(descendant); }); return { v: NodeList }; }(); if ((typeof _ret4 === 'undefined' ? 'undefined' : _typeof(_ret4)) === "object") return _ret4.v; } return typeof node === 'function' ? node(validate) : getAncestor(node, root, validate); }; break; // tag: '...' default: validate = function validate(node) { return node.name === type; }; instruction = function checkTag(node, root) { if (discover) { var _ret5 = function () { var NodeList = []; traverseDescendants([node], function (descendant) { if (validate(descendant)) { NodeList.push(descendant); } }); return { v: NodeList }; }(); if ((typeof _ret5 === 'undefined' ? 'undefined' : _typeof(_ret5)) === "object") return _ret5.v; } return typeof node === 'function' ? node(validate) : getAncestor(node, root, validate); }; } })(); if (!pseudo) { return instruction; } var rule = pseudo.match(/-(child|type)\((\d+)\)$/); var kind = rule[1]; var index = parseInt(rule[2], 10) - 1; var validatePseudo = function validatePseudo(node) { if (node) { var compareSet = node.parent.childTags; if (kind === 'type') { compareSet = compareSet.filter(validate); } var nodeIndex = compareSet.findIndex(function (child) { return child === node; }); if (nodeIndex === index) { return true; } } return false; }; return function enhanceInstruction(node) { var match = instruction(node); if (discover) { return match.reduce(function (NodeList, matchedNode) { if (validatePseudo(matchedNode)) { NodeList.push(matchedNode); } return NodeList; }, []); } return validatePseudo(match) && match; }; }); } /** * Walking recursive to invoke callbacks * * @param {Array.} nodes - [description] * @param {Function} handler - [description] */ function traverseDescendants(nodes, handler) { nodes.forEach(function (node) { var progress = true; handler(node, function () { return progress = false; }); if (node.childTags && progress) { traverseDescendants(node.childTags, handler); } }); } /** * Bubble up from bottom to top * * @param {HTMLELement} node - [description] * @param {HTMLELement} root - [description] * @param {Function} validate - [description] * @return {HTMLELement} - [description] */ function getAncestor(node, root, validate) { while (node.parent) { node = node.parent; if (validate(node)) { return node; } if (node === root) { break; } } return null; } module.exports = exports['default']; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * # Select * * Construct a unique CSS query selector to access the selected DOM element(s). * For longevity it applies different matching and optimization strategies. */ exports.getSingleSelector = getSingleSelector; exports.getMultiSelector = getMultiSelector; exports.default = getQuerySelector; var _adapt = __webpack_require__(3); var _adapt2 = _interopRequireDefault(_adapt); var _match = __webpack_require__(5); var _match2 = _interopRequireDefault(_match); var _optimize = __webpack_require__(2); var _optimize2 = _interopRequireDefault(_optimize); var _utilities = __webpack_require__(0); var _common = __webpack_require__(1); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Get a selector for the provided element * * @param {HTMLElement} element - [description] * @param {Object} options - [description] * @return {string} - [description] */ function getSingleSelector(element) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (element.nodeType === 3) { element = element.parentNode; } if (element.nodeType !== 1) { throw new Error('Invalid input - only HTMLElements or representations of them are supported! (not "' + (typeof element === 'undefined' ? 'undefined' : _typeof(element)) + '")'); } var globalModified = (0, _adapt2.default)(element, options); var selector = (0, _match2.default)(element, options); var optimized = (0, _optimize2.default)(selector, element, options); // debug // console.log(` // selector: ${selector} // optimized: ${optimized} // `) if (globalModified) { delete true; } return optimized; } /** * Get a selector to match multiple descendants from an ancestor * * @param {Array.|NodeList} elements - [description] * @param {Object} options - [description] * @return {string} - [description] */ function getMultiSelector(elements) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!Array.isArray(elements)) { elements = (0, _utilities.convertNodeList)(elements); } if (elements.some(function (element) { return element.nodeType !== 1; })) { throw new Error('Invalid input - only an Array of HTMLElements or representations of them is supported!'); } var globalModified = (0, _adapt2.default)(elements[0], options); var ancestor = (0, _common.getCommonAncestor)(elements, options); var ancestorSelector = getSingleSelector(ancestor, options); // TODO: consider usage of multiple selectors + parent-child relation + check for part redundancy var commonSelectors = getCommonSelectors(elements); var descendantSelector = commonSelectors[0]; var selector = (0, _optimize2.default)(ancestorSelector + ' ' + descendantSelector, elements, options); var selectorMatches = (0, _utilities.convertNodeList)(document.querySelectorAll(selector)); if (!elements.every(function (element) { return selectorMatches.some(function (entry) { return entry === element; }); })) { // TODO: cluster matches to split into similar groups for sub selections return console.warn('\n The selected elements can\'t be efficiently mapped.\n Its probably best to use multiple single selectors instead!\n ', elements); } if (globalModified) { delete true; } return selector; } /** * Get selectors to describe a set of elements * * @param {Array.} elements - [description] * @return {string} - [description] */ function getCommonSelectors(elements) { var _getCommonProperties = (0, _common.getCommonProperties)(elements), classes = _getCommonProperties.classes, attributes = _getCommonProperties.attributes, tag = _getCommonProperties.tag; var selectorPath = []; if (tag) { selectorPath.push(tag); } if (classes) { var classSelector = classes.map(function (name) { return '.' + name; }).join(''); selectorPath.push(classSelector); } if (attributes) { var attributeSelector = Object.keys(attributes).reduce(function (parts, name) { parts.push('[' + name + '="' + attributes[name] + '"]'); return parts; }, []).join(''); selectorPath.push(attributeSelector); } if (selectorPath.length) { // TODO: check for parent-child relation } return [selectorPath.join('')]; } /** * Choose action depending on the input (multiple/single) * * NOTE: extended detection is used for special cases like the elements pass all the criteria regarding length and the first child having style, so we must also check to ensure the target isn't an HTML node itself. targets.splice(i--, 1); this._targets = targets = targets.concat(_slice(targ)); continue; } this._siblings[i] = _register(targ, this, false); if (overwrite === 1) if (this._siblings[i].length > 1) { _applyOverwrite(targ, this, null, 1, this._siblings[i]); } } } else { this._propLookup = {}; this._siblings = _register(target, this, false); if (overwrite === 1) if (this._siblings.length > 1) { _applyOverwrite(target, this, null, 1, this._siblings); } } if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) { this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render) this.render(Math.min(0, -this._delay)); //in case delay is negative } }, true), _isSelector = function(v) { return (v && v.length && v !== window && v[0] && (v[0] === window || (v[0].nodeType && v[0].style && !v.nodeType))); //we cannot check "nodeType" if the target is window from within an iframe, otherwise it will trigger a security error in some browsers like Firefox. }, _autoCSS = function(vars, target) { var css = {}, p; for (p in vars) { if (!_reservedProps[p] && (!(p in target) || p === "transform" || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties. css[p] = vars[p]; delete vars[p]; } } vars.css = css; }; p = TweenLite.prototype = new Animation(); p.constructor = TweenLite; p.kill()._gc = false; //----TweenLite defaults, overwrite management, and root updates ---------------------------------------------------- p.ratio = 0; p._firstPT = p._targets = p._overwrittenProps = p._startAt = null; p._notifyPluginsOfEnabled = p._lazy = false; TweenLite.version = "2.1.2"; TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1); TweenLite.defaultOverwrite = "auto"; TweenLite.ticker = _ticker; TweenLite.autoSleep = 120; TweenLite.lagSmoothing = function(threshold, adjustedLag) { _ticker.lagSmoothing(threshold, adjustedLag); }; TweenLite.selector = window.$ || window.jQuery || function(e) { var selector = window.$ || window.jQuery; if (selector) { TweenLite.selector = selector; return selector(e); } if (!_doc) { //in some dev environments (like Angular 6), GSAP gets loaded before the document is defined! So re-query it here if/when necessary. _doc = window.document; } return (!_doc) ? e : (_doc.querySelectorAll ? _doc.querySelectorAll(e) : _doc.getElementById((e.charAt(0) === "#") ? e.substr(1) : e)); }; var _lazyTweens = [], _lazyLookup = {}, _numbersExp = /(?:(-|-=|\+=)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, _relExp = /[\+-]=-?[\.\d]/, //_nonNumbersExp = /(?:([\-+](?!(\d|=)))|[^\d\-+=e]|(e(?![\-+][\d])))+/ig, _setRatio = function(v) { var pt = this._firstPT, min = 0.000001, val; while (pt) { val = !pt.blob ? pt.c * v + pt.s : (v === 1 && this.end != null) ? this.end : v ? this.join("") : this.start; if (pt.m) { val = pt.m.call(this._tween, val, this._target || pt.t, this._tween); } else if (val < min) if (val > -min && !pt.blob) { //prevents issues with converting very small numbers to strings in the browser val = 0; } if (!pt.f) { pt.t[pt.p] = val; } else if (pt.fp) { pt.t[pt.p](pt.fp, val); } else { pt.t[pt.p](val); } pt = pt._next; } }, _blobRound = function(v) { return (((v * 1000) | 0) / 1000) + ""; }, //compares two strings (start/end), finds the numbers that are different and spits back an array representing the whole value but with the changing values isolated as elements. For example, "rgb(0,0,0)" and "rgb(100,50,0)" would become ["rgb(", 0, ",", 50, ",0)"]. Notice it merges the parts that are identical (performance optimization). The array also has a linked list of PropTweens attached starting with _firstPT that contain the tweening data (t, p, s, c, f, etc.). It also stores the starting value as a "start" property so that we can revert to it if/when necessary, like when a tween rewinds fully. If the quantity of numbers differs between the start and end, it will always prioritize the end value(s). The pt parameter is optional - it's for a PropTween that will be appended to the end of the linked list and is typically for actually setting the value after all of the elements have been updated (with array.join("")). _blobDif = function(start, end, filter, pt) { var a = [], charIndex = 0, s = "", color = 0, startNums, endNums, num, i, l, nonNumbers, currentNum; a.start = start; a.end = end; start = a[0] = start + ""; //ensure values are strings end = a[1] = end + ""; if (filter) { filter(a); //pass an array with the starting and ending values and let the filter do whatever it needs to the values. start = a[0]; end = a[1]; } a.length = 0; startNums = start.match(_numbersExp) || []; endNums = end.match(_numbersExp) || []; if (pt) { pt._next = null; pt.blob = 1; a._firstPT = a._applyPT = pt; //apply last in the linked list (which means inserting it first) } l = endNums.length; for (i = 0; i < l; i++) { currentNum = endNums[i]; nonNumbers = end.substr(charIndex, end.indexOf(currentNum, charIndex)-charIndex); s += (nonNumbers || !i) ? nonNumbers : ","; //note: SVG spec allows omission of comma/space when a negative sign is wedged between two numbers, like 2.5-5.3 instead of 2.5,-5.3 but when tweening, the negative value may switch to positive, so we insert the comma just in case. charIndex += nonNumbers.length; if (color) { //sense rgba() values and round them. color = (color + 1) % 5; } else if (nonNumbers.substr(-5) === "rgba(") { color = 1; } if (currentNum === startNums[i] || startNums.length <= i) { s += currentNum; } else { if (s) { a.push(s); s = ""; } num = parseFloat(startNums[i]); a.push(num); a._firstPT = {_next: a._firstPT, t:a, p: a.length-1, s:num, c:((currentNum.charAt(1) === "=") ? parseInt(currentNum.charAt(0) + "1", 10) * parseFloat(currentNum.substr(2)) : (parseFloat(currentNum) - num)) || 0, f:0, m:(color && color < 4) ? Math.round : _blobRound}; //limiting to 3 decimal places and casting as a string can really help performance when array.join() is called! //note: we don't set _prev because we'll never need to remove individual PropTweens from this list. } charIndex += currentNum.length; } s += end.substr(charIndex); if (s) { a.push(s); } a.setRatio = _setRatio; if (_relExp.test(end)) { //if the end string contains relative values, delete it so that on the final render (in _setRatio()), we don't actually set it to the string with += or -= characters (forces it to use the calculated value). a.end = null; } return a; }, //note: "funcParam" is only necessary for function-based getters/setters that require an extra parameter like getAttribute("width") and setAttribute("width", value). In this example, funcParam would be "width". Used by AttrPlugin for example. _addPropTween = function(target, prop, start, end, overwriteProp, mod, funcParam, stringFilter, index) { if (typeof(end) === "function") { end = end(index || 0, target); } var type = typeof(target[prop]), getterName = (type !== "function") ? "" : ((prop.indexOf("set") || typeof(target["get" + prop.substr(3)]) !== "function") ? prop : "get" + prop.substr(3)), s = (start !== "get") ? start : !getterName ? target[prop] : funcParam ? target[getterName](funcParam) : target[getterName](), isRelative = (typeof(end) === "string" && end.charAt(1) === "="), pt = {t:target, p:prop, s:s, f:(type === "function"), pg:0, n:overwriteProp || prop, m:(!mod ? 0 : (typeof(mod) === "function") ? mod : Math.round), pr:0, c:isRelative ? parseInt(end.charAt(0) + "1", 10) * parseFloat(end.substr(2)) : (parseFloat(end) - s) || 0}, blob; if (typeof(s) !== "number" || (typeof(end) !== "number" && !isRelative)) { if (funcParam || isNaN(s) || (!isRelative && isNaN(end)) || typeof(s) === "boolean" || typeof(end) === "boolean") { //a blob (string that has multiple numbers in it) pt.fp = funcParam; blob = _blobDif(s, (isRelative ? (parseFloat(pt.s) + pt.c) + (pt.s + "").replace(/[0-9\-\.]/g, "") : end), stringFilter || TweenLite.defaultStringFilter, pt); pt = {t: blob, p: "setRatio", s: 0, c: 1, f: 2, pg: 0, n: overwriteProp || prop, pr: 0, m: 0}; //"2" indicates it's a Blob property tween. Needed for RoundPropsPlugin for example. } else { pt.s = parseFloat(s); if (!isRelative) { pt.c = (parseFloat(end) - pt.s) || 0; } } } if (pt.c) { //only add it to the linked list if there's a change. if ((pt._next = this._firstPT)) { pt._next._prev = pt; } this._firstPT = pt; return pt; } }, _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector, lazyTweens:_lazyTweens, blobDif:_blobDif}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object. _plugins = TweenLite._plugins = {}, _tweenLookup = _internals.tweenLookup = {}, _tweenLookupNum = 0, _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1, lazy:1, onOverwrite:1, callbackScope:1, stringFilter:1, id:1, yoyoEase:1, stagger:1}, _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0}, _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(), _rootTimeline = Animation._rootTimeline = new SimpleTimeline(), _nextGCFrame = 30, _lazyRender = _internals.lazyRender = function() { var l = _lazyTweens.length, i, tween; _lazyLookup = {}; for (i = 0; i < l; i++) { tween = _lazyTweens[i]; if (tween && tween._lazy !== false) { tween.render(tween._lazy[0], tween._lazy[1], true); tween._lazy = false; } } _lazyTweens.length = 0; }; _rootTimeline._startTime = _ticker.time; _rootFramesTimeline._startTime = _ticker.frame; _rootTimeline._active = _rootFramesTimeline._active = true; setTimeout(_lazyRender, 1); //on some mobile devices, there isn't a "tick" before code runs which means any lazy renders wouldn't run before the next official "tick". Animation._updateRoot = TweenLite.render = function() { var i, a, p; if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again. _lazyRender(); } _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false); if (_lazyTweens.length) { _lazyRender(); } if (_ticker.frame >= _nextGCFrame) { //dump garbage every 120 frames or whatever the user sets TweenLite.autoSleep to _nextGCFrame = _ticker.frame + (parseInt(TweenLite.autoSleep, 10) || 120); for (p in _tweenLookup) { a = _tweenLookup[p].tweens; i = a.length; while (--i > -1) { if (a[i]._gc) { a.splice(i, 1); } } if (a.length === 0) { delete _tweenLookup[p]; } } //if there are no more tweens in the root timelines, or if they're all paused, make the _timer sleep to reduce load on the CPU slightly p = _rootTimeline._first; if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) { while (p && p._paused) { p = p._next; } if (!p) { _ticker.sleep(); } } } }; _ticker.addEventListener("tick", Animation._updateRoot); var _register = function(target, tween, scrub) { var id = target._gsTweenID, a, i; if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) { _tweenLookup[id] = {target:target, tweens:[]}; } if (tween) { a = _tweenLookup[id].tweens; a[(i = a.length)] = tween; if (scrub) { while (--i > -1) { if (a[i] === tween) { a.splice(i, 1); } } } } return _tweenLookup[id].tweens; }, _onOverwrite = function(overwrittenTween, overwritingTween, target, killedProps) { var func = overwrittenTween.vars.onOverwrite, r1, r2; if (func) { r1 = func(overwrittenTween, overwritingTween, target, killedProps); } func = TweenLite.onOverwrite; if (func) { r2 = func(overwrittenTween, overwritingTween, target, killedProps); } return (r1 !== false && r2 !== false); }, _applyOverwrite = function(target, tween, props, mode, siblings) { var i, changed, curTween, l; if (mode === 1 || mode >= 4) { l = siblings.length; for (i = 0; i < l; i++) { if ((curTween = siblings[i]) !== tween) { if (!curTween._gc) { if (curTween._kill(null, target, tween)) { changed = true; } } } else if (mode === 5) { break; } } return changed; } //NOTE: Add tiny amount to overcome floating point errors that can cause the startTime to be VERY slightly off (when a tween's time() is set for example) var startTime = tween._startTime + _tinyNum, overlaps = [], oCount = 0, zeroDur = (tween._duration === 0), globalStart; i = siblings.length; while (--i > -1) { if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) { //ignore } else if (curTween._timeline !== tween._timeline) { globalStart = globalStart || _checkOverlap(tween, 0, zeroDur); if (_checkOverlap(curTween, globalStart, zeroDur) === 0) { overlaps[oCount++] = curTween; } } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= _tinyNum * 2)) { overlaps[oCount++] = curTween; } } i = oCount; while (--i > -1) { curTween = overlaps[i]; l = curTween._firstPT; //we need to discern if there were property tweens originally; if they all get removed in the next line's _kill() call, the tween should be killed. See https://github.com/greensock/GreenSock-JS/issues/278 if (mode === 2) if (curTween._kill(props, target, tween)) { changed = true; } if (mode !== 2 || (!curTween._firstPT && curTween._initted && l)) { if (mode !== 2 && !_onOverwrite(curTween, tween)) { continue; } if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween. changed = true; } } } return changed; }, _checkOverlap = function(tween, reference, zeroDur) { var tl = tween._timeline, ts = tl._timeScale, t = tween._startTime; while (tl._timeline) { t += tl._startTime; ts *= tl._timeScale; if (tl._paused) { return -100; } tl = tl._timeline; } t /= ts; return (t > reference) ? t - reference : ((zeroDur && t === reference) || (!tween._initted && t - reference < 2 * _tinyNum)) ? _tinyNum : ((t += tween.totalDuration() / tween._timeScale / ts) > reference + _tinyNum) ? 0 : t - reference - _tinyNum; }; //---- TweenLite instance methods ----------------------------------------------------------------------------- p._init = function() { var v = this.vars, op = this._overwrittenProps, dur = this._duration, immediate = !!v.immediateRender, ease = v.ease, startAt = this._startAt, i, initPlugins, pt, p, startVars, l; if (v.startAt) { if (startAt) { startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change. startAt.kill(); } startVars = {}; for (p in v.startAt) { //copy the properties/values into a new object to avoid collisions, like var to = {x:0}, from = {x:500}; timeline.fromTo(e, 1, from, to).fromTo(e, 1, to, from); startVars[p] = v.startAt[p]; } startVars.data = "isStart"; startVars.overwrite = false; startVars.immediateRender = true; startVars.lazy = (immediate && v.lazy !== false); startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop). startVars.onUpdate = v.onUpdate; startVars.onUpdateParams = v.onUpdateParams; startVars.onUpdateScope = v.onUpdateScope || v.callbackScope || this; this._startAt = TweenLite.to(this.target || {}, 0, startVars); if (immediate) { if (this._time > 0) { this._startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()). } else if (dur !== 0) { return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting. } } } else if (v.runBackwards && dur !== 0) { //from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards) if (startAt) { startAt.render(-1, true); startAt.kill(); this._startAt = null; } else { if (this._time !== 0) { //in rare cases (like if a from() tween runs and then is invalidate()-ed), immediateRender could be true but the initial forced-render gets skipped, so there's no need to force the render in this context when the _time is greater than 0 immediate = false; } pt = {}; for (p in v) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through. if (!_reservedProps[p] || p === "autoCSS") { pt[p] = v[p]; } } pt.overwrite = 0; pt.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in. pt.lazy = (immediate && v.lazy !== false); pt.immediateRender = immediate; //zero-duration tweens render immediately by default, but if we're not specifically instructed to render this tween immediately, we should skip this and merely _init() to record the starting values (rendering them immediately would push them to completion which is wasteful in that case - we'd have to render(-1) immediately after) this._startAt = TweenLite.to(this.target, 0, pt); if (!immediate) { this._startAt._init(); //ensures that the initial values are recorded this._startAt._enabled(false); //no need to have the tween render on the next cycle. Disable it because we'll always manually control the renders of the _startAt tween. if (this.vars.immediateRender) { this._startAt = null; } } else if (this._time === 0) { return; } } } this._ease = ease = (!ease) ? TweenLite.defaultEase : (ease instanceof Ease) ? ease : (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase; if (v.easeParams instanceof Array && ease.config) { this._ease = ease.config.apply(ease, v.easeParams); } this._easeType = this._ease._type; this._easePower = this._ease._power; this._firstPT = null; if (this._targets) { l = this._targets.length; for (i = 0; i < l; i++) { if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null), i) ) { initPlugins = true; } } } else { initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op, 0); } if (initPlugins) { TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite } if (op) if (!this._firstPT) if (typeof(this.target) !== "function") { //if all tweening properties have been overwritten, kill the tween. If the target is a function, it's probably a delayedCall so let it live. this._enabled(false, false); } if (v.runBackwards) { pt = this._firstPT; while (pt) { pt.s += pt.c; pt.c = -pt.c; pt = pt._next; } } this._onUpdate = v.onUpdate; this._initted = true; }; p._initProps = function(target, propLookup, siblings, overwrittenProps, index) { var p, i, initPlugins, plugin, pt, v; if (target == null) { return false; } if (_lazyLookup[target._gsTweenID]) { _lazyRender(); //if other tweens of the same target have recently initted but haven't rendered yet, we've got to force the render so that the starting values are correct (imagine populating a timeline with a bunch of sequential tweens and then jumping to the end) } if (!this.vars.css) if (target.style) if (target !== window && target.nodeType) if (_plugins.css) if (this.vars.autoCSS !== false) { //it's so common to use TweenLite/Max to animate the css of DOM elements, we assume that if the target is a DOM element, that's what is intended (a convenience so that users don't have to wrap things in css:{}, although we still recommend it for a slight performance boost and better specificity). Note: we cannot check "nodeType" on the window inside an iframe. _autoCSS(this.vars, target); } for (p in this.vars) { v = this.vars[p]; if (_reservedProps[p]) { if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) { this.vars[p] = v = this._swapSelfInParams(v, this); } } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this, index)) { //t - target [object] //p - property [string] //s - start [number] //c - change [number] //f - isFunction [boolean] //n - name [string] //pg - isPlugin [boolean] //pr - priority [number] //m - mod [function | 0] this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:1, n:p, pg:1, pr:plugin._priority, m:0}; i = plugin._overwriteProps.length; while (--i > -1) { propLookup[plugin._overwriteProps[i]] = this._firstPT; } if (plugin._priority || plugin._onInitAllProps) { initPlugins = true; } if (plugin._onDisable || plugin._onEnable) { this._notifyPluginsOfEnabled = true; } if (pt._next) { pt._next._prev = pt; } } else { propLookup[p] = _addPropTween.call(this, target, p, "get", v, p, 0, null, this.vars.stringFilter, index); } } if (overwrittenProps) if (this._kill(overwrittenProps, target)) { //another tween may have tried to overwrite properties of this tween before init() was called (like if two tweens start at the same time, the one created second will run first) return this._initProps(target, propLookup, siblings, overwrittenProps, index); } if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) { this._kill(propLookup, target); return this._initProps(target, propLookup, siblings, overwrittenProps, index); } if (this._firstPT) if ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration)) { //zero duration tweens don't lazy render by default; everything else does. _lazyLookup[target._gsTweenID] = true; } return initPlugins; }; p.render = function(time, suppressEvents, force) { var self = this, prevTime = self._time, duration = self._duration, prevRawPrevTime = self._rawPrevTime, isComplete, callback, pt, rawPrevTime; if (time >= duration - _tinyNum && time >= 0) { //to work around occasional floating point math artifacts. self._totalTime = self._time = duration; self.ratio = self._ease._calcEnd ? self._ease.getRatio(1) : 1; if (!self._reversed ) { isComplete = true; callback = "onComplete"; force = (force || self._timeline.autoRemoveChildren); //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline. } if (duration === 0) if (self._initted || !self.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. if (self._startTime === self._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate. time = 0; } if (prevRawPrevTime < 0 || (time <= 0 && time >= -_tinyNum) || (prevRawPrevTime === _tinyNum && self.data !== "isPause")) if (prevRawPrevTime !== time) { //note: when this.data is "isPause", it's a callback added by addPause() on a timeline that we should not be triggered when LEAVING its exact start time. In other words, tl.addPause(1).play(1) shouldn't pause. force = true; if (prevRawPrevTime > _tinyNum) { callback = "onReverseComplete"; } } self._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. } } else if (time < _tinyNum) { //to work around occasional floating point math artifacts, round super small values to 0. self._totalTime = self._time = 0; self.ratio = self._ease._calcEnd ? self._ease.getRatio(0) : 0; if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0)) { callback = "onReverseComplete"; isComplete = self._reversed; } if (time > -_tinyNum) { time = 0; } else if (time < 0) { self._active = false; if (duration === 0) if (self._initted || !self.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. if (prevRawPrevTime >= 0 && !(prevRawPrevTime === _tinyNum && self.data === "isPause")) { force = true; } self._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. } } if (!self._initted || (self._startAt && self._startAt.progress())) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately. Also, we check progress() because if startAt has already rendered at its end, we should force a render at its beginning. Otherwise, if you put the playhead directly on top of where a fromTo({immediateRender:false}) starts, and then move it backwards, the from() won't revert its values. force = true; } } else { self._totalTime = self._time = time; if (self._easeType) { var r = time / duration, type = self._easeType, pow = self._easePower; if (type === 1 || (type === 3 && r >= 0.5)) { r = 1 - r; } if (type === 3) { r *= 2; } if (pow === 1) { r *= r; } else if (pow === 2) { r *= r * r; } else if (pow === 3) { r *= r * r * r; } else if (pow === 4) { r *= r * r * r * r; } self.ratio = (type === 1) ? 1 - r : (type === 2) ? r : (time / duration < 0.5) ? r / 2 : 1 - (r / 2); } else { self.ratio = self._ease.getRatio(time / duration); } } if (self._time === prevTime && !force) { return; } else if (!self._initted) { self._init(); if (!self._initted || self._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example. return; } else if (!force && self._firstPT && ((self.vars.lazy !== false && self._duration) || (self.vars.lazy && !self._duration))) { self._time = self._totalTime = prevTime; self._rawPrevTime = prevRawPrevTime; _lazyTweens.push(self); self._lazy = [time, suppressEvents]; return; } //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently. if (self._time && !isComplete) { self.ratio = self._ease.getRatio(self._time / duration); } else if (isComplete && self._ease._calcEnd) { self.ratio = self._ease.getRatio((self._time === 0) ? 0 : 1); } } if (self._lazy !== false) { //in case a lazy render is pending, we should flush it because the new render is occurring now (imagine a lazy tween instantiating and then immediately the user calls tween.seek(tween.duration()), skipping to the end - the end render would be forced, and then if we didn't flush the lazy render, it'd fire AFTER the seek(), rendering it at the wrong time. self._lazy = false; } if (!self._active) if (!self._paused && self._time !== prevTime && time >= 0) { self._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done. } if (prevTime === 0) { if (self._startAt) { if (time >= 0) { self._startAt.render(time, true, force); } else if (!callback) { callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area. } } if (self.vars.onStart) if (self._time !== 0 || duration === 0) if (!suppressEvents) { self._callback("onStart"); } } pt = self._firstPT; while (pt) { if (pt.f) { pt.t[pt.p](pt.c * self.ratio + pt.s); } else { pt.t[pt.p] = pt.c * self.ratio + pt.s; } pt = pt._next; } if (self._onUpdate) { if (time < 0) if (self._startAt && time !== -0.0001) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. self._startAt.render(time, true, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete. } if (!suppressEvents) if (self._time !== prevTime || isComplete || force) { self._callback("onUpdate"); } } if (callback) if (!self._gc || force) { //check _gc because there's a chance that kill() could be called in an onUpdate if (time < 0 && self._startAt && !self._onUpdate && time !== -0.0001) { //-0.0001 is a special value that we use when looping back to the beginning of a repeated TimelineMax, in which case we shouldn't render the _startAt values. self._startAt.render(time, true, force); } if (isComplete) { if (self._timeline.autoRemoveChildren) { self._enabled(false, false); } self._active = false; } if (!suppressEvents && self.vars[callback]) { self._callback(callback); } if (duration === 0 && self._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless. self._rawPrevTime = 0; } } }; p._kill = function(vars, target, overwritingTween) { if (vars === "all") { vars = null; } if (vars == null) if (target == null || target === this.target) { this._lazy = false; return this._enabled(false, false); } target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target; var simultaneousOverwrite = (overwritingTween && this._time && overwritingTween._startTime === this._startTime && this._timeline === overwritingTween._timeline), firstPT = this._firstPT, i, overwrittenProps, p, pt, propLookup, changed, killProps, record, killed; if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { i = target.length; while (--i > -1) { if (this._kill(vars, target[i], overwritingTween)) { changed = true; } } } else { if (this._targets) { i = this._targets.length; while (--i > -1) { if (target === this._targets[i]) { propLookup = this._propLookup[i] || {}; this._overwrittenProps = this._overwrittenProps || []; overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all"; break; } } } else if (target !== this.target) { return false; } else { propLookup = this._propLookup; overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all"; } if (propLookup) { killProps = vars || propLookup; record = (vars !== overwrittenProps && overwrittenProps !== "all" && vars !== propLookup && (typeof(vars) !== "object" || !vars._tempKill)); //_tempKill is a super-secret way to delete a particular tweening property but NOT have it remembered as an official overwritten property (like in BezierPlugin) if (overwritingTween && (TweenLite.onOverwrite || this.vars.onOverwrite)) { for (p in killProps) { if (propLookup[p]) { if (!killed) { killed = []; } killed.push(p); } } if ((killed || !vars) && !_onOverwrite(this, overwritingTween, target, killed)) { //if the onOverwrite returned false, that means the user wants to override the overwriting (cancel it). return false; } } for (p in killProps) { if ((pt = propLookup[p])) { if (simultaneousOverwrite) { //if another tween overwrites this one and they both start at exactly the same time, yet this tween has already rendered once (for example, at 0.001) because it's first in the queue, we should revert the values to where they were at 0 so that the starting values aren't contaminated on the overwriting tween. if (pt.f) { pt.t[pt.p](pt.s); } else { pt.t[pt.p] = pt.s; } changed = true; } if (pt.pg && pt.t._kill(killProps)) { changed = true; //some plugins need to be notified so they can perform cleanup tasks first } if (!pt.pg || pt.t._overwriteProps.length === 0) { if (pt._prev) { pt._prev._next = pt._next; } else if (pt === this._firstPT) { this._firstPT = pt._next; } if (pt._next) { pt._next._prev = pt._prev; } pt._next = pt._prev = null; } delete propLookup[p]; } if (record) { overwrittenProps[p] = 1; } } if (!this._firstPT && this._initted && firstPT) { //if all tweening properties are killed, kill the tween. Without this line, if there's a tween with multiple targets and then you killTweensOf() each target individually, the tween would technically still remain active and fire its onComplete even though there aren't any more properties tweening. this._enabled(false, false); } } } return changed; }; p.invalidate = function() { if (this._notifyPluginsOfEnabled) { TweenLite._onPluginEvent("_onDisable", this); } var t = this._time; this._firstPT = this._overwrittenProps = this._startAt = this._onUpdate = null; this._notifyPluginsOfEnabled = this._active = this._lazy = false; this._propLookup = (this._targets) ? {} : []; Animation.prototype.invalidate.call(this); if (this.vars.immediateRender) { this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render) this.render(t, false, this.vars.lazy !== false); } return this; }; p._enabled = function(enabled, ignoreTimeline) { if (!_tickerActive) { _ticker.wake(); } if (enabled && this._gc) { var targets = this._targets, i; if (targets) { i = targets.length; while (--i > -1) { this._siblings[i] = _register(targets[i], this, true); } } else { this._siblings = _register(this.target, this, true); } } Animation.prototype._enabled.call(this, enabled, ignoreTimeline); if (this._notifyPluginsOfEnabled) if (this._firstPT) { return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this); } return false; }; //----TweenLite static methods ----------------------------------------------------- TweenLite.to = function(target, duration, vars) { return new TweenLite(target, duration, vars); }; TweenLite.from = function(target, duration, vars) { vars.runBackwards = true; vars.immediateRender = (vars.immediateRender != false); return new TweenLite(target, duration, vars); }; TweenLite.fromTo = function(target, duration, fromVars, toVars) { toVars.startAt = fromVars; toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); return new TweenLite(target, duration, toVars); }; TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) { return new TweenLite(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, callbackScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, immediateRender:false, lazy:false, useFrames:useFrames, overwrite:0}); }; TweenLite.set = function(target, vars) { return new TweenLite(target, 0, vars); }; TweenLite.getTweensOf = function(target, onlyActive) { if (target == null) { return []; } target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target; var i, a, j, t; if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { i = target.length; a = []; while (--i > -1) { a = a.concat(TweenLite.getTweensOf(target[i], onlyActive)); } i = a.length; //now get rid of any duplicates (tweens of arrays of objects could cause duplicates) while (--i > -1) { t = a[i]; j = i; while (--j > -1) { if (t === a[j]) { a.splice(i, 1); } } } } else if (target._gsTweenID) { a = _register(target).concat(); i = a.length; while (--i > -1) { if (a[i]._gc || (onlyActive && !a[i].isActive())) { a.splice(i, 1); } } } return a || []; }; TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) { if (typeof(onlyActive) === "object") { vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted) onlyActive = false; } var a = TweenLite.getTweensOf(target, onlyActive), i = a.length; while (--i > -1) { a[i]._kill(vars, target); } }; /* * ---------------------------------------------------------------- * TweenPlugin (could easily be split out as a separate file/class, but included for ease of use (so that people don't need to include another script call before loading plugins which is easy to forget) * ---------------------------------------------------------------- */ var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) { this._overwriteProps = (props || "").split(","); this._propName = this._overwriteProps[0]; this._priority = priority || 0; this._super = TweenPlugin.prototype; }, true); p = TweenPlugin.prototype; TweenPlugin.version = "1.19.0"; TweenPlugin.API = 2; p._firstPT = null; p._addTween = _addPropTween; p.setRatio = _setRatio; p._kill = function(lookup) { var a = this._overwriteProps, pt = this._firstPT, i; if (lookup[this._propName] != null) { this._overwriteProps = []; } else { i = a.length; while (--i > -1) { if (lookup[a[i]] != null) { a.splice(i, 1); } } } while (pt) { if (lookup[pt.n] != null) { if (pt._next) { pt._next._prev = pt._prev; } if (pt._prev) { pt._prev._next = pt._next; pt._prev = null; } else if (this._firstPT === pt) { this._firstPT = pt._next; } } pt = pt._next; } return false; }; p._mod = p._roundProps = function(lookup) { var pt = this._firstPT, val; while (pt) { val = lookup[this._propName] || (pt.n != null && lookup[ pt.n.split(this._propName + "_").join("") ]); if (val && typeof(val) === "function") { //some properties that are very plugin-specific add a prefix named after the _propName plus an underscore, so we need to ignore that extra stuff here. if (pt.f === 2) { pt.t._applyPT.m = val; } else { pt.m = val; } } pt = pt._next; } }; TweenLite._onPluginEvent = function(type, tween) { var pt = tween._firstPT, changed, pt2, first, last, next; if (type === "_onInitAllProps") { //sorts the PropTween linked list in order of priority because some plugins need to render earlier/later than others, like MotionBlurPlugin applies its effects after all x/y/alpha tweens have rendered on each frame. while (pt) { next = pt._next; pt2 = first; while (pt2 && pt2.pr > pt.pr) { pt2 = pt2._next; } if ((pt._prev = pt2 ? pt2._prev : last)) { pt._prev._next = pt; } else { first = pt; } if ((pt._next = pt2)) { pt2._prev = pt; } else { last = pt; } pt = next; } pt = tween._firstPT = first; } while (pt) { if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) { changed = true; } pt = pt._next; } return changed; }; TweenPlugin.activate = function(plugins) { var i = plugins.length; while (--i > -1) { if (plugins[i].API === TweenPlugin.API) { _plugins[(new plugins[i]())._propName] = plugins[i]; } } return true; }; //provides a more concise way to define plugins that have no dependencies besides TweenPlugin and TweenLite, wrapping common boilerplate stuff into one function (added in 1.9.0). You don't NEED to use this to define a plugin - the old way still works and can be useful in certain (rare) situations. _gsDefine.plugin = function(config) { if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; } var propName = config.propName, priority = config.priority || 0, overwriteProps = config.overwriteProps, map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_mod", mod:"_mod", initAll:"_onInitAllProps"}, Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin", function() { TweenPlugin.call(this, propName, priority); this._overwriteProps = overwriteProps || []; }, (config.global === true)), p = Plugin.prototype = new TweenPlugin(propName), prop; p.constructor = Plugin; Plugin.API = config.API; for (prop in map) { if (typeof(config[prop]) === "function") { p[map[prop]] = config[prop]; } } Plugin.version = config.version; TweenPlugin.activate([Plugin]); return Plugin; }; //now run through all the dependencies discovered and if any are missing, log that to the console as a warning. This is why it's best to have TweenLite load last - it can check all the dependencies for you. a = window._gsQueue; if (a) { for (i = 0; i < a.length; i++) { a[i](); } for (p in _defLookup) { if (!_defLookup[p].func) { window.console.log("GSAP encountered missing dependency: " + p); } } } _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated })((typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window, "TweenLite"); /*jslint plusplus: true, vars: true, indent: 2 */ /* convertPointFromPageToNode.js from convertPointFromPageToNode(element, event.pageX, event.pageY) -> {x, y} returns coordinate in element's local coordinate system (works properly with css transforms without perspective projection) convertPointFromNodeToPage(element, offsetX, offsetY) -> {x, y} returns coordinate in window's coordinate system (works properly with css transforms without perspective projection) */ (function () { 'use strict' var I = (typeof(WebKitCSSMatrix) == 'undefined') ? new DOMMatrix() : new WebKitCSSMatrix() function Point(x, y, z) { this.x = x this.y = y this.z = z } Point.prototype.transformBy = function (matrix) { var tmp = matrix.multiply(I.translate(this.x, this.y, this.z)) return new Point(tmp.m41, tmp.m42, tmp.m43) } function createMatrix(transform) { try { return (typeof(WebKitCSSMatrix) == 'undefined') ? new DOMMatrix(transform) : new WebKitCSSMatrix(transform) } catch(e) { console.warn(transform) console.warn(e.toString()) return I } } function getTransformationMatrix(element) { var transformationMatrix = I var x = element while (x != undefined && x !== x.ownerDocument.documentElement) { var computedStyle = window.getComputedStyle(x, undefined) var transform = computedStyle.transform || 'none' var c = transform === 'none' ? I : createMatrix(transform) transformationMatrix = c.multiply(transformationMatrix) x = x.parentNode } var w = element.offsetWidth var h = element.offsetHeight var i = 4 var left = +Infinity var top = +Infinity while (--i >= 0) { var p = new Point(i === 0 || i === 1 ? 0 : w, i === 0 || i === 3 ? 0 : h, 0).transformBy(transformationMatrix) if (p.x < left) { left = p.x } if (p.y < top) { top = p.y } } var rect = element.getBoundingClientRect() transformationMatrix = I.translate(window.pageXOffset + rect.left - left, window.pageYOffset + rect.top - top, 0) .multiply(transformationMatrix) return transformationMatrix } window.convertPointFromPageToNode = function (element, pageX, pageY) { return new Point(pageX, pageY, 0).transformBy( getTransformationMatrix(element).inverse()) } window.convertPointFromNodeToPage = function (element, offsetX, offsetY) { return new Point(offsetX, offsetY, 0).transformBy( getTransformationMatrix(element)) } }())