]> git.gir.st - VimFx.git/blob - extension/lib/marker.coffee
Fix validation of imported prefs: Don't allow array
[VimFx.git] / extension / lib / marker.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2012, 2013.
3 # Copyright Simon Lydell 2013, 2014, 2015, 2016.
4 #
5 # This file is part of VimFx.
6 #
7 # VimFx is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # VimFx is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
19 ###
20
21 # This file contains an abstraction for hint markers. It creates the UI for a
22 # marker and provides methods to manipulate the markers.
23
24 utils = require('./utils')
25
26 class Marker
27 # `@wrapper` is a stand-in for the element that the marker represents. See
28 # `MarkerContainer::injectHints` for more information.
29 constructor: (@wrapper, @document, {@isComplementary}) ->
30 @elementShape = @wrapper.shape
31 @markerElement = utils.createBox(@document, 'marker')
32 @markerElement.setAttribute('data-type', @wrapper.type)
33 @weight = @wrapper.combinedArea
34 @width = 0
35 @height = 0
36 @hint = ''
37 @originalHint = null
38 @text = @wrapper.text?.toLowerCase() ? ''
39 @visible = true
40 @highlighted = false
41 @zoom = 1
42 @viewport = null
43 @position = null
44 @originalPosition = null
45 @dx = 0
46 @dy = 0
47
48 reset: ->
49 @setHint(@originalHint)
50 @show()
51 @refreshPosition()
52
53 show: -> @setVisibility(true)
54 hide: -> @setVisibility(false)
55
56 setVisibility: (@visible) ->
57 @markerElement.classList.toggle('marker--hidden', not @visible)
58
59 # To be called when the marker has been both assigned a hint and inserted
60 # into the DOM, and thus gotten a width and height.
61 setPosition: (@viewport, @zoom) ->
62 {
63 textOffset
64 width: elementWidth
65 nonCoveredPoint: {x: left, y: top, offset}
66 } = @elementShape
67
68 rect = @markerElement.getBoundingClientRect()
69
70 @width = rect.width / @zoom
71 @height = rect.height / @zoom
72
73 # Center the marker vertically on the non-covered point.
74 top -= Math.ceil(@height / 2)
75
76 if textOffset?
77 # Move the marker just to the left of the text of its element.
78 left -= Math.max(0, @width - textOffset)
79 else
80 # Otherwise make sure that it doesn’t flow outside the right side of its
81 # element. This is to avoid the following situation (where `+` is a small
82 # button, `Link text` is a (larger) link and `DAG` and `E` are the hints
83 # placed on top of them.) This makes it clearer which hint does what.
84 # Example site: Hackernews.
85 #
86 # z-layer before after
87 # bottom +Link text +Link text
88 # middle DAG DAG
89 # top E E
90 left -= Math.max(0, @width - elementWidth)
91
92 # Make the position relative to the top frame.
93 left += offset.left
94 top += offset.top
95
96 @originalPosition = {left, top}
97 @moveTo(left + @dx, top + @dy)
98
99 moveTo: (left, top) ->
100 # Make sure that the marker stays within the viewport.
101 left = Math.min(left, @viewport.right - @width)
102 top = Math.min(top, @viewport.bottom - @height)
103 left = Math.max(left, @viewport.left)
104 top = Math.max(top, @viewport.top)
105
106 # Take the current zoom into account.
107 left = Math.round(left * @zoom)
108 top = Math.round(top * @zoom)
109
110 # The positioning is absolute.
111 @markerElement.style.left = "#{left}px"
112 @markerElement.style.top = "#{top}px"
113
114 # For quick access.
115 @position = {
116 left, right: left + @width,
117 top, bottom: top + @height,
118 }
119
120 updatePosition: (@dx, @dy) ->
121 @moveTo(@originalPosition.left + @dx, @originalPosition.top + @dy)
122
123 refreshPosition: ->
124 @setPosition(@viewport, @zoom)
125
126 setHint: (@hint) ->
127 @originalHint ?= @hint
128 @markerElement.textContent = ''
129 fragment = @document.createDocumentFragment()
130 utils.createBox(@document, 'marker-char', fragment, char) for char in @hint
131 @markerElement.appendChild(fragment)
132
133 matchHint: (hint) ->
134 return @hint.startsWith(hint)
135
136 matchText: (strings) ->
137 return strings.every((string) => @text.includes(string))
138
139 markMatchedPart: (hint) ->
140 matchEnd = if @matchHint(hint) then hint.length else 0
141 for child, index in @markerElement.children
142 child.classList.toggle('marker-char--matched', index < matchEnd)
143 return
144
145 markMatched: (matched) ->
146 @markerElement.classList.toggle('marker--matched', matched)
147
148 markHighlighted: (@highlighted) ->
149 @markerElement.classList.toggle('marker--highlighted', @highlighted)
150
151 module.exports = Marker
Imprint / Impressum