vendor/ui/js/src/components/blob.js in dolt-0.17.0 vs vendor/ui/js/src/components/blob.js in dolt-0.18.0
- old
+ new
@@ -1,39 +1,154 @@
/*global cull, dome*/
+this.gts = this.gts || {};
+
+/**
+ * Utilities for working with syntax highligted blobs. Assumes the markup for
+ * blobs generated by Dolt. Exports three functions:
+ *
+ * * trackFocus
+ * Sets up events to highlight lines on mouseover, and allows the user to
+ * highlight regions
+ * * regionFromUrl
+ * Parses the URL and finds line number or region to highlight
+ * * highlightRegion
+ * Highlights a region
+ *
+ * The following functions all assume that a blob is contained in some root
+ * element that represents each line in the file as an li element.
+ *
+ * A "region" is an array of two numbers, denoting the first and last line of
+ * a consecutive region. Both boundaries are inclusive. Regions start at 1 (not
+ * 0).
+ * [1, 2] - The region consisting of the first and second line, inclusive
+ * [1, 1] - The first line
+ * [10, 14] - The region of lines 10, 11, 12, 13 and 14
+ */
this.gts.blob = (function () {
- function highlightLineOnFocus(element, start, end) {
+ /**
+ * Accepts the root element of a blob and a region, and adds the "region"
+ * class name to all li elements in the region.
+ */
+ function highlightRegion(element, region) {
+ // Line numbers are 1-based, but ElementLists are 0-indexed,
+ // offset region accordingly
+ var start = region[0] - 1;
+ var end = region[1] - 1;
+ var lines = element.getElementsByTagName("li");
+
+ cull.doall(function (li, num) {
+ var inRegion = start <= num && num <= end;
+ dome.cn[inRegion ? "add" : "rm"]("region", li);
+ }, element.getElementsByTagName("li"));
+ }
+
+ /**
+ * Given an li element representing a line, return which line number it
+ * represents.
+ */
+ function getLineNum(element) {
+ while (element && element.tagName !== "LI") {
+ element = element.parentNode;
+ }
+ var lineNum = element && element.className.match(/L(\d+)/)[1];
+ if (!lineNum) { return; }
+ return parseInt(lineNum, 10) + 1;
+ }
+
+ /**
+ * Set the active region. Marks the region and updates the URL with the
+ * new region as an anchor ("#L<start>-<end>").
+ */
+ function setRegion(element, region, redirect) {
+ var regionStr = region[0];
+ regionStr += region[1] !== region[0] ? "-" + region[1] : "";
+ redirect("#L" + regionStr);
+ highlightRegion(element, region);
+ }
+
+ /**
+ * Given two numbers, return a region where the lowest number is the start
+ * and the highest number is the end. The end is optional, in which case
+ * the [start, start] region is returned.
+ */
+ function orderedRegion(start, end) {
+ end = typeof end === "number" ? end : start;
+ return [Math.min(start, end), Math.max(start, end)];
+ }
+
+ /**
+ * Track selected region through clicks on lines.
+ *
+ * Clicking a line highlights it by setting the region to that line
+ * (e.g [n, n]).
+ *
+ * Holding shift while clicking a line makes a region starting at the
+ * previously clicked line and ending at the line being clicked. Example:
+ *
+ * Click on line 4. Press shift, click line 9. Lines 4, 5, 6, 7, 8 and 9
+ * will be highlighted. While holding shift, click line 1. Lines 1, 2, 3 and
+ * 4 will be highlighted. Release shift, click line 5, line 5 (and no
+ * others) will be highlighted.
+ */
+ function trackSelectedRegion(element, redirect) {
+ var shift = false, anchor;
+ var events = [];
+
+ dome.on(document.body, "keydown", function (e) {
+ shift = e.which === 16;
+ });
+ dome.on(document.body, "keyup", function (e) {
+ shift = e.which !== 16;
+ });
+
+ dome.on(element, "click", function (e) {
+ var region, currLine = getLineNum(e.target);
+ if (!shift || !anchor) { anchor = e.target; }
+
+ if (anchor && shift) {
+ region = orderedRegion(getLineNum(anchor), currLine);
+ e.preventDefault();
+ } else {
+ region = [currLine, currLine];
+ }
+
+ setRegion(element, region, redirect);
+ });
+ }
+
+ /**
+ * Track focus on lines by highlighting lines as the cursor hovers them, and
+ * by highlighting (and selecting) regions as lines are clicked (see the
+ * above function)
+ */
+ function trackFocus(element, redirect) {
var highlight = function () { dome.cn.add("focus", this); };
var dim = function () { dome.cn.rm("focus", this); };
cull.doall(function (li) {
dome.on(li, "mouseenter", highlight);
dome.on(li, "mouseleave", dim);
}, element.getElementsByTagName("li"));
+
+ trackSelectedRegion(element, redirect);
}
+ /**
+ * Regions can be specified over the URL by using the #L<start>[-<end>]
+ * syntax. This function extracts it. Examples:
+ *
+ * /some/url#L1 - [1, 1]
+ * /some/url#L1-2 - [1, 2]
+ * /some/url#l12-2 - [2, 12] (ordered)
+ */
function regionFromUrl(url) {
var matches = url.match(/\#l(\d+)(?:-(\d+))?/i);
if (!matches) { return; }
- var start = Math.min(matches[2] || matches[1], matches[1]);
- var end = Math.max(matches[2] || matches[1], matches[1]);
- return [start, end];
+ return orderedRegion(matches[1], matches[2] && Number(matches[2]));
}
- function highlightRegion(element, region) {
- // Line numbers are 1-based, but ElementLists are 0-indexed,
- // offset region accordingly
- var start = region[0] - 1;
- var end = region[1] - 1;
- var lines = element.getElementsByTagName("li");
-
- cull.doall(function (li, num) {
- var inRegion = start <= num && num <= end;
- dome.cn[inRegion ? "add" : "rm"]("region", li);
- }, element.getElementsByTagName("li"));
- }
-
return {
- highlightLineOnFocus: highlightLineOnFocus,
+ trackFocus: trackFocus,
regionFromUrl: regionFromUrl,
highlightRegion: highlightRegion
};
}());