Commit e59aad6e authored by Robert Speicher's avatar Robert Speicher

Refactor LineHighlighter

parent 32366d18
...@@ -26,9 +26,13 @@ ...@@ -26,9 +26,13 @@
# </pre> # </pre>
# </div> # </div>
# </div> # </div>
#
class @LineHighlighter class @LineHighlighter
# CSS class applied to highlighted lines
highlightClass: 'hll'
# Internal copy of location.hash so we're not dependent on `location` in tests # Internal copy of location.hash so we're not dependent on `location` in tests
@_hash = '' _hash: ''
# Initialize a LineHighlighter object # Initialize a LineHighlighter object
# #
...@@ -41,7 +45,7 @@ class @LineHighlighter ...@@ -41,7 +45,7 @@ class @LineHighlighter
unless hash == '' unless hash == ''
range = @hashToRange(hash) range = @hashToRange(hash)
unless isNaN(range[0]) if range[0]
@highlightRange(range) @highlightRange(range)
# Scroll to the first highlighted line on initial load # Scroll to the first highlighted line on initial load
...@@ -69,7 +73,7 @@ class @LineHighlighter ...@@ -69,7 +73,7 @@ class @LineHighlighter
lineNumber = $(event.target).data('line-number') lineNumber = $(event.target).data('line-number')
current = @hashToRange(@_hash) current = @hashToRange(@_hash)
if isNaN(current[0]) or !event.shiftKey unless current[0] and event.shiftKey
# If there's no current selection, or there is but Shift wasn't held, # If there's no current selection, or there is but Shift wasn't held,
# treat this like a single-line selection. # treat this like a single-line selection.
@setHash(lineNumber) @setHash(lineNumber)
...@@ -85,7 +89,7 @@ class @LineHighlighter ...@@ -85,7 +89,7 @@ class @LineHighlighter
# Unhighlight previously highlighted lines # Unhighlight previously highlighted lines
clearHighlight: -> clearHighlight: ->
$('.hll').removeClass('hll') $(".#{@highlightClass}").removeClass(@highlightClass)
# Convert a URL hash String into line numbers # Convert a URL hash String into line numbers
# #
...@@ -93,60 +97,44 @@ class @LineHighlighter ...@@ -93,60 +97,44 @@ class @LineHighlighter
# #
# Examples: # Examples:
# #
# hashToRange('#L5') # => [5, NaN] # hashToRange('#L5') # => [5, null]
# hashToRange('#L5-15') # => [5, 15] # hashToRange('#L5-15') # => [5, 15]
# hashToRange('#foo') # => [NaN, NaN] # hashToRange('#foo') # => [null, null]
# #
# Returns an Array # Returns an Array
hashToRange: (hash) -> hashToRange: (hash) ->
first = parseInt(hash.replace(/^#L(\d+)/, '$1')) matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/)
last = parseInt(hash.replace(/^#L\d+-(\d+)/, '$1'))
if matches and matches.length
first = parseInt(matches[1])
last = matches[2] and parseInt(matches[2]) or null
[first, last] [first, last]
else
[null, null]
# Highlight a single line # Highlight a single line
# #
# lineNumber - Number to highlight. Must be parsable as an Integer. # lineNumber - Line number to highlight
# highlightLine: (lineNumber) =>
# Returns undefined if lineNumber is not parsable as an Integer. $("#LC#{lineNumber}").addClass(@highlightClass)
highlightLine: (lineNumber) ->
return if isNaN(parseInt(lineNumber))
$("#LC#{lineNumber}").addClass('hll')
# Highlight all lines within a range # Highlight all lines within a range
# #
# range - An Array of starting and ending line numbers. # range - Array containing the starting and ending line numbers
#
# Examples:
#
# # Highlight lines 5 through 15
# highlightRange([5, 15])
#
# # The first value is required, and must be a number
# highlightRange(['foo', 15]) # Invalid, returns undefined
# highlightRange([NaN, NaN]) # Invalid, returns undefined
#
# # The second value is optional; if omitted, only highlights the first line
# highlightRange([5, NaN]) # Valid
#
# Returns undefined if the first line is NaN.
highlightRange: (range) -> highlightRange: (range) ->
return if isNaN(range[0]) if range[1]
if isNaN(range[1])
@highlightLine(range[0])
else
for lineNumber in [range[0]..range[1]] for lineNumber in [range[0]..range[1]]
@highlightLine(lineNumber) @highlightLine(lineNumber)
else
@highlightLine(range[0])
# Set the URL hash string
setHash: (firstLineNumber, lastLineNumber) => setHash: (firstLineNumber, lastLineNumber) =>
return if isNaN(parseInt(firstLineNumber)) if lastLineNumber
if isNaN(parseInt(lastLineNumber))
hash = "#L#{firstLineNumber}"
else
hash = "#L#{firstLineNumber}-#{lastLineNumber}" hash = "#L#{firstLineNumber}-#{lastLineNumber}"
else
hash = "#L#{firstLineNumber}"
@_hash = hash @_hash = hash
@__setLocationHash__(hash) @__setLocationHash__(hash)
......
...@@ -13,6 +13,7 @@ describe 'LineHighlighter', -> ...@@ -13,6 +13,7 @@ describe 'LineHighlighter', ->
beforeEach -> beforeEach ->
fixture.load('line_highlighter.html') fixture.load('line_highlighter.html')
@class = new LineHighlighter() @class = new LineHighlighter()
@css = @class.highlightClass
@spies = { @spies = {
__setLocationHash__: spyOn(@class, '__setLocationHash__').and.callFake -> __setLocationHash__: spyOn(@class, '__setLocationHash__').and.callFake ->
} }
...@@ -20,12 +21,12 @@ describe 'LineHighlighter', -> ...@@ -20,12 +21,12 @@ describe 'LineHighlighter', ->
describe 'behavior', -> describe 'behavior', ->
it 'highlights one line given in the URL hash', -> it 'highlights one line given in the URL hash', ->
new LineHighlighter('#L13') new LineHighlighter('#L13')
expect($('#LC13')).toHaveClass('hll') expect($('#LC13')).toHaveClass(@css)
it 'highlights a range of lines given in the URL hash', -> it 'highlights a range of lines given in the URL hash', ->
new LineHighlighter('#L5-25') new LineHighlighter('#L5-25')
expect($('.hll').length).toBe(21) expect($(".#{@css}").length).toBe(21)
expect($("#LC#{line}")).toHaveClass('hll') for line in [5..25] expect($("#LC#{line}")).toHaveClass(@css) for line in [5..25]
it 'scrolls to the first highlighted line on initial load', -> it 'scrolls to the first highlighted line on initial load', ->
spy = spyOn($, 'scrollTo') spy = spyOn($, 'scrollTo')
...@@ -50,14 +51,14 @@ describe 'LineHighlighter', -> ...@@ -50,14 +51,14 @@ describe 'LineHighlighter', ->
describe 'without shiftKey', -> describe 'without shiftKey', ->
it 'highlights one line when clicked', -> it 'highlights one line when clicked', ->
clickLine(13) clickLine(13)
expect($('#LC13')).toHaveClass('hll') expect($('#LC13')).toHaveClass(@css)
it 'unhighlights previously highlighted lines', -> it 'unhighlights previously highlighted lines', ->
clickLine(13) clickLine(13)
clickLine(20) clickLine(20)
expect($('#LC13')).not.toHaveClass('hll') expect($('#LC13')).not.toHaveClass(@css)
expect($('#LC20')).toHaveClass('hll') expect($('#LC20')).toHaveClass(@css)
it 'sets the hash', -> it 'sets the hash', ->
spy = spyOn(@class, 'setHash').and.callThrough() spy = spyOn(@class, 'setHash').and.callThrough()
...@@ -75,8 +76,8 @@ describe 'LineHighlighter', -> ...@@ -75,8 +76,8 @@ describe 'LineHighlighter', ->
describe 'without existing highlight', -> describe 'without existing highlight', ->
it 'highlights the clicked line', -> it 'highlights the clicked line', ->
clickLine(13, shiftKey: true) clickLine(13, shiftKey: true)
expect($('#LC13')).toHaveClass('hll') expect($('#LC13')).toHaveClass(@css)
expect($('.hll').length).toBe(1) expect($(".#{@css}").length).toBe(1)
it 'sets the hash', -> it 'sets the hash', ->
spy = spyOn(@class, 'setHash') spy = spyOn(@class, 'setHash')
...@@ -87,14 +88,14 @@ describe 'LineHighlighter', -> ...@@ -87,14 +88,14 @@ describe 'LineHighlighter', ->
it 'uses existing line as last line when target is lesser', -> it 'uses existing line as last line when target is lesser', ->
clickLine(20) clickLine(20)
clickLine(15, shiftKey: true) clickLine(15, shiftKey: true)
expect($('.hll').length).toBe(6) expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass('hll') for line in [15..20] expect($("#LC#{line}")).toHaveClass(@css) for line in [15..20]
it 'uses existing line as first line when target is greater', -> it 'uses existing line as first line when target is greater', ->
clickLine(5) clickLine(5)
clickLine(10, shiftKey: true) clickLine(10, shiftKey: true)
expect($('.hll').length).toBe(6) expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass('hll') for line in [5..10] expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10]
describe 'with existing multi-line highlight', -> describe 'with existing multi-line highlight', ->
beforeEach -> beforeEach ->
...@@ -103,26 +104,26 @@ describe 'LineHighlighter', -> ...@@ -103,26 +104,26 @@ describe 'LineHighlighter', ->
it 'uses target as first line when it is less than existing first line', -> it 'uses target as first line when it is less than existing first line', ->
clickLine(5, shiftKey: true) clickLine(5, shiftKey: true)
expect($('.hll').length).toBe(6) expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass('hll') for line in [5..10] expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10]
it 'uses target as last line when it is greater than existing first line', -> it 'uses target as last line when it is greater than existing first line', ->
clickLine(15, shiftKey: true) clickLine(15, shiftKey: true)
expect($('.hll').length).toBe(6) expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass('hll') for line in [10..15] expect($("#LC#{line}")).toHaveClass(@css) for line in [10..15]
describe '#hashToRange', -> describe '#hashToRange', ->
beforeEach -> beforeEach ->
@subject = @class.hashToRange @subject = @class.hashToRange
it 'extracts a single line number from the hash', -> it 'extracts a single line number from the hash', ->
expect(@subject('#L5')).toEqual([5, NaN]) expect(@subject('#L5')).toEqual([5, null])
it 'extracts a range of line numbers from the hash', -> it 'extracts a range of line numbers from the hash', ->
expect(@subject('#L5-15')).toEqual([5, 15]) expect(@subject('#L5-15')).toEqual([5, 15])
it 'returns [NaN, NaN] when the hash is not a line number', -> it 'returns [null, null] when the hash is not a line number', ->
expect(@subject('#foo')).toEqual([NaN, NaN]) expect(@subject('#foo')).toEqual([null, null])
describe '#highlightLine', -> describe '#highlightLine', ->
beforeEach -> beforeEach ->
...@@ -130,36 +131,16 @@ describe 'LineHighlighter', -> ...@@ -130,36 +131,16 @@ describe 'LineHighlighter', ->
it 'highlights the specified line', -> it 'highlights the specified line', ->
@subject(13) @subject(13)
expect($('#LC13')).toHaveClass('hll') expect($('#LC13')).toHaveClass(@css)
it 'accepts a String-based number', -> it 'accepts a String-based number', ->
@subject('13') @subject('13')
expect($('#LC13')).toHaveClass('hll') expect($('#LC13')).toHaveClass(@css)
it 'returns undefined when given NaN', ->
expect(@subject(NaN)).toBe(undefined)
expect(@subject('foo')).toBe(undefined)
describe '#highlightRange', ->
beforeEach ->
@subject = @class.highlightRange
it 'returns undefined when first line is NaN', ->
expect(@subject([NaN, 15])).toBe(undefined)
expect(@subject(['foo', 15])).toBe(undefined)
it 'returns undefined when given an invalid first line', ->
expect(@subject(['foo', 15])).toBe(undefined)
expect(@subject([NaN, NaN])).toBe(undefined)
expect(@subject('foo')).toBe(undefined)
describe '#setHash', -> describe '#setHash', ->
beforeEach -> beforeEach ->
@subject = @class.setHash @subject = @class.setHash
it 'returns undefined when given an invalid first line', ->
expect(@subject('foo', 15)).toBe(undefined)
it 'sets the location hash for a single line', -> it 'sets the location hash for a single line', ->
@subject(5) @subject(5)
expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5') expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment