diff options
author | Robert Speicher <rspeicher@gmail.com> | 2015-06-15 17:53:39 -0400 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2015-06-19 04:31:24 -0400 |
commit | 32366d18118281b32b5e770824d637a01d15093b (patch) | |
tree | e8241017935ec6a01632b060b45429f2471dba9e /app/assets/javascripts/line_highlighter.js.coffee | |
parent | 1f88d9b56f02ab05aa1ea055a53627b4c934cf51 (diff) | |
download | gitlab-ce-32366d18118281b32b5e770824d637a01d15093b.tar.gz |
Rename BlobView to LineHighlighter
Diffstat (limited to 'app/assets/javascripts/line_highlighter.js.coffee')
-rw-r--r-- | app/assets/javascripts/line_highlighter.js.coffee | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/app/assets/javascripts/line_highlighter.js.coffee b/app/assets/javascripts/line_highlighter.js.coffee new file mode 100644 index 00000000000..a60a04783ac --- /dev/null +++ b/app/assets/javascripts/line_highlighter.js.coffee @@ -0,0 +1,160 @@ +# LineHighlighter +# +# Handles single- and multi-line selection and highlight for blob views. +# +#= require jquery.scrollTo +# +# ### Example Markup +# +# <div id="tree-content-holder"> +# <div class="file-content"> +# <div class="line-numbers"> +# <a href="#L1" id="L1" data-line-number="1">1</a> +# <a href="#L2" id="L2" data-line-number="2">2</a> +# <a href="#L3" id="L3" data-line-number="3">3</a> +# <a href="#L4" id="L4" data-line-number="4">4</a> +# <a href="#L5" id="L5" data-line-number="5">5</a> +# </div> +# <pre class="code highlight"> +# <code> +# <span id="LC1" class="line">...</span> +# <span id="LC2" class="line">...</span> +# <span id="LC3" class="line">...</span> +# <span id="LC4" class="line">...</span> +# <span id="LC5" class="line">...</span> +# </code> +# </pre> +# </div> +# </div> +class @LineHighlighter + # Internal copy of location.hash so we're not dependent on `location` in tests + @_hash = '' + + # Initialize a LineHighlighter object + # + # hash - String URL hash for dependency injection in tests + constructor: (hash = location.hash) -> + @_hash = hash + + @bindEvents() + + unless hash == '' + range = @hashToRange(hash) + + unless isNaN(range[0]) + @highlightRange(range) + + # Scroll to the first highlighted line on initial load + # Offset -50 for the sticky top bar, and another -100 for some context + $.scrollTo("#L#{range[0]}", offset: -150) + + bindEvents: -> + $('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler + + # While it may seem odd to bind to the mousedown event and then throw away + # the click event, there is a method to our madness. + # + # If not done this way, the line number anchor will sometimes keep its + # active state even when the event is cancelled, resulting in an ugly border + # around the link and/or a persisted underline text decoration. + + $('#tree-content-holder').on 'click', 'a[data-line-number]', (event) -> + event.preventDefault() + + clickHandler: (event) => + event.preventDefault() + + @clearHighlight() + + lineNumber = $(event.target).data('line-number') + current = @hashToRange(@_hash) + + if isNaN(current[0]) or !event.shiftKey + # If there's no current selection, or there is but Shift wasn't held, + # treat this like a single-line selection. + @setHash(lineNumber) + @highlightLine(lineNumber) + else if event.shiftKey + if lineNumber < current[0] + range = [lineNumber, current[0]] + else + range = [current[0], lineNumber] + + @setHash(range[0], range[1]) + @highlightRange(range) + + # Unhighlight previously highlighted lines + clearHighlight: -> + $('.hll').removeClass('hll') + + # Convert a URL hash String into line numbers + # + # hash - Hash String + # + # Examples: + # + # hashToRange('#L5') # => [5, NaN] + # hashToRange('#L5-15') # => [5, 15] + # hashToRange('#foo') # => [NaN, NaN] + # + # Returns an Array + hashToRange: (hash) -> + first = parseInt(hash.replace(/^#L(\d+)/, '$1')) + last = parseInt(hash.replace(/^#L\d+-(\d+)/, '$1')) + + [first, last] + + # Highlight a single line + # + # lineNumber - Number to highlight. Must be parsable as an Integer. + # + # Returns undefined if lineNumber is not parsable as an Integer. + highlightLine: (lineNumber) -> + return if isNaN(parseInt(lineNumber)) + + $("#LC#{lineNumber}").addClass('hll') + + # Highlight all lines within a range + # + # range - An Array of 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) -> + return if isNaN(range[0]) + + if isNaN(range[1]) + @highlightLine(range[0]) + else + for lineNumber in [range[0]..range[1]] + @highlightLine(lineNumber) + + setHash: (firstLineNumber, lastLineNumber) => + return if isNaN(parseInt(firstLineNumber)) + + if isNaN(parseInt(lastLineNumber)) + hash = "#L#{firstLineNumber}" + else + hash = "#L#{firstLineNumber}-#{lastLineNumber}" + + @_hash = hash + @__setLocationHash__(hash) + + # Make the actual hash change in the browser + # + # This method is stubbed in tests. + __setLocationHash__: (value) -> + # We're using pushState instead of assigning location.hash directly to + # prevent the page from scrolling on the hashchange event + history.pushState({turbolinks: false, url: value}, document.title, value) |