From a199335790aec50cf3ed7cc27c5b407875c37107 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 10:15:54 +0000
Subject: Use the DOM rather than XPath to detect clickable elements
---
lib/dom_utils.coffee | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index a0ac0bd3..26fa9b81 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -41,6 +41,42 @@ DomUtils =
if (namespace == "xhtml") then "http://www.w3.org/1999/xhtml" else null
document.evaluate(xpath, document.documentElement, namespaceResolver, resultType, null)
+ #
+ # Returns all the clickable element children of contextNode. This also can include contextNode itself.
+ #
+ getClickableElements: (contextNode = document.documentElement) ->
+ elements = Array::slice.call(contextNode?.getElementsByTagName "*")
+ elements.unshift contextNode # Check the contextNode as well.
+ clickableElements = []
+ for element in elements
+ isClickable = false
+ tagName = element.tagName.toLowerCase()
+ isClickable = (->
+ if element.hasAttribute "onclick"
+ true
+ else if element.hasAttribute "tabindex"
+ true
+ else if element.getAttribute "role" in ["button", "link"]
+ true
+ else if element.getAttribute("class")?.toLowerCase().indexOf("button") >= 0
+ true
+ else if element.getAttribute("contentEditable")?.toLowerCase() in ["", "contentEditable", "true"]
+ true
+ else if tagName == "a"
+ true
+ else if tagName == "area"
+ element.hasAttribute "href"
+ else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
+ not (element.disabled or element.hasAttribute "readonly")
+ else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or
+ tagName in ["button", "select"]
+ not element.disabled
+ else
+ false
+ )()
+ clickableElements.push element if isClickable
+ clickableElements
+
#
# Returns the first visible clientRect of an element if it exists. Otherwise it returns null.
#
--
cgit v1.2.3
From c80ad2c367f873f2b2547b60cebe49715a85ffe4 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 10:40:02 +0000
Subject: Treat area elements as being at the point of their img element
---
lib/dom_utils.coffee | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 26fa9b81..152a378e 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -64,8 +64,13 @@ DomUtils =
true
else if tagName == "a"
true
- else if tagName == "area"
- element.hasAttribute "href"
+ else if tagName == "img"
+ mapName = element.getAttribute "usemap"
+ if mapName
+ map = document.querySelector(mapName.replace /^#/, "")
+ areas = Array::slice.call(map.getElementsByTagName "area")
+ elements.concat areas
+ false
else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
not (element.disabled or element.hasAttribute "readonly")
else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or
--
cgit v1.2.3
From 8c8ec835d673f0ec1cce242cf26cca077c845064 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 11:08:07 +0000
Subject: Use element.readOnly instead of getAttribute "readonly"
---
lib/dom_utils.coffee | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 152a378e..46bf3639 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -72,7 +72,7 @@ DomUtils =
elements.concat areas
false
else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
- not (element.disabled or element.hasAttribute "readonly")
+ not (element.disabled or element.readOnly)
else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or
tagName in ["button", "select"]
not element.disabled
--
cgit v1.2.3
From c7e2f1cdef2d5e99761d7bb8ecbad91f89de6958 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 11:12:58 +0000
Subject: Inline DomUtils.getClickableElements
---
lib/dom_utils.coffee | 41 -----------------------------------------
1 file changed, 41 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 46bf3639..a0ac0bd3 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -41,47 +41,6 @@ DomUtils =
if (namespace == "xhtml") then "http://www.w3.org/1999/xhtml" else null
document.evaluate(xpath, document.documentElement, namespaceResolver, resultType, null)
- #
- # Returns all the clickable element children of contextNode. This also can include contextNode itself.
- #
- getClickableElements: (contextNode = document.documentElement) ->
- elements = Array::slice.call(contextNode?.getElementsByTagName "*")
- elements.unshift contextNode # Check the contextNode as well.
- clickableElements = []
- for element in elements
- isClickable = false
- tagName = element.tagName.toLowerCase()
- isClickable = (->
- if element.hasAttribute "onclick"
- true
- else if element.hasAttribute "tabindex"
- true
- else if element.getAttribute "role" in ["button", "link"]
- true
- else if element.getAttribute("class")?.toLowerCase().indexOf("button") >= 0
- true
- else if element.getAttribute("contentEditable")?.toLowerCase() in ["", "contentEditable", "true"]
- true
- else if tagName == "a"
- true
- else if tagName == "img"
- mapName = element.getAttribute "usemap"
- if mapName
- map = document.querySelector(mapName.replace /^#/, "")
- areas = Array::slice.call(map.getElementsByTagName "area")
- elements.concat areas
- false
- else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
- not (element.disabled or element.readOnly)
- else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or
- tagName in ["button", "select"]
- not element.disabled
- else
- false
- )()
- clickableElements.push element if isClickable
- clickableElements
-
#
# Returns the first visible clientRect of an element if it exists. Otherwise it returns null.
#
--
cgit v1.2.3
From 5f9290693ab0f35c46cea6cea0a9f5c06b4ee0ad Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 12:27:40 +0000
Subject: Combine rectangle calculation and clickable element detection
---
lib/dom_utils.coffee | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index a0ac0bd3..3d7e805f 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -90,6 +90,34 @@ DomUtils =
return childClientRect
null
+ getClientRectsForAreas: (imgClientRect, areas) ->
+ rects = []
+ for area in areas
+ coords = area.coords.split(",").map((coord) -> parseInt(coord, 10))
+ shape = area.shape.toLowerCase()
+ if shape == "rect"
+ [x1, y1, x2, y2] = coords
+ else if shape == "circle"
+ [x, y, r] = coords
+ x1 = x - r
+ x2 = x + r
+ y1 = y - r
+ y2 = y + r
+ else # For polygons and unknown shapes, don't return a rectangle.
+ # TODO(mrmr1993): revisit this.
+ continue
+
+ rect =
+ top: imgClientRect.top + y1
+ left: imgClientRect.left + x1
+ right: imgClientRect.left + x2
+ bottom: imgClientRect.top + y2
+ width: x2 - x1
+ height: y2 - y1
+
+ rects.push {element: area, rect: rect} unless isNaN rect.top
+ rects
+
#
# Selectable means that we should use the simulateSelect method to activate the element instead of a click.
#
--
cgit v1.2.3
From 62686e83d690919f00afe1ac7f5955cecb1d2b2f Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 12:31:09 +0000
Subject: Try to make image map rectangles work better
---
lib/dom_utils.coffee | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 3d7e805f..842dda0f 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -95,17 +95,18 @@ DomUtils =
for area in areas
coords = area.coords.split(",").map((coord) -> parseInt(coord, 10))
shape = area.shape.toLowerCase()
- if shape == "rect"
+ if shape == "rect" or coords.length == 4
[x1, y1, x2, y2] = coords
- else if shape == "circle"
+ else if shape == "circle" or coords.length == 3
[x, y, r] = coords
x1 = x - r
x2 = x + r
y1 = y - r
y2 = y + r
- else # For polygons and unknown shapes, don't return a rectangle.
- # TODO(mrmr1993): revisit this.
- continue
+ else
+ # Just consider the rectangle surrounding the first two points in a polygon. It's possible to do
+ # something more sophisticated, but likely not worth the effort.
+ [x1, y1, x2, y2] = coords
rect =
top: imgClientRect.top + y1
--
cgit v1.2.3
From c2ab9aaa27b5fbf1f065743772c6f04dd3c5f39d Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 12:39:22 +0000
Subject: Don't show link hints for offscreen image maps
---
lib/dom_utils.coffee | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 842dda0f..8ade58bb 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -52,18 +52,9 @@ DomUtils =
} for clientRect in element.getClientRects())
for clientRect in clientRects
- if (clientRect.top < 0)
- clientRect.height += clientRect.top
- clientRect.top = 0
+ clientRect = @cropRectToVisible clientRect
- if (clientRect.left < 0)
- clientRect.width += clientRect.left
- clientRect.left = 0
-
- if (clientRect.top >= window.innerHeight - 4 || clientRect.left >= window.innerWidth - 4)
- continue
-
- if (clientRect.width < 3 || clientRect.height < 3)
+ if (!clientRect || clientRect.width < 3 || clientRect.height < 3)
continue
# eliminate invisible elements (see test_harnesses/visibility_test.html)
@@ -90,6 +81,21 @@ DomUtils =
return childClientRect
null
+ cropRectToVisible: (rect) ->
+ if (rect.top < 0)
+ rect.height += rect.top
+ rect.top = 0
+
+ if (rect.left < 0)
+ rect.width += rect.left
+ rect.left = 0
+
+ if (rect.top >= window.innerHeight - 4 || rect.left >= window.innerWidth - 4)
+ null
+ else
+ rect
+
+
getClientRectsForAreas: (imgClientRect, areas) ->
rects = []
for area in areas
@@ -108,7 +114,7 @@ DomUtils =
# something more sophisticated, but likely not worth the effort.
[x1, y1, x2, y2] = coords
- rect =
+ rect = @cropRectToVisible
top: imgClientRect.top + y1
left: imgClientRect.left + x1
right: imgClientRect.left + x2
@@ -116,7 +122,7 @@ DomUtils =
width: x2 - x1
height: y2 - y1
- rects.push {element: area, rect: rect} unless isNaN rect.top
+ rects.push {element: area, rect: rect} unless not rect or isNaN rect.top
rects
#
--
cgit v1.2.3
From 833942ae06f680bc1949a7bced4719b707950568 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 12:47:38 +0000
Subject: Stop ignoring clickable opacity: none; elements
Some websites (notably Facebook) use `opacity: none;` to show an image in
the place of a less-customisable element (eg. an ``).
To not show link hints for such transparent elements is confusing and
often the wrong thing to do.
---
lib/dom_utils.coffee | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 8ade58bb..1e2cc812 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -60,8 +60,7 @@ DomUtils =
# eliminate invisible elements (see test_harnesses/visibility_test.html)
computedStyle = window.getComputedStyle(element, null)
if (computedStyle.getPropertyValue('visibility') != 'visible' ||
- computedStyle.getPropertyValue('display') == 'none' ||
- computedStyle.getPropertyValue('opacity') == '0')
+ computedStyle.getPropertyValue('display') == 'none')
continue
return clientRect
--
cgit v1.2.3
From 158b3f09fd222b0e93510dc17521833de73bcf88 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Wed, 17 Dec 2014 13:49:29 +0000
Subject: Unify two loops into one
---
lib/dom_utils.coffee | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 1e2cc812..aaa93923 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -51,20 +51,6 @@ DomUtils =
width: clientRect.width, height: clientRect.height
} for clientRect in element.getClientRects())
- for clientRect in clientRects
- clientRect = @cropRectToVisible clientRect
-
- if (!clientRect || clientRect.width < 3 || clientRect.height < 3)
- continue
-
- # eliminate invisible elements (see test_harnesses/visibility_test.html)
- computedStyle = window.getComputedStyle(element, null)
- if (computedStyle.getPropertyValue('visibility') != 'visible' ||
- computedStyle.getPropertyValue('display') == 'none')
- continue
-
- return clientRect
-
for clientRect in clientRects
# If the link has zero dimensions, it may be wrapping visible
# but floated elements. Check for this.
@@ -78,6 +64,21 @@ DomUtils =
childClientRect = @getVisibleClientRect(child)
continue if (childClientRect == null)
return childClientRect
+
+ else
+ clientRect = @cropRectToVisible clientRect
+
+ if (!clientRect || clientRect.width < 3 || clientRect.height < 3)
+ continue
+
+ # eliminate invisible elements (see test_harnesses/visibility_test.html)
+ computedStyle = window.getComputedStyle(element, null)
+ if (computedStyle.getPropertyValue('visibility') != 'visible' ||
+ computedStyle.getPropertyValue('display') == 'none')
+ continue
+
+ return clientRect
+
null
cropRectToVisible: (rect) ->
--
cgit v1.2.3
From 855e9a4e19ab0926f5531c37272f00a715f45ed8 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Thu, 18 Dec 2014 10:33:09 +0000
Subject: Remove overlapping rects from link hints
---
lib/dom_utils.coffee | 9 ++------
lib/utils.coffee | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 7 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index aaa93923..7e19a7fc 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -114,13 +114,8 @@ DomUtils =
# something more sophisticated, but likely not worth the effort.
[x1, y1, x2, y2] = coords
- rect = @cropRectToVisible
- top: imgClientRect.top + y1
- left: imgClientRect.left + x1
- right: imgClientRect.left + x2
- bottom: imgClientRect.top + y2
- width: x2 - x1
- height: y2 - y1
+ rect = Utils.shiftRect (Utils.createRect x1, y1, x2, y2), imgClientRect.left, imgClientRect.top
+ rect = @cropRectToVisible rect
rects.push {element: area, rect: rect} unless not rect or isNaN rect.top
rects
diff --git a/lib/utils.coffee b/lib/utils.coffee
index b7f8731a..6cc45f32 100644
--- a/lib/utils.coffee
+++ b/lib/utils.coffee
@@ -136,6 +136,66 @@ Utils =
# locale-sensitive uppercase detection
hasUpperCase: (s) -> s.toLowerCase() != s
+ # Create a rect given the top left and bottom right corners.
+ createRect: (x1, y1, x2, y2) ->
+ bottom: y2
+ top: y1
+ left: x1
+ right: x2
+ width: x2 - x1
+ height: y2 - y1
+
+ # Translate a rect by x horizontally and y vertically.
+ shiftRect: (rect, x, y) ->
+ bottom: rect.bottom + y
+ top: rect.top + y
+ left: rect.left + x
+ right: rect.right + x
+ width: rect.width
+ height: rect.height
+
+ # Subtract rect2 from rect1, returning an array of rects which are in rect1 but not rect2.
+ subtractRect: (rect1, rect2_) ->
+ # Bound rect2 by rect1
+ rect2 = {}
+ rect2 = @createRect(
+ Math.max(rect1.left, rect2_.left),
+ Math.max(rect1.top, rect2_.top),
+ Math.min(rect1.right, rect2_.right),
+ Math.min(rect1.bottom, rect2_.bottom)
+ )
+
+ # If bounding rect2 has made the width or height negative, rect1 does not contain rect2.
+ return [rect1] if rect2.width < 0 or rect2.height < 0
+
+ #
+ # All the possible rects, in the order
+ # +-+-+-+
+ # |1|2|3|
+ # +-+-+-+
+ # |4| |5|
+ # +-+-+-+
+ # |6|7|8|
+ # +-+-+-+
+ # where the outer rectangle is rect1 and the inner rectangle is rect 2. Note that the rects may be of
+ # width or height 0.
+ #
+ rects = [
+ # Top row.
+ @createRect rect1.left, rect1.top, rect2.left, rect2.top
+ @createRect rect2.left, rect1.top, rect2.right, rect2.top
+ @createRect rect2.right, rect1.top, rect1.right, rect2.top
+ # Middle row.
+ @createRect rect1.left, rect2.top, rect2.left, rect2.bottom
+ @createRect rect2.right, rect2.top, rect1.right, rect2.bottom
+ # Bottom row.
+ @createRect rect1.left, rect2.bottom, rect2.left, rect1.bottom
+ @createRect rect2.left, rect2.bottom, rect2.right, rect1.bottom
+ @createRect rect2.right, rect2.bottom, rect1.right, rect1.bottom
+ ]
+
+ rects.filter (rect) -> rect.height > 0 and rect.width > 0
+
# This creates a new function out of an existing function, where the new function takes fewer arguments. This
# allows us to pass around functions instead of functions + a partial list of arguments.
Function::curry = ->
--
cgit v1.2.3
From 9c9c48598534c2a0cd8aec28a4a806d74f28e090 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Thu, 18 Dec 2014 11:56:53 +0000
Subject: Move rect functions to their own file
---
lib/dom_utils.coffee | 3 +--
lib/rect.coffee | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/utils.coffee | 60 ------------------------------------------------
3 files changed, 65 insertions(+), 62 deletions(-)
create mode 100644 lib/rect.coffee
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 7e19a7fc..ebbed006 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -95,7 +95,6 @@ DomUtils =
else
rect
-
getClientRectsForAreas: (imgClientRect, areas) ->
rects = []
for area in areas
@@ -114,7 +113,7 @@ DomUtils =
# something more sophisticated, but likely not worth the effort.
[x1, y1, x2, y2] = coords
- rect = Utils.shiftRect (Utils.createRect x1, y1, x2, y2), imgClientRect.left, imgClientRect.top
+ rect = Rect.translate (Rect.create x1, y1, x2, y2), imgClientRect.left, imgClientRect.top
rect = @cropRectToVisible rect
rects.push {element: area, rect: rect} unless not rect or isNaN rect.top
diff --git a/lib/rect.coffee b/lib/rect.coffee
new file mode 100644
index 00000000..67c9de7c
--- /dev/null
+++ b/lib/rect.coffee
@@ -0,0 +1,64 @@
+# Commands for manipulating rects.
+Rect =
+ # Create a rect given the top left and bottom right corners.
+ create: (x1, y1, x2, y2) ->
+ bottom: y2
+ top: y1
+ left: x1
+ right: x2
+ width: x2 - x1
+ height: y2 - y1
+
+ # Translate a rect by x horizontally and y vertically.
+ translate: (rect, x, y) ->
+ bottom: rect.bottom + y
+ top: rect.top + y
+ left: rect.left + x
+ right: rect.right + x
+ width: rect.width
+ height: rect.height
+
+ # Subtract rect2 from rect1, returning an array of rects which are in rect1 but not rect2.
+ subtract: (rect1, rect2_) ->
+ # Bound rect2 by rect1
+ rect2 = {}
+ rect2 = @create(
+ Math.max(rect1.left, rect2_.left),
+ Math.max(rect1.top, rect2_.top),
+ Math.min(rect1.right, rect2_.right),
+ Math.min(rect1.bottom, rect2_.bottom)
+ )
+
+ # If bounding rect2 has made the width or height negative, rect1 does not contain rect2.
+ return [rect1] if rect2.width < 0 or rect2.height < 0
+
+ #
+ # All the possible rects, in the order
+ # +-+-+-+
+ # |1|2|3|
+ # +-+-+-+
+ # |4| |5|
+ # +-+-+-+
+ # |6|7|8|
+ # +-+-+-+
+ # where the outer rectangle is rect1 and the inner rectangle is rect 2. Note that the rects may be of
+ # width or height 0.
+ #
+ rects = [
+ # Top row.
+ @create rect1.left, rect1.top, rect2.left, rect2.top
+ @create rect2.left, rect1.top, rect2.right, rect2.top
+ @create rect2.right, rect1.top, rect1.right, rect2.top
+ # Middle row.
+ @create rect1.left, rect2.top, rect2.left, rect2.bottom
+ @create rect2.right, rect2.top, rect1.right, rect2.bottom
+ # Bottom row.
+ @create rect1.left, rect2.bottom, rect2.left, rect1.bottom
+ @create rect2.left, rect2.bottom, rect2.right, rect1.bottom
+ @create rect2.right, rect2.bottom, rect1.right, rect1.bottom
+ ]
+
+ rects.filter (rect) -> rect.height > 0 and rect.width > 0
+
+root = exports ? window
+root.Rect = Rect
diff --git a/lib/utils.coffee b/lib/utils.coffee
index 6cc45f32..b7f8731a 100644
--- a/lib/utils.coffee
+++ b/lib/utils.coffee
@@ -136,66 +136,6 @@ Utils =
# locale-sensitive uppercase detection
hasUpperCase: (s) -> s.toLowerCase() != s
- # Create a rect given the top left and bottom right corners.
- createRect: (x1, y1, x2, y2) ->
- bottom: y2
- top: y1
- left: x1
- right: x2
- width: x2 - x1
- height: y2 - y1
-
- # Translate a rect by x horizontally and y vertically.
- shiftRect: (rect, x, y) ->
- bottom: rect.bottom + y
- top: rect.top + y
- left: rect.left + x
- right: rect.right + x
- width: rect.width
- height: rect.height
-
- # Subtract rect2 from rect1, returning an array of rects which are in rect1 but not rect2.
- subtractRect: (rect1, rect2_) ->
- # Bound rect2 by rect1
- rect2 = {}
- rect2 = @createRect(
- Math.max(rect1.left, rect2_.left),
- Math.max(rect1.top, rect2_.top),
- Math.min(rect1.right, rect2_.right),
- Math.min(rect1.bottom, rect2_.bottom)
- )
-
- # If bounding rect2 has made the width or height negative, rect1 does not contain rect2.
- return [rect1] if rect2.width < 0 or rect2.height < 0
-
- #
- # All the possible rects, in the order
- # +-+-+-+
- # |1|2|3|
- # +-+-+-+
- # |4| |5|
- # +-+-+-+
- # |6|7|8|
- # +-+-+-+
- # where the outer rectangle is rect1 and the inner rectangle is rect 2. Note that the rects may be of
- # width or height 0.
- #
- rects = [
- # Top row.
- @createRect rect1.left, rect1.top, rect2.left, rect2.top
- @createRect rect2.left, rect1.top, rect2.right, rect2.top
- @createRect rect2.right, rect1.top, rect1.right, rect2.top
- # Middle row.
- @createRect rect1.left, rect2.top, rect2.left, rect2.bottom
- @createRect rect2.right, rect2.top, rect1.right, rect2.bottom
- # Bottom row.
- @createRect rect1.left, rect2.bottom, rect2.left, rect1.bottom
- @createRect rect2.left, rect2.bottom, rect2.right, rect1.bottom
- @createRect rect2.right, rect2.bottom, rect1.right, rect1.bottom
- ]
-
- rects.filter (rect) -> rect.height > 0 and rect.width > 0
-
# This creates a new function out of an existing function, where the new function takes fewer arguments. This
# allows us to pass around functions instead of functions + a partial list of arguments.
Function::curry = ->
--
cgit v1.2.3
From 91bb7d7b85df3b90882e92aeae2fa2021f61733e Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Thu, 18 Dec 2014 12:59:35 +0000
Subject: Add tests for lib/rect
---
lib/rect.coffee | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
(limited to 'lib')
diff --git a/lib/rect.coffee b/lib/rect.coffee
index 67c9de7c..adc1fc36 100644
--- a/lib/rect.coffee
+++ b/lib/rect.coffee
@@ -9,8 +9,16 @@ Rect =
width: x2 - x1
height: y2 - y1
+ copy: (rect) ->
+ bottom: rect.bottom
+ top: rect.top
+ left: rect.left
+ right: rect.right
+ width: rect.width
+ height: rect.height
+
# Translate a rect by x horizontally and y vertically.
- translate: (rect, x, y) ->
+ translate: (rect, x = 0, y = 0) ->
bottom: rect.bottom + y
top: rect.top + y
left: rect.left + x
@@ -19,18 +27,17 @@ Rect =
height: rect.height
# Subtract rect2 from rect1, returning an array of rects which are in rect1 but not rect2.
- subtract: (rect1, rect2_) ->
+ subtract: (rect1, rect2) ->
# Bound rect2 by rect1
- rect2 = {}
rect2 = @create(
- Math.max(rect1.left, rect2_.left),
- Math.max(rect1.top, rect2_.top),
- Math.min(rect1.right, rect2_.right),
- Math.min(rect1.bottom, rect2_.bottom)
+ Math.max(rect1.left, rect2.left),
+ Math.max(rect1.top, rect2.top),
+ Math.min(rect1.right, rect2.right),
+ Math.min(rect1.bottom, rect2.bottom)
)
# If bounding rect2 has made the width or height negative, rect1 does not contain rect2.
- return [rect1] if rect2.width < 0 or rect2.height < 0
+ return [Rect.copy rect1] if rect2.width < 0 or rect2.height < 0
#
# All the possible rects, in the order
@@ -60,5 +67,16 @@ Rect =
rects.filter (rect) -> rect.height > 0 and rect.width > 0
+ contains: (rect1, rect2) ->
+ rect1.right > rect2.left and
+ rect1.left < rect2.right and
+ rect1.bottom > rect2.top and
+ rect1.top < rect2.bottom
+
+ equals: (rect1, rect2) ->
+ for property in ["top", "bottom", "left", "right", "width", "height"]
+ return false if rect1[property] != rect2[property]
+ true
+
root = exports ? window
root.Rect = Rect
--
cgit v1.2.3
From ef863e5748c088f80ec9a0ffcaa06201c42e6c98 Mon Sep 17 00:00:00 2001
From: mrmr1993
Date: Thu, 18 Dec 2014 13:23:25 +0000
Subject: Make some minor changes/tweaks to rect handling in dom_utils
---
lib/dom_utils.coffee | 46 +++++++++++++++++++++++++++-------------------
1 file changed, 27 insertions(+), 19 deletions(-)
(limited to 'lib')
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index ebbed006..df1db3b9 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -68,8 +68,7 @@ DomUtils =
else
clientRect = @cropRectToVisible clientRect
- if (!clientRect || clientRect.width < 3 || clientRect.height < 3)
- continue
+ continue unless clientRect
# eliminate invisible elements (see test_harnesses/visibility_test.html)
computedStyle = window.getComputedStyle(element, null)
@@ -81,33 +80,42 @@ DomUtils =
null
+ #
+ # Bounds the rect by the current viewport dimensions. If the rect is offscreen or has a height or width < 3
+ # then null is returned instead of a rect.
+ #
cropRectToVisible: (rect) ->
- if (rect.top < 0)
- rect.height += rect.top
- rect.top = 0
-
- if (rect.left < 0)
- rect.width += rect.left
- rect.left = 0
-
- if (rect.top >= window.innerHeight - 4 || rect.left >= window.innerWidth - 4)
+ boundedRect = Rect.create(
+ Math.max(rect.left, 0),
+ Math.max(rect.top, 0),
+ Math.min(rect.right, window.innerWidth),
+ Math.min(rect.bottom, window.innerHeight)
+ )
+ if boundedRect.width < 3 or boundedRect.height < 3
null
else
- rect
+ boundedRect
+ #
+ # Get the client rects for the elements in a