]> git.gir.st - VimFx.git/blob - extension/packages/mode-hints/marker.coffee
Improve marker generation. Fix #325.
[VimFx.git] / extension / packages / mode-hints / marker.coffee
1 { createElement } = require 'utils'
2 { SerializableBloomFilter
3 , DummyBloomFilter } = require 'mode-hints/bloomfilter'
4
5 { getPref } = require 'prefs'
6
7 HTMLDocument = Ci.nsIDOMHTMLDocument
8 HTMLAnchorElement = Ci.nsIDOMHTMLAnchorElement
9
10 realBloomFilter = new SerializableBloomFilter('hints_bloom_data', 256 * 32, 16)
11 dummyBloomFilter = new DummyBloomFilter()
12
13 # Wraps the markable element and provides methods to manipulate the markers
14 class Marker
15 # Creates the marker DOM node
16 constructor: (@element, @elementShape) ->
17 document = @element.ownerDocument
18 @markerElement = createElement(document, 'div', {class: 'VimFxHintMarker'})
19
20 Object.defineProperty this, 'bloomFilter',
21 get: -> if getPref('hints_bloom_on') then realBloomFilter else dummyBloomFilter
22
23 show: -> @setVisibility(true)
24 hide: -> @setVisibility(false)
25 setVisibility: (visible) ->
26 @markerElement.classList.toggle('VimFxHiddenHintMarker', !visible)
27 updateVisibility: ->
28 if @hintChars.startsWith(@enteredHintChars) then @show() else @hide()
29
30 # To be called when the marker has been both assigned a hint and inserted
31 # into the DOM, and thus gotten a height and width
32 setPosition: (viewport) ->
33 {
34 markerElement: { offsetHeight: height, offsetWidth: width }
35 elementShape: { nonCoveredPoint: { x, y } }
36 } = this
37
38 # Make sure that the hint isn’t partly off-screen
39 x = Math.min(x, viewport.width - width)
40 y = Math.min(y, viewport.height - height)
41
42 left = viewport.scrollX + x
43 top = viewport.scrollY + y
44
45 # The positioning is absolute
46 @markerElement.style.left = "#{ left }px"
47 @markerElement.style.top = "#{ top }px"
48
49 # For quick access
50 @position = {left, right: left + width, top, bottom: top + height, height, width}
51
52 setHint: (@hintChars) ->
53 # Hint chars that have been matched so far
54 @enteredHintChars = ''
55
56 document = @element.ownerDocument
57
58 while @markerElement.hasChildNodes()
59 @markerElement.firstChild.remove()
60
61 fragment = document.createDocumentFragment()
62 for char in @hintChars
63 charContainer = createElement(document, 'span')
64 charContainer.textContent = char.toUpperCase()
65 fragment.appendChild(charContainer)
66
67 @markerElement.appendChild(fragment)
68
69 matchHintChar: (char) ->
70 @toggleLastHintChar(true)
71 @enteredHintChars += char.toLowerCase()
72 @updateVisibility()
73
74 deleteHintChar: ->
75 @enteredHintChars = @enteredHintChars[...-1]
76 @toggleLastHintChar(false)
77 @updateVisibility()
78
79 toggleLastHintChar: (visible) ->
80 @markerElement.children[@enteredHintChars.length]?.classList.toggle('VimFxCharMatch', visible)
81
82 isMatched: ->
83 return @hintChars == @enteredHintChars
84
85 reset: ->
86 @setHint(@hintChars)
87 @show()
88
89 # Returns string features of the element that can be used in the bloom filter
90 # in order to add relevance to the hint marker
91 extractBloomFeatures: ->
92 features = {}
93
94 # Class name of an element (walks up the node tree to find first element with at least one class)
95 suffix = ''
96 el = @element
97 while el.classList?.length == 0 and el not instanceof HTMLDocument
98 suffix += " #{ el.tagName }"
99 el = el.parentNode
100 if el?.classList?
101 for className in el.classList
102 features["#{ el.tagName }.#{ className }#{ suffix }"] = 10
103
104 if @element.id
105 features["#{ el.tagName }.#{ @element.id }"] = 5
106
107 if @element instanceof HTMLAnchorElement
108 features["a"] = 20 # Reward links no matter what
109 features["#{ el.tagName }.#{ @element.href }"] = 60
110 features["#{ el.tagName }.#{ @element.title }"] = 40
111
112 return features
113
114 # Returns rating of all present bloom features (plus 1)
115 calcBloomRating: ->
116 rating = 1
117 for feature, weight of @extractBloomFeatures()
118 rating += if @bloomFilter.test(feature) then weight else 0
119
120 return rating
121
122 reward: ->
123 for feature, weight of @extractBloomFeatures()
124 @bloomFilter.add(feature)
125 @bloomFilter.save()
126
127 exports.Marker = Marker
Imprint / Impressum