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