add line highlighting and linking (#654)

This commit is contained in:
Jesse Plamondon-Willard 2019-08-04 15:55:24 -04:00
parent 1b9ce5e64f
commit f24e7428df
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
3 changed files with 127 additions and 4 deletions

View File

@ -107,7 +107,7 @@ else if (Model.PasteID != null)
@foreach (JsonValidatorErrorModel error in Model.Errors) @foreach (JsonValidatorErrorModel error in Model.Errors)
{ {
<tr> <tr>
<td>@error.Line</td> <td><a href="#L@(error.Line)">@error.Line</a></td>
<td>@error.Path</td> <td>@error.Path</td>
<td>@error.Message</td> <td>@error.Message</td>
</tr> </tr>
@ -121,6 +121,6 @@ else if (Model.PasteID != null)
} }
<h2>Raw content</h2> <h2>Raw content</h2>
<pre class="sunlight-highlight-javascript">@Model.Content</pre> <pre id="raw-content" class="sunlight-highlight-javascript">@Model.Content</pre>
</div> </div>
} }

View File

@ -72,6 +72,10 @@
background: #fff; background: #fff;
} }
#output div.sunlight-line-highlight-active {
background-color: #eeeacc;
}
/********* /*********
** Upload form ** Upload form
*********/ *********/

View File

@ -1,20 +1,139 @@
/* globals $ */ /* globals $ */
var smapi = smapi || {}; var smapi = smapi || {};
/**
* Manages the logic for line range selections.
* @param {int} maxLines The maximum number of lines in the content.
*/
smapi.LineNumberRange = function (maxLines) {
var self = this;
/**
* @var {int} minLine The first line in the selection, or null if no lines selected.
*/
self.minLine = null;
/**
* @var {int} maxLine The last line in the selection, or null if no lines selected.
*/
self.maxLine = null;
/**
* Parse line numbers from a URL hash.
* @param {string} hash the URL hash to parse.
*/
self.parseFromUrlHash = function (hash) {
self.minLine = null;
self.maxLine = null;
// parse hash
var hashParts = hash.match(/^#L(\d+)(?:-L(\d+))?$/);
if (!hashParts || hashParts.length <= 1)
return;
// extract min/max lines
self.minLine = parseInt(hashParts[1]);
self.maxLine = parseInt(hashParts[2]) || self.minLine;
};
/**
* Generate a URL hash for the current line range.
* @returns {string} The generated URL hash.
*/
self.buildHash = function() {
if (!self.minLine)
return "";
else if (self.minLine === self.maxLine)
return "#L" + self.minLine;
else
return "#L" + self.minLine + "-L" + self.maxLine;
}
/**
* Get a list of all selected lines.
* @returns {Array<int>} The selected line numbers.
*/
self.getLinesSelected = function() {
// format
if (!self.minLine)
return [];
var lines = [];
for (var i = self.minLine; i <= self.maxLine; i++)
lines.push(i);
return lines;
};
return self;
};
/**
* UI logic for the JSON validator page.
* @param {any} sectionUrl The base JSON validator page URL.
* @param {any} pasteID The Pastebin paste ID for the content being viewed, if any.
*/
smapi.jsonValidator = function (sectionUrl, pasteID) { smapi.jsonValidator = function (sectionUrl, pasteID) {
/**
* The original content element.
*/
var originalContent = $("#raw-content").clone();
/**
* The currently highlighted lines.
*/
var selection = new smapi.LineNumberRange();
/** /**
* Rebuild the syntax-highlighted element. * Rebuild the syntax-highlighted element.
*/ */
var formatCode = function () { var formatCode = function () {
Sunlight.highlightAll(); // reset if needed
$(".sunlight-container").replaceWith(originalContent.clone());
// apply default highlighting
Sunlight.highlightAll({
lineHighlight: selection.getLinesSelected()
});
// fix line links
$(".sunlight-line-number-margin a").each(function() {
var link = $(this);
var lineNumber = parseInt(link.text());
link
.attr("id", "L" + lineNumber)
.attr("href", "#L" + lineNumber)
.removeAttr("name")
.data("line-number", lineNumber);
});
};
/**
* Scroll the page so the selected range is visible.
*/
var scrollToRange = function() {
if (!selection.minLine)
return;
var targetLine = Math.max(1, selection.minLine - 5);
$("#L" + targetLine).get(0).scrollIntoView();
}; };
/** /**
* Initialise the JSON validator page. * Initialise the JSON validator page.
*/ */
var init = function () { var init = function () {
// code formatting // set initial code formatting
selection.parseFromUrlHash(location.hash);
formatCode(); formatCode();
scrollToRange();
// update code formatting on hash change
$(window).on("hashchange", function() {
selection.parseFromUrlHash(location.hash);
formatCode();
scrollToRange();
});
// change format // change format
$("#output #format").on("change", function() { $("#output #format").on("change", function() {