aboutsummaryrefslogtreecommitdiff
path: root/javascript/app/utils
diff options
context:
space:
mode:
Diffstat (limited to 'javascript/app/utils')
-rw-r--r--javascript/app/utils/api-urls.js29
-rw-r--r--javascript/app/utils/color-themes.js188
-rw-r--r--javascript/app/utils/go-to-definition.js95
-rw-r--r--javascript/app/utils/line-selection.js111
4 files changed, 423 insertions, 0 deletions
diff --git a/javascript/app/utils/api-urls.js b/javascript/app/utils/api-urls.js
new file mode 100644
index 0000000..b2748b6
--- /dev/null
+++ b/javascript/app/utils/api-urls.js
@@ -0,0 +1,29 @@
+import config from '../config/environment';
+
+export const urls = {
+ packageInfoUrl : function(packageId) {
+ return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/packageInfo.json";
+ },
+ fileUrl : function(packageId,filePath) {
+ return config.APP.staticUrlPrefix+"/"+packageId+"/"+filePath;
+ },
+ haskellModuleUrl : function (packageId,filePath) {
+ return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/"+encodeURIComponent(encodeURIComponent(filePath))+ ".json";
+ },
+ packagesUrl : config.APP.apiUrlPrefix + "/packages",
+ identifierDefinitionSiteUrl : function(packageId,moduleName,componentId,entity,name) {
+ return config.APP.apiUrlPrefix + "/definitionSite/" + packageId+"/"+componentId+"/"+moduleName+"/"+entity+"/"+encodeURIComponent(name).replace(/\./g, '%2E');
+ },
+ modulePathUrl : function (packageId,moduleName,componentId) {
+ return config.APP.apiUrlPrefix + "/modulePath/"+packageId+"/"+componentId+"/"+moduleName;
+ },
+ expressionsUrl : function (packageId,modulePath,lineStart,columnStart,lineEnd,columnEnd) {
+ return config.APP.apiUrlPrefix + "/expressions/"+packageId+"/"+encodeURIComponent(modulePath) +"/"+lineStart+"/"+columnStart+"/"+lineEnd+"/"+columnEnd;
+ },
+ referencesUrl : function (packageId,externalId) {
+ return config.APP.apiUrlPrefix + "/references/"+packageId+"/"+encodeURIComponent(externalId);
+ },
+ identifierSearchUrl : function (packageId,query) {
+ return config.APP.apiUrlPrefix + "/identifiers/"+packageId+"/"+encodeURIComponent(query).replace(/\./g, '%2E');
+ }
+}
diff --git a/javascript/app/utils/color-themes.js b/javascript/app/utils/color-themes.js
new file mode 100644
index 0000000..0a563ff
--- /dev/null
+++ b/javascript/app/utils/color-themes.js
@@ -0,0 +1,188 @@
+function colorThemeToCss(colorTheme) {
+ const css = `
+ body {
+ color: ${colorTheme.defaultColor} !important;
+ background-color: ${colorTheme.backgroundColor} !important;
+ }
+ input {
+ color: ${colorTheme.defaultColor} !important;
+ background-color: ${colorTheme.backgroundColor} !important;
+ border-color: ${colorTheme.borderColor} !important;
+ }
+ .package-content {
+ border-top: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .header a {
+ color : ${colorTheme.menuLinkColor} !important;
+ }
+ a {
+ color: ${colorTheme.typeColor} !important;
+ }
+ span.link {
+ color: ${colorTheme.typeColor} !important;
+ }
+ .header {
+ background-color: ${colorTheme.menuColor} !important;
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .declarations-content {
+ background-color: ${colorTheme.navigationPanelColor} !important;
+ border: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .declarations-header {
+ background-color: ${colorTheme.navigationPanelColor} !important;
+ border: 1px solid ${colorTheme.borderColor} !important;
+ }
+ li.declaration {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .left-panel {
+ background-color: ${colorTheme.navigationPanelColor} !important;
+ border-right: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .show-left-panel-button {
+ background-color: ${colorTheme.navigationPanelColor} !important;
+ border-right:1px solid ${colorTheme.borderColor} !important;
+ border-bottom:1px solid ${colorTheme.borderColor} !important;
+ }
+ .right-panel {
+ background-color: ${colorTheme.backgroundColor} !important;
+ }
+ a.jstree-anchor {
+ color: ${colorTheme.defaultColor} !important;
+ }
+ .declaration > a {
+ color: ${colorTheme.defaultColor} !important;
+ }
+ .highlighted-line {
+ background : ${colorTheme.highlightedLineColor} !important;
+ }
+ table.source-code {
+ background-color: ${colorTheme.backgroundColor} !important;
+ color: ${colorTheme.defaultColor} !important;
+ }
+ .jstree-clicked {
+ background-color: ${colorTheme.backgroundColor} !important;
+ }
+ .jstree-hovered {
+ background-color: ${colorTheme.backgroundColor} !important;
+ }
+ ul.autocomplete-items {
+ background-color: ${colorTheme.backgroundColor} !important;
+ border-top: 1px solid ${colorTheme.borderColor} !important;
+ border-left: 1px solid ${colorTheme.borderColor} !important;
+ border-right: 1px solid ${colorTheme.borderColor} !important;
+ }
+ ul.autocomplete-items > li {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ ul.autocomplete-items > li:hover {
+ background-color: ${colorTheme.highlightedLineColor} !important;
+ }
+ ul.autocomplete-items > li.highlighted {
+ background-color: ${colorTheme.highlightedLineColor} !important;
+ }
+ .source-code-snippet {
+ color: ${colorTheme.defaultColor} !important;
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .source-code-snippet:hover {
+ background-color: ${colorTheme.highlightedLineColor} !important;
+ }
+ .bottom-panel {
+ background-color: ${colorTheme.backgroundColor} !important;
+ border-top: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .bottom-panel-header {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .paginated-list-header {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ li.search-result {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .search-results-header {
+ border-bottom: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .info-window-content {
+ border-top: 1px solid ${colorTheme.borderColor} !important;
+ }
+ .info-window {
+ border: 1px solid ${colorTheme.borderColor} !important;
+ background-color:${colorTheme.infoWindowColor} !important;
+ color: ${colorTheme.defaultColor} !important;
+ }
+ .type-info {
+ border-top: 1px solid ${colorTheme.borderColor} !important;
+ }`;
+ return css;
+}
+
+const darkTheme = {
+ id: "darkTheme",
+ name: "Dark theme",
+ description: "Dark theme (Monokai based)",
+ defaultColor: "#F8F8F2",
+ backgroundColor: "#272822",
+ typeColor: "#66D9EF",
+ literalColor: "#E6DB74",
+ topLevelIdFromCurrentModule : "#A6E22E",
+ localIdentifierColor: ["#F0A3FF","#0075DC","#993F00",
+ "#2BCE48","#FFCC99","#808080","#94FFB5","#8F7C00",
+ "#C20088","#FFA405","#FFA8BB","#426600","#FF0010",
+ "#5EF1F2","#00998F","#E0FF66","#FFFF80",
+ "#FFFF00","#FF5005"],
+ menuColor: "#3c3b37",
+ menuLinkColor : "#F8F8F2",
+ infoWindowColor: "#3c3b37",
+ navigationPanelColor: "#3c3b37",
+ linkColor : "#0366d6",
+ borderColor: "#535557",
+ highlightedLineColor: "#4a4a4a"
+};
+
+const lightTheme = {
+ id: "lightTheme",
+ name: "Light theme",
+ description: "Light theme (Github based)",
+ defaultColor: "#24292e",
+ backgroundColor: "#ffffff",
+ typeColor: "#005cc5",
+ literalColor: "#032f62",
+ topLevelIdFromCurrentModule : "#6f42c1",
+ localIdentifierColor: ["#005C31",
+ "#2BCE48","#808080","#8F7C00",
+ "#C20088","#FFA405","#ffa8bb","#426600","#FF0010",
+ "#09d7d8","#00998F","#990000","#FF5005"],
+ menuColor: "#f2f4f8",
+ menuLinkColor : "#24292e",
+ infoWindowColor: "#f2f4f8",
+ navigationPanelColor: "#f2f4f8",
+ linkColor : "#0366d6",
+ borderColor: "#e1e4e8",
+ highlightedLineColor: "#eaeaea"
+};
+
+function updateColorThemeCss (colorTheme) {
+ const newStyle = document.createElement('style');
+ newStyle.type = 'text/css';
+ newStyle.innerHTML = colorThemeToCss(colorTheme);
+ newStyle.id = 'color-theme';
+ const oldStyle = document.querySelector("style#color-theme");
+ if(oldStyle) {
+ oldStyle.parentElement.removeChild(oldStyle);
+ }
+ document.getElementsByTagName('head')[0].appendChild(newStyle);
+}
+
+const themes = {
+ darkTheme: darkTheme,
+ lightTheme: lightTheme
+};
+
+export {
+ updateColorThemeCss,
+ colorThemeToCss,
+ themes
+}
diff --git a/javascript/app/utils/go-to-definition.js b/javascript/app/utils/go-to-definition.js
new file mode 100644
index 0000000..e05f6bf
--- /dev/null
+++ b/javascript/app/utils/go-to-definition.js
@@ -0,0 +1,95 @@
+function exactLocationToUrl(exactLocation) {
+ const modulePath = exactLocation.modulePath;
+ const packageId = exactLocation.packageId.name + "-" + exactLocation.packageId.version;
+ let hash = "";
+ if(exactLocation.startLine != 1) {
+ hash = "#L" + exactLocation.startLine;
+ }
+ return "/package/"+packageId+"/show/"+modulePath+hash;
+}
+
+function hackageUrl(packageId,locationInfo) {
+ const dasherizedModuleName = locationInfo.moduleName.replace(/\./g,'-');
+ let key;
+ if(locationInfo.entity === "Val") {
+ key = "v";
+ } else {
+ key = "t";
+ }
+ let hash = "";
+ if(locationInfo.entity === "Val" || locationInfo.entity === "Typ") {
+ hash = "#"+key+":"+locationInfo.haddockAnchorId;
+ }
+ return "https://hackage.haskell.org/package/"+packageId+"/docs/"+dasherizedModuleName+".html"+hash;
+}
+
+function openUrl(buttonId,url) {
+ if(buttonId === 2) {//middle mouse button
+ window.open(url, '_blank');
+ } else if(buttonId == 1) {//left mouse button
+ window.location = url;
+ }
+ return false;
+}
+
+function saveCurrentLocation(currentLineNumber) {
+ if(currentLineNumber) {
+ const url = window.location.origin + window.location.pathname + "#L" + currentLineNumber;
+ if(location.href != url) {
+ window.location.hash = "#L" + currentLineNumber;
+ }
+ }
+}
+
+function goToDefinition(store,locationInfo,buttonId,currentLineNumber) {
+ if(locationInfo.tag === "ExactLocation") {
+ const url = exactLocationToUrl(locationInfo);
+ if(locationInfo.startLine !== currentLineNumber) {
+ saveCurrentLocation(currentLineNumber);
+ }
+ openUrl(buttonId,url);
+ } else if(locationInfo.tag === "ApproximateLocation") {
+ const packageId = locationInfo.packageId.name+"-"+locationInfo.packageId.version;
+ if(locationInfo.entity === "Mod") {
+ store.loadDefinitionSite(packageId,
+ locationInfo.moduleName,
+ locationInfo.componentId,
+ locationInfo.entity,
+ locationInfo.moduleName)
+ .then((defSite) => {
+ const packageId = defSite.location.packageId.name + "-" + defSite.location.packageId.version;
+ openUrl(buttonId,"/package/" + packageId + "/show/" + defSite.location.modulePath);
+ }).catch(() => {
+ openUrl(buttonId,hackageUrl(packageId,locationInfo));
+ });
+ } else {
+ store.loadDefinitionSite(packageId,
+ locationInfo.moduleName,
+ locationInfo.componentId,
+ locationInfo.entity,
+ locationInfo.name)
+ .then((definitionSite) => {
+ if(definitionSite.location.tag === "ExactLocation") {
+ const url = exactLocationToUrl(definitionSite.location);
+ if(locationInfo.startLine !== currentLineNumber) {
+ saveCurrentLocation(currentLineNumber);
+ }
+ openUrl(buttonId,url);
+ } else {
+ saveCurrentLocation(currentLineNumber);
+ openUrl(buttonId,hackageUrl(packageId,locationInfo));
+ }
+ }).catch((e) => {
+ console.log(e);
+ saveCurrentLocation(currentLineNumber);
+ openUrl(buttonId,hackageUrl(packageId,locationInfo));
+ });
+ }
+ } else {
+ alert('No location info');
+ }
+}
+
+export {
+ goToDefinition,openUrl
+}
diff --git a/javascript/app/utils/line-selection.js b/javascript/app/utils/line-selection.js
new file mode 100644
index 0000000..d6d0976
--- /dev/null
+++ b/javascript/app/utils/line-selection.js
@@ -0,0 +1,111 @@
+function initializeLineSelection(sourceCodeContainerElement,component) {
+ const lineNumbers = Array.prototype.slice.call(sourceCodeContainerElement.querySelectorAll("td.line-number"));
+ if(lineNumbers.length > 0) {
+ const onhashchange = function () {
+ highlightSelectedLines(sourceCodeContainerElement);
+ }
+ window.addEventListener("hashchange",onhashchange);
+ component._onhashchange = onhashchange;
+
+ let shiftPressed;
+ const onkeydown = function (event) {
+ if(event.keyCode === 16) { shiftPressed = true; }
+ };
+ const onkeyup = function (event) {
+ if(event.keyCode === 16) { shiftPressed = false; }
+ };
+
+ document.addEventListener('keydown',onkeydown);
+ document.addEventListener('keyup',onkeyup);
+ component._onkeydown = onkeydown;
+ component._onkeyup = onkeyup;
+
+ let selectedLine1,selectedLine2;
+ lineNumbers.forEach((lineNumberElement) => {
+ lineNumberElement.onclick = function() {
+ const number = parseInt(this.textContent);
+ if(shiftPressed && selectedLine1) {
+ if(selectedLine1 != number) {
+ selectedLine2 = number;
+ if(selectedLine1 < selectedLine2) {
+ highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine2);
+ window.location.hash = "L"+selectedLine1+"-L"+selectedLine2;
+ } else {
+ highlightLines(sourceCodeContainerElement,selectedLine2,selectedLine1);
+ window.location.hash = "L"+selectedLine2+"-L"+selectedLine1;
+ }
+ }
+ } else {
+ selectedLine1 = number;
+ selectedLine2 = null;
+ highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine1);
+ window.location.hash = "L"+number;
+ }
+ }
+ });
+ const lines = highlightSelectedLines(sourceCodeContainerElement);
+ if(lines.length) {
+ selectedLine1 = lines[0];
+ selectedLine2 = lines[1];
+ }
+ }
+}
+
+function highlightSelectedLines (sourceCodeContainerElement) {
+ const lineInfo = window.location.hash.slice(1);
+ if(lineInfo) {
+ if(lineInfo.includes('-')) {
+ const lines = lineInfo.split("-");
+ const lineNumber1 = parseInt(lines[0].substring(1));
+ const lineNumber2 = parseInt(lines[1].substring(1));
+ if(lineNumber1 && lineNumber2 && lineNumber1 <= lineNumber2) {
+ highlightLines(sourceCodeContainerElement,lineNumber1,lineNumber2);
+ const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber1);
+ if(line) {
+ scrollLineIntoView(line,sourceCodeContainerElement);
+ }
+ return [lineNumber1,lineNumber2];
+ }
+ } else {
+ const lineNumber = parseInt(lineInfo.substring(1));
+ if(lineNumber) {
+ highlightLines(sourceCodeContainerElement,lineNumber,lineNumber);
+ const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber);
+ if(line) {
+ scrollLineIntoView(line,sourceCodeContainerElement);
+ }
+ return [lineNumber];
+ }
+ }
+ } else {
+ highlightLines(sourceCodeContainerElement,0,0);
+ return [];
+ }
+}
+
+function scrollLineIntoView(lineElement,sourceCodeContainerElement) {
+ lineElement.parentNode.scrollIntoView();
+ const container = sourceCodeContainerElement.parentNode.parentNode;
+ const windowHeight = container.offsetHeight;
+ const fullHeight = sourceCodeContainerElement.offsetHeight;
+
+ if(fullHeight - container.scrollTop > windowHeight) {
+ container.scrollTop = container.scrollTop - (windowHeight/2 - 20);
+ }
+}
+
+function highlightLines(parentElement,startLine,endLine) {
+ const lineElements = Array.prototype.slice.call(parentElement.querySelectorAll("td.line-content"));
+ lineElements.forEach((lineElement) => {
+ const number = parseInt(lineElement.id.substring(2)); //<td "id"="LC10">...</td>
+ if(number >= startLine && number <= endLine) {
+ lineElement.classList.add('highlighted-line');
+ } else {
+ lineElement.classList.remove('highlighted-line');
+ }
+ });
+}
+
+export {
+ initializeLineSelection,highlightLines,highlightSelectedLines
+}