aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorStephen Blott2014-12-30 15:19:21 +0000
committerStephen Blott2014-12-30 15:19:21 +0000
commit2e9b9105601a2ea9f454875e776b00baec4299d8 (patch)
tree759d1f88f0dbc39265fa0641dc8f02b6a18d441d /lib
parenta4a591156f451c1d360530fce6674189f384b452 (diff)
parentc3df7699527f88c660e0d61fafdd1ad334236d77 (diff)
downloadvimium-2e9b9105601a2ea9f454875e776b00baec4299d8.tar.bz2
Merge branch 'smblott-link-hints-overlap' into post-1.46
Diffstat (limited to 'lib')
-rw-r--r--lib/dom_utils.coffee92
-rw-r--r--lib/rect.coffee82
2 files changed, 145 insertions, 29 deletions
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 8db71001..ba5e279f 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -50,34 +50,7 @@ DomUtils =
#
getVisibleClientRect: (element) ->
# Note: this call will be expensive if we modify the DOM in between calls.
- clientRects = ({
- top: clientRect.top, right: clientRect.right, bottom: clientRect.bottom, left: clientRect.left,
- width: clientRect.width, height: clientRect.height
- } for clientRect in element.getClientRects())
-
- for clientRect in clientRects
- if (clientRect.top < 0)
- clientRect.height += clientRect.top
- clientRect.top = 0
-
- 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)
- continue
-
- # 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')
- continue
-
- return clientRect
+ clientRects = (Rect.copy clientRect for clientRect in element.getClientRects())
for clientRect in clientRects
# If the link has zero dimensions, it may be wrapping visible
@@ -90,11 +63,72 @@ DomUtils =
continue if (computedStyle.getPropertyValue('float') == 'none' &&
computedStyle.getPropertyValue('position') != 'absolute')
childClientRect = @getVisibleClientRect(child)
- continue if (childClientRect == null)
+ continue if childClientRect == null or childClientRect.width < 3 or childClientRect.height < 3
return childClientRect
+
+ else
+ clientRect = @cropRectToVisible clientRect
+
+ continue if clientRect == null or clientRect.width < 3 or clientRect.height < 3
+
+ # 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
#
+ # 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) ->
+ boundedRect = Rect.create(
+ Math.max(rect.left, 0)
+ Math.max(rect.top, 0)
+ rect.right
+ rect.bottom
+ )
+ if boundedRect.top >= window.innerHeight - 4 or boundedRect.left >= window.innerWidth - 4
+ null
+ else
+ boundedRect
+
+ #
+ # Get the client rects for the <area> elements in a <map> based on the position of the <img> element using
+ # the map. Returns an array of rects.
+ #
+ getClientRectsForAreas: (imgClientRect, areas) ->
+ rects = []
+ for area in areas
+ coords = area.coords.split(",").map((coord) -> parseInt(coord, 10))
+ shape = area.shape.toLowerCase()
+ if shape in ["rect", "rectangle"] # "rectangle" is an IE non-standard.
+ [x1, y1, x2, y2] = coords
+ else if shape in ["circle", "circ"] # "circ" is an IE non-standard.
+ [x, y, r] = coords
+ diff = r / Math.sqrt 2 # Gives us an inner square
+ x1 = x - diff
+ x2 = x + diff
+ y1 = y - diff
+ y2 = y + diff
+ else if shape == "default"
+ [x1, y1, x2, y2] = [0, 0, imgClientRect.width, imgClientRect.height]
+ 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 = Rect.translate (Rect.create x1, y1, x2, y2), imgClientRect.left, imgClientRect.top
+ rect = @cropRectToVisible rect
+
+ rects.push {element: area, rect: rect} if rect and not isNaN rect.top
+ rects
+
+ #
# Selectable means that we should use the simulateSelect method to activate the element instead of a click.
#
# The html5 input types that should use simulateSelect are:
diff --git a/lib/rect.coffee b/lib/rect.coffee
new file mode 100644
index 00000000..adc1fc36
--- /dev/null
+++ b/lib/rect.coffee
@@ -0,0 +1,82 @@
+# 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
+
+ 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 = 0, y = 0) ->
+ 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 = @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 [Rect.copy 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
+
+ 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