mixpanel-js-utils
Advanced tools
+3
| { | ||
| "presets": [ "es2015-rollup" ] | ||
| } |
Sorry, the diff of this file is not supported yet
+14
| { | ||
| "browser": true, | ||
| "curly": true, | ||
| "latedef": true, | ||
| "esnext": true, | ||
| "mocha": true, | ||
| "module": true, | ||
| "node": true, | ||
| "quotmark": true, | ||
| "strict": true, | ||
| "undef": true, | ||
| "unused": true, | ||
| "trailing": true | ||
| } |
Sorry, the diff of this file is not supported yet
+17
| # mixpanel-js-utils | ||
| A JS utility library for Mixpanel. Currently this is used to share code between our application and our JS library. | ||
| # Installing and using | ||
| Install via NPM: | ||
| `npm install mixpanel-js-utils` | ||
| Import to use: | ||
| `import utils from 'mixpanel-js-utils'` | ||
| # Developing | ||
| * Create a branch | ||
| * Add your changes | ||
| * Build and test your changes via `npm test` | ||
| * Update the version in `package.json` | ||
| * Commit, review, merge, and tag the new release | ||
| * `npm publish` to push to npm |
+204
| var jsdom = require('mocha-jsdom'); | ||
| var expect = require('chai').expect; | ||
| var utils = require('../build/index.cjs.js'); | ||
| jsdom(); | ||
| describe('elementHasOwnHandler', function() { | ||
| var elementHasOwnHandler = utils.elementHasOwnHandler; | ||
| describe('testTags', function() { | ||
| it('should return true for "interactive" tags, false for everything else', function() { | ||
| ['div', 'span', 'strong'].forEach(function(tagName) { | ||
| var el = document.createElement(tagName); | ||
| expect(false).to.equal(elementHasOwnHandler(el)); | ||
| }); | ||
| ['form', 'input', 'select', 'textarea', 'submit', 'a'].forEach(function(tagName) { | ||
| var el = document.createElement(tagName); | ||
| expect(true).to.equal(elementHasOwnHandler(el)); | ||
| }); | ||
| }); | ||
| }); | ||
| describe('testOnClick', function() { | ||
| it('should return true for elements with an onclick handler', function() { | ||
| var el = document.createElement('div'); | ||
| expect(false).to.equal(elementHasOwnHandler(el)); | ||
| el.setAttribute('onclick', 'test'); | ||
| expect(true).to.equal(elementHasOwnHandler(el)); | ||
| el = document.createElement('div'); | ||
| el.onclick = 'test'; | ||
| expect(true).to.equal(elementHasOwnHandler(el)); | ||
| }); | ||
| }); | ||
| describe('testJqueryEventHandlers', function() { | ||
| it('should return true for elements that have bound jQuery event handlers', function() { | ||
| var div = document.createElement('div'); | ||
| var divBound = document.createElement('div'); | ||
| var divBound2 = document.createElement('div'); | ||
| div.id = 'div'; | ||
| divBound.id = 'div_bound'; | ||
| divBound2.id = 'div_bound2'; | ||
| // mock | ||
| window.jQuery = { | ||
| _data: function(elem, name) { | ||
| var _bound = { | ||
| 'div_bound': { | ||
| 'events': ['some_event_binding'] | ||
| } | ||
| }; | ||
| return _bound[elem.id] && _bound[elem.id][name]; | ||
| } | ||
| }; | ||
| expect(false).to.equal(elementHasOwnHandler(div)); | ||
| expect(true).to.equal(elementHasOwnHandler(divBound)); | ||
| // mock | ||
| window.jQuery = { | ||
| data: function(elem, name) { | ||
| var _bound = { | ||
| 'div_bound2': { | ||
| 'events': ['some_event_binding'] | ||
| } | ||
| }; | ||
| return _bound[elem.id] && _bound[elem.id][name]; | ||
| }, | ||
| }; | ||
| expect(true).to.equal(elementHasOwnHandler(divBound2)); | ||
| }); | ||
| it('should return true for elements with data-events attribute', function() { | ||
| var div = document.createElement('div'); | ||
| expect(false).to.equal(elementHasOwnHandler(div)); | ||
| div.setAttribute('data-events', 'some_event_bindings'); | ||
| expect(true).to.equal(elementHasOwnHandler(div)); | ||
| }); | ||
| it('should return true if Event.observers contains the node', function() { | ||
| var div = document.createElement('div'); | ||
| // mock | ||
| window.Event = {}; | ||
| window.Event.observers = []; | ||
| expect(false).to.equal(elementHasOwnHandler(div)); | ||
| window.Event.observers.push([div]); | ||
| expect(true).to.equal(elementHasOwnHandler(div)); | ||
| }); | ||
| it('should return true if Event.cache contains a reference to the node', function() { | ||
| var div = document.createElement('div'); | ||
| div._eventId = 'event_1'; | ||
| // mock | ||
| window.Event = {}; | ||
| window.Event.cache = {}; | ||
| expect(false).to.equal(elementHasOwnHandler(div)); | ||
| window.Event.cache[div._eventId] = { | ||
| click: ['some_event_binding'] | ||
| }; | ||
| expect(true).to.equal(elementHasOwnHandler(div)); | ||
| var div2 = document.createElement('div'); | ||
| div2._prototypeEventID = ['event_1']; | ||
| window.Event.cache = {}; | ||
| window.Event.cache[div2._prototypeEventID[0]] = { | ||
| click: ['some_event_binding'] | ||
| }; | ||
| expect(true).to.equal(elementHasOwnHandler(div2)); | ||
| }); | ||
| }); | ||
| }); | ||
| describe('nearestInteractiveElement', function() { | ||
| var nearestInteractiveElement = utils.nearestInteractiveElement; | ||
| it('should return cached version', function() { | ||
| var div = document.createElement('div'); | ||
| var cache = {}; | ||
| expect(div).to.equal(nearestInteractiveElement(div, cache)); | ||
| cache[div] = { | ||
| element: div, | ||
| closest: 'cached' | ||
| }; | ||
| expect('cached').to.equal(nearestInteractiveElement(div, cache)); | ||
| }); | ||
| it('should return null for html and head tags', function() { | ||
| var html = document.createElement('html'); | ||
| expect(null).to.equal(nearestInteractiveElement(html)); | ||
| var head = document.createElement('head'); | ||
| expect(null).to.equal(nearestInteractiveElement(head)); | ||
| }); | ||
| describe('find closest element that seems interactive', function() { | ||
| var buildTestDom = function() { | ||
| var body = document.createElement('body'); | ||
| var div1 = document.createElement('div'); | ||
| div1.id = 'div_1'; | ||
| var div2 = document.createElement('div'); | ||
| div2.id = 'div_2'; | ||
| var div3 = document.createElement('div'); | ||
| div3.id = 'div_3'; | ||
| body.appendChild(div1); | ||
| div1.appendChild(div2); | ||
| div2.appendChild(div3); | ||
| return [body, div1, div2, div3]; | ||
| }; | ||
| it('should return the original element if no element is matched', function() { | ||
| var elements = buildTestDom(); | ||
| var div3 = elements[3]; | ||
| expect(div3.id).to.equal(nearestInteractiveElement(div3).id); | ||
| }); | ||
| it('should match elements with data- attributes', function() { | ||
| var elements = buildTestDom(); | ||
| var div2 = elements[2]; | ||
| var div3 = elements[3]; | ||
| div2.setAttribute('data-test', 'test'); | ||
| expect(div2.id).to.equal(nearestInteractiveElement(div3).id); | ||
| }); | ||
| it('should ignore elements with pointer-events css set to none', function() { | ||
| var elements = buildTestDom(); | ||
| var div1 = elements[1]; | ||
| var div2 = elements[2]; | ||
| var div3 = elements[3]; | ||
| div1.setAttribute('data-test', 'test'); | ||
| div2.setAttribute('data-test', 'test'); | ||
| div2.style['pointer-events'] = 'none'; | ||
| expect(div1.id).to.equal(nearestInteractiveElement(div3).id); | ||
| }); | ||
| it('should match the child element if the cursor goes to default or auto from something else', function() { | ||
| var elements = buildTestDom(); | ||
| var div1 = elements[1]; | ||
| var div2 = elements[2]; | ||
| var div3 = elements[3]; | ||
| div2.style.cursor = 'pointer'; | ||
| div1.style.cursor = 'default'; | ||
| expect(div2.id).to.equal(nearestInteractiveElement(div3).id); | ||
| }); | ||
| it('should match elements with their own click handler', function() { | ||
| var elements = buildTestDom(); | ||
| var div1 = elements[1]; | ||
| var div3 = elements[3]; | ||
| div1.onclick = 'test'; | ||
| expect(div1.id).to.equal(nearestInteractiveElement(div3).id); | ||
| }); | ||
| }); | ||
| }); |
+45
-45
| 'use strict'; | ||
| var hasOwnHandler = function (element) { | ||
| var tests = [ | ||
| function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) !== -1; | ||
| }, | ||
| function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, | ||
| function (node) { | ||
| return !!(jQuery._data || jQuery.data)(node, 'events') | ||
| }, | ||
| function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return !!node.getAttributes('data-events'); | ||
| }, | ||
| function (node) { | ||
| // jquery 1.5.x | ||
| for(var i = 0; i < Event.observers.length; i++) { | ||
| var observer = Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| var elementHasOwnHandler = function elementHasOwnHandler(element) { | ||
| var tests = [function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) > -1; | ||
| }, function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, function (node) { | ||
| return !!(window.jQuery._data || window.jQuery.data)(node, 'events'); | ||
| }, function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return node.getAttribute('data-events'); | ||
| }, function (node) { | ||
| // jquery 1.5.x | ||
| for (var i = 0; i < window.Event.observers.length; i++) { | ||
| var observer = window.Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| return false; | ||
| }, | ||
| function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| } | ||
| ]; | ||
| return false; | ||
| }, function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return window.Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| }]; | ||
@@ -46,3 +39,3 @@ for (var i = 0; i < tests.length; i++) { | ||
| var nearestInteractiveElement = function(element, cache) { | ||
| var nearestInteractiveElement = function nearestInteractiveElement(element, cache) { | ||
| if (cache && cache[element] && cache[element].element === element) { | ||
@@ -52,13 +45,14 @@ return cache[element].closest; | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { // can't call css on these | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { | ||
| // can't call css on these | ||
| return null; | ||
| } | ||
| // next, try to find the closest element with a click handler | ||
| var child, closest; | ||
| // next, try to find the closest element that seems interactive | ||
| var child = void 0, | ||
| closest = void 0; | ||
| var candidate = element; | ||
| var visited_candidates = []; | ||
| for (var candidate = element; | ||
| !closest && candidate && !(candidate.tagName.toLowerCase() === 'body'); | ||
| child = candidate, candidate = candidate.parentElement) { | ||
| do { | ||
| visited_candidates.push(candidate); | ||
@@ -68,2 +62,4 @@ | ||
| if (candidate.style['pointer-events'] === 'none') { | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| continue; | ||
@@ -76,4 +72,4 @@ } | ||
| var childStyles = window.getComputedStyle(child); | ||
| var cursor = candidateStyles['cursor']; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles['cursor']) { | ||
| var cursor = candidateStyles.cursor; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles.cursor) { | ||
| closest = child; | ||
@@ -86,3 +82,3 @@ break; | ||
| var attrs = candidate.attributes; | ||
| for(var i = 0; i < attrs.length; i++) { | ||
| for (var i = 0; i < attrs.length; i++) { | ||
| if (/^data\-/.test(attrs[i].name)) { | ||
@@ -95,13 +91,16 @@ closest = candidate; | ||
| // if the element has its own click handler, it is almost certainly clickable | ||
| if (hasOwnHandler(candidate)) { | ||
| if (elementHasOwnHandler(candidate)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| } | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| } while (candidate && candidate.tagName.toLowerCase() !== 'body'); | ||
| if (cache && closest) { | ||
| for (var i = 0; i < visited_candidates.length; i++) { | ||
| var candidate = visited_candidates[i]; | ||
| cache[candidate] = closest; | ||
| }; | ||
| for (var _i = 0; _i < visited_candidates.length; _i++) { | ||
| var _candidate = visited_candidates[_i]; | ||
| cache[_candidate] = closest; | ||
| } | ||
| cache[element] = { | ||
@@ -116,2 +115,3 @@ closest: closest, | ||
| exports.elementHasOwnHandler = elementHasOwnHandler; | ||
| exports.nearestInteractiveElement = nearestInteractiveElement; |
+48
-46
@@ -1,32 +0,27 @@ | ||
| var hasOwnHandler = function (element) { | ||
| var tests = [ | ||
| function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) !== -1; | ||
| }, | ||
| function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, | ||
| function (node) { | ||
| return !!(jQuery._data || jQuery.data)(node, 'events') | ||
| }, | ||
| function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return !!node.getAttributes('data-events'); | ||
| }, | ||
| function (node) { | ||
| // jquery 1.5.x | ||
| for(var i = 0; i < Event.observers.length; i++) { | ||
| var observer = Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| 'use strict'; | ||
| var elementHasOwnHandler = function elementHasOwnHandler(element) { | ||
| var tests = [function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) > -1; | ||
| }, function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, function (node) { | ||
| return !!(window.jQuery._data || window.jQuery.data)(node, 'events'); | ||
| }, function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return node.getAttribute('data-events'); | ||
| }, function (node) { | ||
| // jquery 1.5.x | ||
| for (var i = 0; i < window.Event.observers.length; i++) { | ||
| var observer = window.Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| return false; | ||
| }, | ||
| function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| } | ||
| ]; | ||
| return false; | ||
| }, function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return window.Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| }]; | ||
@@ -44,3 +39,3 @@ for (var i = 0; i < tests.length; i++) { | ||
| var nearestInteractiveElement = function(element, cache) { | ||
| var nearestInteractiveElement = function nearestInteractiveElement(element, cache) { | ||
| if (cache && cache[element] && cache[element].element === element) { | ||
@@ -50,13 +45,14 @@ return cache[element].closest; | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { // can't call css on these | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { | ||
| // can't call css on these | ||
| return null; | ||
| } | ||
| // next, try to find the closest element with a click handler | ||
| var child, closest; | ||
| // next, try to find the closest element that seems interactive | ||
| var child = void 0, | ||
| closest = void 0; | ||
| var candidate = element; | ||
| var visited_candidates = []; | ||
| for (var candidate = element; | ||
| !closest && candidate && !(candidate.tagName.toLowerCase() === 'body'); | ||
| child = candidate, candidate = candidate.parentElement) { | ||
| do { | ||
| visited_candidates.push(candidate); | ||
@@ -66,2 +62,4 @@ | ||
| if (candidate.style['pointer-events'] === 'none') { | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| continue; | ||
@@ -74,4 +72,4 @@ } | ||
| var childStyles = window.getComputedStyle(child); | ||
| var cursor = candidateStyles['cursor']; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles['cursor']) { | ||
| var cursor = candidateStyles.cursor; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles.cursor) { | ||
| closest = child; | ||
@@ -84,3 +82,3 @@ break; | ||
| var attrs = candidate.attributes; | ||
| for(var i = 0; i < attrs.length; i++) { | ||
| for (var i = 0; i < attrs.length; i++) { | ||
| if (/^data\-/.test(attrs[i].name)) { | ||
@@ -93,13 +91,16 @@ closest = candidate; | ||
| // if the element has its own click handler, it is almost certainly clickable | ||
| if (hasOwnHandler(candidate)) { | ||
| if (elementHasOwnHandler(candidate)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| } | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| } while (candidate && candidate.tagName.toLowerCase() !== 'body'); | ||
| if (cache && closest) { | ||
| for (var i = 0; i < visited_candidates.length; i++) { | ||
| var candidate = visited_candidates[i]; | ||
| cache[candidate] = closest; | ||
| }; | ||
| for (var _i = 0; _i < visited_candidates.length; _i++) { | ||
| var _candidate = visited_candidates[_i]; | ||
| cache[_candidate] = closest; | ||
| } | ||
| cache[element] = { | ||
@@ -114,2 +115,3 @@ closest: closest, | ||
| export { nearestInteractiveElement }; | ||
| exports.elementHasOwnHandler = elementHasOwnHandler; | ||
| exports.nearestInteractiveElement = nearestInteractiveElement; |
+26
-5
| { | ||
| "name": "mixpanel-js-utils", | ||
| "version": "0.0.5", | ||
| "version": "0.0.6", | ||
| "description": "Javascript utilities for use across Mixpanel products", | ||
@@ -8,4 +8,10 @@ "main": "build/index.cjs.js", | ||
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1", | ||
| "build": "./node_modules/rollup/bin/rollup -c rollup.config.cjs.js && ./node_modules/rollup/bin/rollup -c rollup.config.es6.js" | ||
| "test": "npm run-script build && ./node_modules/mocha/bin/mocha test/index.js", | ||
| "build": "./node_modules/rollup/bin/rollup -c rollup.config.cjs.js && ./node_modules/rollup/bin/rollup -c rollup.config.es6.js", | ||
| "lint": "jshint .", | ||
| "validate": "npm ls", | ||
| "pre-commit": [ | ||
| "lint", | ||
| "test" | ||
| ] | ||
| }, | ||
@@ -23,4 +29,19 @@ "repository": { | ||
| "devDependencies": { | ||
| "rollup": "^0.25.4" | ||
| } | ||
| "babel": "^6.5.2", | ||
| "babel-preset-es2015-rollup": "^1.1.1", | ||
| "chai": "^3.5.0", | ||
| "js-fixtures": "^1.5.3", | ||
| "jsdom": "^8.1.0", | ||
| "mocha": "^2.4.5", | ||
| "mocha-jsdom": "^1.1.0", | ||
| "precommit-hook": "^3.0.0", | ||
| "rollup": "^0.25.4", | ||
| "rollup-plugin-babel": "^2.4.0", | ||
| "rollup-plugin-multi-entry": "^1.2.0" | ||
| }, | ||
| "pre-commit": [ | ||
| "lint", | ||
| "validate", | ||
| "test" | ||
| ] | ||
| } |
@@ -0,5 +1,12 @@ | ||
| import babel from 'rollup-plugin-babel'; | ||
| export default { | ||
| entry: 'src/index.js', | ||
| format: 'cjs', | ||
| dest: 'build/index.cjs.js' | ||
| entry: './src/index.js', | ||
| dest: './build/index.cjs.js', | ||
| plugins: [ | ||
| babel({ | ||
| exclude: 'node_modules/**' | ||
| }) | ||
| ] | ||
| }; |
@@ -0,5 +1,12 @@ | ||
| import babel from 'rollup-plugin-babel'; | ||
| export default { | ||
| format: 'cjs', | ||
| entry: 'src/index.js', | ||
| format: 'es6', | ||
| dest: 'build/index.es6.js' | ||
| dest: 'build/index.es6.js', | ||
| plugins: [ | ||
| babel({ | ||
| exclude: 'node_modules/**' | ||
| }) | ||
| ] | ||
| }; |
+113
-4
@@ -1,4 +0,113 @@ | ||
| import nearestInteractiveElement from './nearestInteractiveElement'; | ||
| export { | ||
| nearestInteractiveElement | ||
| } | ||
| var elementHasOwnHandler = function (element) { | ||
| const tests = [ | ||
| function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) > -1; | ||
| }, | ||
| function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, | ||
| function (node) { | ||
| return !!(window.jQuery._data || window.jQuery.data)(node, 'events'); | ||
| }, | ||
| function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return node.getAttribute('data-events'); | ||
| }, | ||
| function (node) { | ||
| // jquery 1.5.x | ||
| for(let i = 0; i < window.Event.observers.length; i++) { | ||
| const observer = window.Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| }, | ||
| function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return window.Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| } | ||
| ]; | ||
| for (let i = 0; i < tests.length; i++) { | ||
| const test = tests[i]; | ||
| try { | ||
| if (test(element)) { | ||
| return true; | ||
| } | ||
| } catch (e) {} | ||
| } | ||
| return false; | ||
| }; | ||
| var nearestInteractiveElement = function(element, cache) { | ||
| if (cache && cache[element] && cache[element].element === element) { | ||
| return cache[element].closest; | ||
| } | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { // can't call css on these | ||
| return null; | ||
| } | ||
| // next, try to find the closest element that seems interactive | ||
| let child, closest; | ||
| let candidate = element; | ||
| let visited_candidates = []; | ||
| do { | ||
| visited_candidates.push(candidate); | ||
| // skip candidates that don't accept pointer events | ||
| if (candidate.style['pointer-events'] === 'none') { | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| continue; | ||
| } | ||
| // if the cursor becomes default, assume that the child was clickable | ||
| if (child) { | ||
| const candidateStyles = window.getComputedStyle(candidate); | ||
| const childStyles = window.getComputedStyle(child); | ||
| const cursor = candidateStyles.cursor; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles.cursor) { | ||
| closest = child; | ||
| break; | ||
| } | ||
| } | ||
| // if there are data- attributes we assume that it's used for something interactive | ||
| const attrs = candidate.attributes; | ||
| for(let i = 0; i < attrs.length; i++) { | ||
| if (/^data\-/.test(attrs[i].name)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| } | ||
| // if the element has its own click handler, it is almost certainly clickable | ||
| if (elementHasOwnHandler(candidate)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| child = candidate; | ||
| candidate = candidate.parentElement; | ||
| } while (candidate && candidate.tagName.toLowerCase() !== 'body'); | ||
| if (cache && closest) { | ||
| for (let i = 0; i < visited_candidates.length; i++) { | ||
| const candidate = visited_candidates[i]; | ||
| cache[candidate] = closest; | ||
| } | ||
| cache[element] = { | ||
| closest: closest, | ||
| element: element | ||
| }; | ||
| } | ||
| return closest || element; | ||
| }; | ||
| export { elementHasOwnHandler, nearestInteractiveElement }; |
| var hasOwnHandler = function (element) { | ||
| var tests = [ | ||
| function (node) { | ||
| return ['form', 'input', 'select', 'textarea', 'submit', 'a'].indexOf(node.tagName.toLowerCase()) !== -1; | ||
| }, | ||
| function (node) { | ||
| // for onclick properties and attributes | ||
| return node.onclick || node.getAttribute('onclick'); | ||
| }, | ||
| function (node) { | ||
| return !!(jQuery._data || jQuery.data)(node, 'events') | ||
| }, | ||
| function (node) { | ||
| // jquery 1.3.x and 1.4x | ||
| return !!node.getAttributes('data-events'); | ||
| }, | ||
| function (node) { | ||
| // jquery 1.5.x | ||
| for(var i = 0; i < Event.observers.length; i++) { | ||
| var observer = Event.observers[i]; | ||
| if ((observer || [])[0] === node) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| }, | ||
| function (node) { | ||
| // jquery 1.6 to 1.6.0.3 | ||
| return Event.cache[node._eventId || node._prototypeEventID[0]].click[0]; | ||
| } | ||
| ]; | ||
| for (var i = 0; i < tests.length; i++) { | ||
| var test = tests[i]; | ||
| try { | ||
| if (test(element)) { | ||
| return true; | ||
| } | ||
| } catch (e) {} | ||
| } | ||
| return false; | ||
| }; | ||
| var nearestInteractiveElement = function(element, cache) { | ||
| if (cache && cache[element] && cache[element].element === element) { | ||
| return cache[element].closest; | ||
| } | ||
| if (['html', 'head'].indexOf(element.tagName.toLowerCase()) > -1) { // can't call css on these | ||
| return null; | ||
| } | ||
| // next, try to find the closest element with a click handler | ||
| var child, closest; | ||
| var visited_candidates = []; | ||
| for (var candidate = element; | ||
| !closest && candidate && !(candidate.tagName.toLowerCase() === 'body'); | ||
| child = candidate, candidate = candidate.parentElement) { | ||
| visited_candidates.push(candidate); | ||
| // skip candidates that don't accept pointer events | ||
| if (candidate.style['pointer-events'] === 'none') { | ||
| continue; | ||
| } | ||
| // if the cursor becomes default, assume that the child was clickable | ||
| if (child) { | ||
| var candidateStyles = window.getComputedStyle(candidate); | ||
| var childStyles = window.getComputedStyle(child); | ||
| var cursor = candidateStyles['cursor']; | ||
| if ((cursor === 'default' || cursor === 'auto') && cursor !== childStyles['cursor']) { | ||
| closest = child; | ||
| break; | ||
| } | ||
| } | ||
| // if there are data- attributes we assume that it's used for something interactive | ||
| var attrs = candidate.attributes; | ||
| for(var i = 0; i < attrs.length; i++) { | ||
| if (/^data\-/.test(attrs[i].name)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| } | ||
| // if the element has its own click handler, it is almost certainly clickable | ||
| if (hasOwnHandler(candidate)) { | ||
| closest = candidate; | ||
| break; | ||
| } | ||
| } | ||
| if (cache && closest) { | ||
| for (var i = 0; i < visited_candidates.length; i++) { | ||
| var candidate = visited_candidates[i]; | ||
| cache[candidate] = closest; | ||
| }; | ||
| cache[element] = { | ||
| closest: closest, | ||
| element: element | ||
| }; | ||
| } | ||
| return closest || element; | ||
| }; | ||
| export default nearestInteractiveElement; |
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
44052881
382702.23%13
62.5%482
60.13%2
-33.33%1
-50%18
Infinity%11
1000%2
100%