1 { SerializableBloomFilter
2 , DummyBloomFilter } = require 'bloomfilter'
4 { getPref } = require 'prefs'
6 HTMLDocument = Ci.nsIDOMHTMLDocument
7 HTMLAnchorElement = Ci.nsIDOMHTMLAnchorElement
9 realBloomFilter = new SerializableBloomFilter('hints_bloom_data', 256 * 32, 16)
10 dummyBloomFilter = new DummyBloomFilter()
12 # Marker class wraps the markable element and provides
13 # methods to manipulate the markers
15 # Creates the marker DOM node
16 constructor: (@element) ->
17 document = @element.ownerDocument
18 window = document.defaultView
19 @markerElement = document.createElement('div')
20 @markerElement.className = 'VimFxReset VimFxHintMarker'
22 Object.defineProperty(this, 'bloomFilter', get: ->
23 if getPref('hints_bloom_on') then realBloomFilter else dummyBloomFilter)
26 show: -> @markerElement.className = 'VimFxReset VimFxHintMarker'
29 hide: -> @markerElement.className = 'VimFxReset VimFxHiddenHintMarker'
31 # Positions the marker on the page. The positioning is absulute
32 setPosition: (rect) ->
33 @markerElement.style.left = rect.left + 'px'
34 @markerElement.style.top = rect.top + 'px'
36 # Assigns hint string to the marker
37 setHint: (@hintChars) ->
38 # Hint chars that have been matched so far
39 @enteredHintChars = ''
41 document = @element.ownerDocument
43 while @markerElement.hasChildNodes()
44 @markerElement.removeChild(@markerElement.firstChild)
46 fragment = document.createDocumentFragment()
47 for char in @hintChars
48 span = document.createElement('span')
49 span.className = 'VimFxReset'
50 span.textContent = char.toUpperCase()
51 fragment.appendChild(span)
53 @markerElement.appendChild(fragment)
55 # Add another char to the `enteredHintString`,
56 # see if it still matches `hintString`, apply classes to
57 # the distinct hint characters and show/hide marker when
58 # the entered string partially (not) matches the hint string
59 matchHintChar: (char) ->
60 # Handle backspace key by removing a previously entered hint char
61 # and resetting its class
62 if char == 'Backspace'
63 if @enteredHintChars.length > 0
64 @enteredHintChars = @enteredHintChars[0...-1]
65 @markerElement.children[@enteredHintChars.length]?.className = 'VimFxReset'
66 # Otherwise append hint char and change hint class
68 @markerElement.children[@enteredHintChars.length]?.className = 'VimFxReset VimFxCharMatch'
69 @enteredHintChars += char.toLowerCase()
71 # If entered hint chars no longer partially match the hint chars
72 # then hide the marker. Othersie show it back
73 if @hintChars.search(@enteredHintChars) == 0 then @show() else @hide()
75 # Checks if the marker will be matched if the next character entered is `char`
77 char == 'Backspace' or @hintChars.search(@enteredHintChars + char.toLowerCase()) == 0
79 # Checks if enterd hint chars completely match the hint chars
81 return @hintChars == @enteredHintChars
83 # Returns string features of the element that can be used in the bloom filter
84 # in order to add relevance to the hint marker
85 extractBloomFeatures: ->
88 # Class name of an element (walks up the node tree to find first element with at least one class)
91 while el.classList?.length == 0 and el not instanceof HTMLDocument
92 suffix = "#{ suffix } #{ el.tagName }"
94 for className in el.classList
95 features["#{ el.tagName }.#{ className }#{ suffix }"] = 10
99 features["#{ el.tagName }.#{ @element.id }"] = 5
101 if @element instanceof HTMLAnchorElement
102 features["a"] = 20 # Reward links no matter what
103 features["#{ el.tagName }.#{ @element.href }"] = 60
104 features["#{ el.tagName }.#{ @element.title }"] = 40
108 # Returns rating of all present bloom features (plus 1)
111 for feature, weight of @extractBloomFeatures()
112 rating += if @bloomFilter.test(feature) then weight else 0
117 for feature, weight of @extractBloomFeatures()
118 @bloomFilter.add(feature)
121 exports.Marker = Marker