]> git.gir.st - VimFx.git/blob - extension/packages/marker.coffee
Implemented Bloom filters to achieve shorter hints for those shortcuts that are used...
[VimFx.git] / extension / packages / marker.coffee
1 { SerializableBloomFilter } = require 'bloomfilter'
2
3 HTMLDocument = Ci.nsIDOMHTMLDocument
4 HTMLAnchorElement = Ci.nsIDOMHTMLAnchorElement
5
6 # Marker class wraps the markable element and provides
7 # methods to manipulate the markers
8 class Marker
9 # Creates the marker DOM node
10 constructor: (@element) ->
11 document = @element.ownerDocument
12 window = document.defaultView
13 @markerElement = document.createElement('div')
14 @markerElement.className = 'VimFxReset VimFxHintMarker'
15
16 # Shows the marker
17 show: -> @markerElement.className = 'VimFxReset VimFxHintMarker'
18
19 # Hides the marker
20 hide: -> @markerElement.className = 'VimFxReset VimFxHiddenHintMarker'
21
22 # Positions the marker on the page. The positioning is absulute
23 setPosition: (rect) ->
24 @markerElement.style.left = rect.left + 'px'
25 @markerElement.style.top = rect.top + 'px'
26
27 # Assigns hint string to the marker
28 setHint: (@hintChars) ->
29 # Hint chars that have been matched so far
30 @enteredHintChars = ''
31
32 document = @element.ownerDocument
33
34 while @markerElement.hasChildNodes()
35 @markerElement.removeChild(@markerElement.firstChild)
36
37 fragment = document.createDocumentFragment()
38 for char in @hintChars
39 span = document.createElement('span')
40 span.className = 'VimFxReset'
41 span.textContent = char.toUpperCase()
42 fragment.appendChild(span)
43
44 @markerElement.appendChild(fragment)
45
46 # Add another char to the `enteredHintString`,
47 # see if it still matches `hintString`, apply classes to
48 # the distinct hint characters and show/hide marker when
49 # the entered string partially (not) matches the hint string
50 matchHintChar: (char) ->
51 # Handle backspace key by removing a previously entered hint char
52 # and resetting its class
53 if char == 'Backspace'
54 if @enteredHintChars.length > 0
55 @enteredHintChars = @enteredHintChars[0...-1]
56 @markerElement.children[@enteredHintChars.length]?.className = 'VimFxReset'
57 # Otherwise append hint char and change hint class
58 else
59 @markerElement.children[@enteredHintChars.length]?.className = 'VimFxReset VimFxCharMatch'
60 @enteredHintChars += char.toLowerCase()
61
62 # If entered hint chars no longer partially match the hint chars
63 # then hide the marker. Othersie show it back
64 if @hintChars.search(@enteredHintChars) == 0 then @show() else @hide()
65
66 # Checks if the marker will be matched if the next character entered is `char`
67 willMatch: (char) ->
68 char == 'Backspace' or @hintChars.search(@enteredHintChars + char.toLowerCase()) == 0
69
70 # Checks if enterd hint chars completely match the hint chars
71 isMatched: ->
72 return @hintChars == @enteredHintChars
73
74 # Returns string features of the element that can be used in the bloom filter
75 # in order to add relevance to the hint marker
76 extractBloomFeatures: ->
77 features = {}
78
79 # Class name of an element (walks up the node tree to find first element with at least one class)
80 suffix = ''
81 el = @element
82 while el.classList?.length == 0 and not el instanceof HTMLDocument
83 suffix = "#{ suffix } #{ el.tagName }"
84 el = el.parentNode
85 for className in el.classList
86 features["#{ el.tagName }.#{ className }#{ suffix }"] = 10
87
88 # Element id
89 if @element.id
90 features["#{ el.tagName }.#{ @element.id }"] = 5
91
92 if @element instanceof HTMLAnchorElement
93 features["a"] = 20 # Reward links no matter what
94 features["#{ el.tagName }.#{ @element.href }"] = 60
95 features["#{ el.tagName }.#{ @element.title }"] = 40
96
97 return features
98
99 # Returns rating of all present bloom features (plus 1)
100 calcBloomRating: ->
101 rating = 1
102 for feature, weight of @extractBloomFeatures()
103 rating += if Marker.bloomFilter.test(feature) then weight else 0
104
105 return rating
106
107 reward: ->
108 for feature, weight of @extractBloomFeatures()
109 Marker.bloomFilter.add(feature)
110 Marker.bloomFilter.save()
111
112 Marker.bloomFilter = new SerializableBloomFilter('hint_bloom_data', 256 * 32, 16)
113
114 exports.Marker = Marker
Imprint / Impressum