1 { SerializableBloomFilter
2 , DummyBloomFilter } = require 'mode-hints/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 # Wraps the markable element and provides methods to manipulate the markers
14 # Creates the marker DOM node
15 constructor: (@element) ->
16 document = @element.ownerDocument
17 window = document.defaultView
18 @markerElement = document.createElement('div')
19 @markerElement.className = 'VimFxReset VimFxHintMarker'
21 Object.defineProperty this, 'bloomFilter',
22 get: -> if getPref('hints_bloom_on') then realBloomFilter else dummyBloomFilter
24 show: -> @setVisibility(true)
25 hide: -> @setVisibility(false)
26 setVisibility: (visible) ->
27 method = if visible then 'remove' else 'add'
28 @markerElement.classList[method]('VimFxHiddenHintMarker')
30 setPosition: (top, left) ->
31 # The positioning is absulute
32 @markerElement.style.top = "#{ top }px"
33 @markerElement.style.left = "#{ left }px"
36 @position = {top, left}
38 # To be called when the marker has been both assigned a hint and inserted into the DOM, and thus
39 # gotten a height and width.
42 position: { top, left }
43 markerElement: { offsetHeight: height, offsetWidth: width }
45 @position = {top, bottom: top + height, left, right: left + width, height, width}
47 setHint: (@hintChars) ->
48 # Hint chars that have been matched so far
49 @enteredHintChars = ''
51 document = @element.ownerDocument
53 while @markerElement.hasChildNodes()
54 @markerElement.removeChild(@markerElement.firstChild)
56 fragment = document.createDocumentFragment()
57 for char in @hintChars
58 span = document.createElement('span')
59 span.className = 'VimFxReset'
60 span.textContent = char.toUpperCase()
61 fragment.appendChild(span)
63 @markerElement.appendChild(fragment)
65 matchHintChar: (char) ->
66 @updateEnteredHintChars(char)
69 @updateEnteredHintChars(false)
71 updateEnteredHintChars: (char) ->
74 @enteredHintChars = @enteredHintChars[...-1]
78 @enteredHintChars += char.toLowerCase()
81 @markerElement.children[@enteredHintChars.length + offset]?.classList[method]('VimFxCharMatch')
82 if @hintChars.startsWith(@enteredHintChars) then @show() else @hide()
85 return @hintChars == @enteredHintChars
91 # Returns string features of the element that can be used in the bloom filter
92 # in order to add relevance to the hint marker
93 extractBloomFeatures: ->
96 # Class name of an element (walks up the node tree to find first element with at least one class)
99 while el.classList?.length == 0 and el not instanceof HTMLDocument
100 suffix += " #{ el.tagName }"
103 for className in el.classList
104 features["#{ el.tagName }.#{ className }#{ suffix }"] = 10
107 features["#{ el.tagName }.#{ @element.id }"] = 5
109 if @element instanceof HTMLAnchorElement
110 features["a"] = 20 # Reward links no matter what
111 features["#{ el.tagName }.#{ @element.href }"] = 60
112 features["#{ el.tagName }.#{ @element.title }"] = 40
116 # Returns rating of all present bloom features (plus 1)
119 for feature, weight of @extractBloomFeatures()
120 rating += if @bloomFilter.test(feature) then weight else 0
125 for feature, weight of @extractBloomFeatures()
126 @bloomFilter.add(feature)
129 exports.Marker = Marker