diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2019-08-31 19:47:00 -0400 | 
|---|---|---|
| committer | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2019-09-02 11:43:33 -0400 | 
| commit | 737a5ee8a814bc89ac40f99264e8835c47f77387 (patch) | |
| tree | cd4bdaca2468c784cee8817eabf395b465c5cec3 /ext/fg/js | |
| parent | d296ebd593d125d131b5bf9974e19d13ca3b3b3f (diff) | |
Allow elements behind other transparent elements to be scanned
Diffstat (limited to 'ext/fg/js')
| -rw-r--r-- | ext/fg/js/document.js | 81 | 
1 files changed, 78 insertions, 3 deletions
| diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index a64b6c04..8bb857e7 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -17,6 +17,8 @@   */ +const REGEX_TRANSPARENT_COLOR = /rgba\s*\([^\)]*,\s*0(?:\.0+)?\s*\)/; +  function docSetImposterStyle(style, propertyName, value) {      style.setProperty(propertyName, value, 'important');  } @@ -88,10 +90,11 @@ function docImposterCreate(element, isTextarea) {  }  function docRangeFromPoint({x, y}) { -    const element = document.elementFromPoint(x, y); +    const elements = document.elementsFromPoint(x, y);      let imposter = null;      let imposterContainer = null; -    if (element) { +    if (elements.length > 0) { +        const element = elements[0];          switch (element.nodeName) {              case 'IMG':              case 'BUTTON': @@ -105,7 +108,7 @@ function docRangeFromPoint({x, y}) {          }      } -    const range = caretRangeFromPoint(x, y); +    const range = caretRangeFromPointExt(x, y, elements);      if (range !== null) {          if (imposter !== null) {              docSetImposterStyle(imposterContainer.style, 'z-index', '-2147483646'); @@ -261,3 +264,75 @@ const caretRangeFromPoint = (() => {      // No support      return () => null;  })(); + +function caretRangeFromPointExt(x, y, elements) { +    const modifications = []; +    try { +        let i = 0; +        while (true) { +            const range = caretRangeFromPoint(x, y); +            if (range === null) { +                return null; +            } + +            const inRange = isPointInRange(x, y, range); +            if (inRange) { +                return range; +            } + +            i = disableTransparentElement(elements, i, modifications); +            if (i < 0) { +                return null; +            } +        } +    } finally { +        if (modifications.length > 0) { +            restoreElementStyleModifications(modifications); +        } +    } +} + +function disableTransparentElement(elements, i, modifications) { +    while (true) { +        if (i >= elements.length) { +            return -1; +        } + +        const element = elements[i++]; +        if (isElementTransparent(element)) { +            const style = element.hasAttribute('style') ? element.getAttribute('style') : null; +            modifications.push({element, style}); +            element.style.pointerEvents = 'none'; +            return i; +        } +    } +} + +function restoreElementStyleModifications(modifications) { +    for (const {element, style} of modifications) { +        if (style === null) { +            element.removeAttribute('style'); +        } else { +            element.setAttribute('style', style); +        } +    } +} + +function isElementTransparent(element) { +    if ( +        element === document.body || +        element === document.documentElement +    ) { +        return false; +    } +    const style = window.getComputedStyle(element); +    return ( +        parseFloat(style.opacity) < 0 || +        style.visibility === 'hidden' || +        (style.backgroundImage === 'none' && isColorTransparent(style.backgroundColor)) +    ); +} + +function isColorTransparent(cssColor) { +    return REGEX_TRANSPARENT_COLOR.test(cssColor); +} |