diff --git a/.eslintrc.js b/.eslintrc.js
index 322567959..78b7288cb 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,15 +1,11 @@
module.exports = {
- "env": {
- "browser": true,
- "jquery": true,
- "jest/globals": true,
- },
- "extends": [
- "eslint:recommended",
- ],
- "parser": "@babel/eslint-parser",
- "plugins": [
- "jest",
- ],
- "rules": {},
+ env: {
+ browser: true,
+ jquery: true,
+ "jest/globals": true,
+ },
+ extends: ["eslint:recommended"],
+ parser: "@babel/eslint-parser",
+ plugins: ["jest"],
+ rules: {},
};
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2a0209189..c94bfcd98 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -58,7 +58,6 @@ repos:
# separate run of pre-commit where we configure a line spacing of 4
# for these file formats.
- html
- - javascript
# Misc autoformatting and linting
- repo: https://github.com/pre-commit/pre-commit-hooks
diff --git a/binderhub/static/js/index.js b/binderhub/static/js/index.js
index b934ba516..120294f30 100644
--- a/binderhub/static/js/index.js
+++ b/binderhub/static/js/index.js
@@ -1,50 +1,58 @@
/* If this file gets over 200 lines of code long (not counting docs / comments), start using a framework
-*/
-import ClipboardJS from 'clipboard';
-import 'event-source-polyfill';
+ */
+import ClipboardJS from "clipboard";
+import "event-source-polyfill";
-import { BinderRepository } from '@jupyterhub/binderhub-client';
-import { updatePathText } from './src/path';
-import { nextHelpText } from './src/loading';
-import { updateFavicon } from './src/favicon';
+import { BinderRepository } from "@jupyterhub/binderhub-client";
+import { updatePathText } from "./src/path";
+import { nextHelpText } from "./src/loading";
+import { updateFavicon } from "./src/favicon";
-import 'xterm/css/xterm.css';
+import "xterm/css/xterm.css";
// Include just the bootstrap components we use
-import 'bootstrap/js/dropdown';
-import 'bootstrap/dist/css/bootstrap.min.css';
-import 'bootstrap/dist/css/bootstrap-theme.min.css';
-
-import '../index.css';
-import { setUpLog } from './src/log';
-import { updateUrls } from './src/urls';
-import { BASE_URL } from './src/constants';
-import { getBuildFormValues } from './src/form';
-import { updateRepoText } from './src/repo';
+import "bootstrap/js/dropdown";
+import "bootstrap/dist/css/bootstrap.min.css";
+import "bootstrap/dist/css/bootstrap-theme.min.css";
+import "../index.css";
+import { setUpLog } from "./src/log";
+import { updateUrls } from "./src/urls";
+import { BASE_URL } from "./src/constants";
+import { getBuildFormValues } from "./src/form";
+import { updateRepoText } from "./src/repo";
function build(providerSpec, log, fitAddon, path, pathType) {
updateFavicon(BASE_URL + "favicon_building.ico");
// split provider prefix off of providerSpec
- const spec = providerSpec.slice(providerSpec.indexOf('/') + 1);
+ const spec = providerSpec.slice(providerSpec.indexOf("/") + 1);
// Update the text of the loading page if it exists
- if ($('div#loader-text').length > 0) {
- $('div#loader-text p.launching').text("Starting repository: " + decodeURIComponent(spec))
+ if ($("div#loader-text").length > 0) {
+ $("div#loader-text p.launching").text(
+ "Starting repository: " + decodeURIComponent(spec),
+ );
}
- $('#build-progress .progress-bar').addClass('hidden');
+ $("#build-progress .progress-bar").addClass("hidden");
log.clear();
- $('.on-build').removeClass('hidden');
+ $(".on-build").removeClass("hidden");
- const buildToken = $("#build-token").data('token');
+ const buildToken = $("#build-token").data("token");
// If BASE_URL is absolute, use that as the base for build endpoint URL.
// Else, first resolve BASE_URL relative to current URL, then use *that* as the
// base for the build endpoint url.
- const buildEndpointUrl = new URL("build", new URL(BASE_URL, window.location.href));
- const image = new BinderRepository(providerSpec, buildEndpointUrl, buildToken);
-
- image.onStateChange('*', function(data) {
+ const buildEndpointUrl = new URL(
+ "build",
+ new URL(BASE_URL, window.location.href),
+ );
+ const image = new BinderRepository(
+ providerSpec,
+ buildEndpointUrl,
+ buildToken,
+ );
+
+ image.onStateChange("*", function (data) {
if (data.message !== undefined) {
log.writeAndStore(data.message);
fitAddon.fit();
@@ -53,48 +61,55 @@ function build(providerSpec, log, fitAddon, path, pathType) {
}
});
- image.onStateChange('waiting', function() {
- $('#phase-waiting').removeClass('hidden');
+ image.onStateChange("waiting", function () {
+ $("#phase-waiting").removeClass("hidden");
});
- image.onStateChange('building', function() {
- $('#phase-building').removeClass('hidden');
+ image.onStateChange("building", function () {
+ $("#phase-building").removeClass("hidden");
log.show();
});
- image.onStateChange('pushing', function() {
- $('#phase-pushing').removeClass('hidden');
+ image.onStateChange("pushing", function () {
+ $("#phase-pushing").removeClass("hidden");
});
- image.onStateChange('failed', function() {
- $('#build-progress .progress-bar').addClass('hidden');
- $('#phase-failed').removeClass('hidden');
+ image.onStateChange("failed", function () {
+ $("#build-progress .progress-bar").addClass("hidden");
+ $("#phase-failed").removeClass("hidden");
$("#loader").addClass("paused");
// If we fail for any reason, show an error message and logs
updateFavicon(BASE_URL + "favicon_fail.ico");
log.show();
- if ($('div#loader-text').length > 0) {
- $('#loader').addClass("error");
- $('div#loader-text p.launching').html('Error loading ' + spec + '!
See logs below for details.');
+ if ($("div#loader-text").length > 0) {
+ $("#loader").addClass("error");
+ $("div#loader-text p.launching").html(
+ "Error loading " + spec + "!
See logs below for details.",
+ );
}
image.close();
});
- image.onStateChange('built', function() {
- $('#phase-already-built').removeClass('hidden');
- $('#phase-launching').removeClass('hidden');
+ image.onStateChange("built", function () {
+ $("#phase-already-built").removeClass("hidden");
+ $("#phase-launching").removeClass("hidden");
updateFavicon(BASE_URL + "favicon_success.ico");
});
- image.onStateChange('ready', function(data) {
+ image.onStateChange("ready", function (data) {
image.close();
// If data.url is an absolute URL, it'll be used. Else, it'll be interpreted
// relative to current page's URL.
const serverUrl = new URL(data.url, window.location.href);
// user server is ready, redirect to there
- window.location.href = image.getFullRedirectURL(serverUrl, data.token, path, pathType);
+ window.location.href = image.getFullRedirectURL(
+ serverUrl,
+ data.token,
+ path,
+ pathType,
+ );
});
image.fetch();
@@ -102,62 +117,71 @@ function build(providerSpec, log, fitAddon, path, pathType) {
}
function indexMain() {
- const [log, fitAddon] = setUpLog();
+ const [log, fitAddon] = setUpLog();
- // setup badge dropdown and default values.
- updateUrls();
+ // setup badge dropdown and default values.
+ updateUrls();
- $("#provider_prefix_sel li").click(function(event){
- event.preventDefault();
+ $("#provider_prefix_sel li").click(function (event) {
+ event.preventDefault();
- $("#provider_prefix-selected").text($(this).text());
- $("#provider_prefix").val($(this).attr("value"));
- updateRepoText();
- updateUrls();
- });
+ $("#provider_prefix-selected").text($(this).text());
+ $("#provider_prefix").val($(this).attr("value"));
+ updateRepoText();
+ updateUrls();
+ });
- $("#url-or-file-btn").find("a").click(function (evt) {
+ $("#url-or-file-btn")
+ .find("a")
+ .click(function (evt) {
evt.preventDefault();
$("#url-or-file-selected").text($(this).text());
updatePathText();
updateUrls();
});
- updatePathText();
- updateRepoText();
+ updatePathText();
+ updateRepoText();
- $('#repository').on('keyup paste change', function() {updateUrls();});
+ $("#repository").on("keyup paste change", function () {
+ updateUrls();
+ });
- $('#ref').on('keyup paste change', function() {updateUrls();});
+ $("#ref").on("keyup paste change", function () {
+ updateUrls();
+ });
- $('#filepath').on('keyup paste change', function() {updateUrls();});
+ $("#filepath").on("keyup paste change", function () {
+ updateUrls();
+ });
- $('#toggle-badge-snippet').on('click', function() {
- const badgeSnippets = $('#badge-snippets');
- if (badgeSnippets.hasClass('hidden')) {
- badgeSnippets.removeClass('hidden');
- $("#badge-snippet-caret").removeClass("glyphicon-triangle-right");
- $("#badge-snippet-caret").addClass("glyphicon-triangle-bottom");
- } else {
- badgeSnippets.addClass('hidden');
- $("#badge-snippet-caret").removeClass("glyphicon-triangle-bottom");
- $("#badge-snippet-caret").addClass("glyphicon-triangle-right");
- }
+ $("#toggle-badge-snippet").on("click", function () {
+ const badgeSnippets = $("#badge-snippets");
+ if (badgeSnippets.hasClass("hidden")) {
+ badgeSnippets.removeClass("hidden");
+ $("#badge-snippet-caret").removeClass("glyphicon-triangle-right");
+ $("#badge-snippet-caret").addClass("glyphicon-triangle-bottom");
+ } else {
+ badgeSnippets.addClass("hidden");
+ $("#badge-snippet-caret").removeClass("glyphicon-triangle-bottom");
+ $("#badge-snippet-caret").addClass("glyphicon-triangle-right");
+ }
- return false;
- });
+ return false;
+ });
- $('#build-form').submit(function() {
- const formValues = getBuildFormValues();
- updateUrls(formValues);
- build(
- formValues.providerPrefix + '/' + formValues.repo + '/' + formValues.ref,
- log, fitAddon,
- formValues.path,
- formValues.pathType
- );
- return false;
- });
+ $("#build-form").submit(function () {
+ const formValues = getBuildFormValues();
+ updateUrls(formValues);
+ build(
+ formValues.providerPrefix + "/" + formValues.repo + "/" + formValues.ref,
+ log,
+ fitAddon,
+ formValues.path,
+ formValues.pathType,
+ );
+ return false;
+ });
}
function loadingMain(providerSpec) {
@@ -167,32 +191,32 @@ function loadingMain(providerSpec) {
// that is good because it is the real value and '/'s will be trimmed in `launch`
const params = new URL(location.href).searchParams;
let pathType, path;
- path = params.get('urlpath');
+ path = params.get("urlpath");
if (path) {
- pathType = 'url';
+ pathType = "url";
} else {
- path = params.get('labpath');
+ path = params.get("labpath");
if (path) {
- pathType = 'lab';
+ pathType = "lab";
} else {
- path = params.get('filepath');
+ path = params.get("filepath");
if (path) {
- pathType = 'file';
+ pathType = "file";
}
}
}
build(providerSpec, log, fitAddon, path, pathType);
// Looping through help text every few seconds
- const launchMessageInterval = 6 * 1000
+ const launchMessageInterval = 6 * 1000;
setInterval(nextHelpText, launchMessageInterval);
// If we have a long launch, add a class so we display a long launch msg
- const launchTimeout = 120 * 1000
+ const launchTimeout = 120 * 1000;
setTimeout(() => {
- $('div#loader-links p.text-center').addClass("longLaunch");
+ $("div#loader-links p.text-center").addClass("longLaunch");
nextHelpText();
- }, launchTimeout)
+ }, launchTimeout);
return false;
}
@@ -202,6 +226,6 @@ window.loadingMain = loadingMain;
window.indexMain = indexMain;
// Load the clipboard after the page loads so it can find the buttons it needs
-window.onload = function() {
- new ClipboardJS('.clipboard');
+window.onload = function () {
+ new ClipboardJS(".clipboard");
};
diff --git a/binderhub/static/js/src/badge.js b/binderhub/static/js/src/badge.js
index 815a32ef4..8ec5d105f 100644
--- a/binderhub/static/js/src/badge.js
+++ b/binderhub/static/js/src/badge.js
@@ -7,10 +7,9 @@ export function makeBadgeMarkup(badgeBaseUrl, baseUrl, url, syntax) {
badgeImageUrl = window.location.origin + baseUrl + "badge_logo.svg";
}
- if (syntax === 'markdown') {
+ if (syntax === "markdown") {
return "[![Binder](" + badgeImageUrl + ")](" + url + ")";
- } else if (syntax === 'rst') {
+ } else if (syntax === "rst") {
return ".. image:: " + badgeImageUrl + "\n :target: " + url;
-
}
}
diff --git a/binderhub/static/js/src/constants.js b/binderhub/static/js/src/constants.js
index 497ffa9c7..b1950ae0a 100644
--- a/binderhub/static/js/src/constants.js
+++ b/binderhub/static/js/src/constants.js
@@ -1,3 +1,2 @@
-
-export const BASE_URL = $('#base-url').data().url;
-export const BADGE_BASE_URL = $('#badge-base-url').data().url;
+export const BASE_URL = $("#base-url").data().url;
+export const BADGE_BASE_URL = $("#badge-base-url").data().url;
diff --git a/binderhub/static/js/src/favicon.js b/binderhub/static/js/src/favicon.js
index 1353226dc..b4a7dead1 100644
--- a/binderhub/static/js/src/favicon.js
+++ b/binderhub/static/js/src/favicon.js
@@ -4,14 +4,14 @@
* @param {String} href Path to Favicon to use
*/
function updateFavicon(href) {
- let link = document.querySelector("link[rel*='icon']");
- if(!link) {
- link = document.createElement('link');
- document.getElementsByTagName('head')[0].appendChild(link);
- }
- link.type = 'image/x-icon';
- link.rel = 'shortcut icon';
- link.href = href;
+ let link = document.querySelector("link[rel*='icon']");
+ if (!link) {
+ link = document.createElement("link");
+ document.getElementsByTagName("head")[0].appendChild(link);
+ }
+ link.type = "image/x-icon";
+ link.rel = "shortcut icon";
+ link.href = href;
}
export { updateFavicon };
diff --git a/binderhub/static/js/src/favicon.test.js b/binderhub/static/js/src/favicon.test.js
index dfd515d3a..c73cab495 100644
--- a/binderhub/static/js/src/favicon.test.js
+++ b/binderhub/static/js/src/favicon.test.js
@@ -1,29 +1,35 @@
import { updateFavicon } from "./favicon";
afterEach(() => {
- // Clear out HEAD after each test run, so our DOM is clean.
- // Jest does *not* clear out the DOM between test runs on the same file!
- document.querySelector("head").innerHTML = '';
+ // Clear out HEAD after each test run, so our DOM is clean.
+ // Jest does *not* clear out the DOM between test runs on the same file!
+ document.querySelector("head").innerHTML = "";
});
test("Setting favicon when there is none works", () => {
- expect(document.querySelector("link[rel*='icon']")).toBeNull();
+ expect(document.querySelector("link[rel*='icon']")).toBeNull();
- updateFavicon("https://example.com/somefile.png");
+ updateFavicon("https://example.com/somefile.png");
- expect(document.querySelector("link[rel*='icon']").href).toBe("https://example.com/somefile.png");
+ expect(document.querySelector("link[rel*='icon']").href).toBe(
+ "https://example.com/somefile.png",
+ );
});
test("Setting favicon multiple times works without leaking link tags", () => {
- expect(document.querySelector("link[rel*='icon']")).toBeNull();
+ expect(document.querySelector("link[rel*='icon']")).toBeNull();
- updateFavicon("https://example.com/somefile.png");
+ updateFavicon("https://example.com/somefile.png");
- expect(document.querySelector("link[rel*='icon']").href).toBe("https://example.com/somefile.png");
- expect(document.querySelectorAll("link[rel*='icon']").length).toBe(1);
+ expect(document.querySelector("link[rel*='icon']").href).toBe(
+ "https://example.com/somefile.png",
+ );
+ expect(document.querySelectorAll("link[rel*='icon']").length).toBe(1);
- updateFavicon("https://example.com/some-other-file.png");
+ updateFavicon("https://example.com/some-other-file.png");
- expect(document.querySelector("link[rel*='icon']").href).toBe("https://example.com/some-other-file.png");
- expect(document.querySelectorAll("link[rel*='icon']").length).toBe(1);
+ expect(document.querySelector("link[rel*='icon']").href).toBe(
+ "https://example.com/some-other-file.png",
+ );
+ expect(document.querySelectorAll("link[rel*='icon']").length).toBe(1);
});
diff --git a/binderhub/static/js/src/form.js b/binderhub/static/js/src/form.js
index 44be45986..60469d1ab 100644
--- a/binderhub/static/js/src/form.js
+++ b/binderhub/static/js/src/form.js
@@ -1,30 +1,36 @@
-import { getPathType } from './path';
-
+import { getPathType } from "./path";
export function getBuildFormValues() {
- const providerPrefix = $('#provider_prefix').val().trim();
- let repo = $('#repository').val().trim();
- if (providerPrefix !== 'git') {
- repo = repo.replace(/^(https?:\/\/)?gist.github.com\//, '');
- repo = repo.replace(/^(https?:\/\/)?github.com\//, '');
- repo = repo.replace(/^(https?:\/\/)?gitlab.com\//, '');
+ const providerPrefix = $("#provider_prefix").val().trim();
+ let repo = $("#repository").val().trim();
+ if (providerPrefix !== "git") {
+ repo = repo.replace(/^(https?:\/\/)?gist.github.com\//, "");
+ repo = repo.replace(/^(https?:\/\/)?github.com\//, "");
+ repo = repo.replace(/^(https?:\/\/)?gitlab.com\//, "");
}
// trim trailing or leading '/' on repo
- repo = repo.replace(/(^\/)|(\/?$)/g, '');
+ repo = repo.replace(/(^\/)|(\/?$)/g, "");
// git providers encode the URL of the git repository as the repo
// argument.
- if (repo.includes("://") || providerPrefix === 'gl') {
+ if (repo.includes("://") || providerPrefix === "gl") {
repo = encodeURIComponent(repo);
}
- let ref = $('#ref').val().trim() || $("#ref").attr("placeholder");
- if (providerPrefix === 'zenodo' || providerPrefix === 'figshare' || providerPrefix === 'dataverse' ||
- providerPrefix === 'hydroshare') {
+ let ref = $("#ref").val().trim() || $("#ref").attr("placeholder");
+ if (
+ providerPrefix === "zenodo" ||
+ providerPrefix === "figshare" ||
+ providerPrefix === "dataverse" ||
+ providerPrefix === "hydroshare"
+ ) {
ref = "";
}
- const path = $('#filepath').val().trim();
+ const path = $("#filepath").val().trim();
return {
- 'providerPrefix': providerPrefix, 'repo': repo,
- 'ref': ref, 'path': path, 'pathType': getPathType()
+ providerPrefix: providerPrefix,
+ repo: repo,
+ ref: ref,
+ path: path,
+ pathType: getPathType(),
};
}
diff --git a/binderhub/static/js/src/loading.js b/binderhub/static/js/src/loading.js
index 956f87e7d..3569fd256 100644
--- a/binderhub/static/js/src/loading.js
+++ b/binderhub/static/js/src/loading.js
@@ -1,31 +1,32 @@
// Cycle through helpful messages on the loading page
const help_messages = [
- 'New to Binder? Check out the Binder Documentation for more information.',
- 'You can learn more about building your own Binder repositories in the Binder community documentation.',
- 'We use the repo2docker tool to automatically build the environment in which to run your code.',
- 'Take a look at the full list of configuration files supported by repo2docker.',
- 'Need more than just a Jupyter notebook? You can customize the user interface.',
- 'Take a look at our gallery of example repositories.',
- 'If a repository takes a long time to launch, it is usually because Binder needs to create the environment for the first time.',
- 'The tool that powers this page is called BinderHub. It is an open source tool that you can deploy yourself.',
- 'The Binder team has a site reliability guide that talks about what it is like to run a BinderHub.',
- 'You can connect with the Binder community in the Jupyter community forum.',
- 'Empty log? Notebook not loading? Maybe your ad blocker is interfering. Consider adding this site to the list of trusted sources.',
- 'Your launch may take longer the first few times a repository is used. This is because our machine needs to create your environment.',
- 'Read our advice for speeding up your Binder launch.',
-]
+ 'New to Binder? Check out the Binder Documentation for more information.',
+ 'You can learn more about building your own Binder repositories in the Binder community documentation.',
+ 'We use the repo2docker tool to automatically build the environment in which to run your code.',
+ 'Take a look at the full list of configuration files supported by repo2docker.',
+ 'Need more than just a Jupyter notebook? You can customize the user interface.',
+ 'Take a look at our gallery of example repositories.',
+ "If a repository takes a long time to launch, it is usually because Binder needs to create the environment for the first time.",
+ 'The tool that powers this page is called BinderHub. It is an open source tool that you can deploy yourself.',
+ 'The Binder team has a site reliability guide that talks about what it is like to run a BinderHub.',
+ 'You can connect with the Binder community in the Jupyter community forum.',
+ "Empty log? Notebook not loading? Maybe your ad blocker is interfering. Consider adding this site to the list of trusted sources.",
+ "Your launch may take longer the first few times a repository is used. This is because our machine needs to create your environment.",
+ 'Read our advice for speeding up your Binder launch.',
+];
// Set a launch timeout beyond-which we'll stop cycling messages
-export function nextHelpText () {
- const text = $('div#loader-links p.text-center');
- let msg;
- if (text !== null) {
- if (!text.hasClass('longLaunch')) {
- // Pick a random help message and update
- msg = help_messages[Math.floor(Math.random() * help_messages.length)];
- } else {
- msg = 'Your session is taking longer than usual to start!
Check the log messages below to see what is happening.';
- }
- text.html(msg);
+export function nextHelpText() {
+ const text = $("div#loader-links p.text-center");
+ let msg;
+ if (text !== null) {
+ if (!text.hasClass("longLaunch")) {
+ // Pick a random help message and update
+ msg = help_messages[Math.floor(Math.random() * help_messages.length)];
+ } else {
+ msg =
+ "Your session is taking longer than usual to start!
Check the log messages below to see what is happening.";
}
+ text.html(msg);
+ }
}
diff --git a/binderhub/static/js/src/log.js b/binderhub/static/js/src/log.js
index 5c687eb50..46664cb40 100644
--- a/binderhub/static/js/src/log.js
+++ b/binderhub/static/js/src/log.js
@@ -1,17 +1,17 @@
-import { Terminal } from 'xterm';
-import { FitAddon } from 'xterm-addon-fit';
+import { Terminal } from "xterm";
+import { FitAddon } from "xterm-addon-fit";
export function setUpLog() {
const log = new Terminal({
convertEol: true,
- disableStdin: true
+ disableStdin: true,
});
const fitAddon = new FitAddon();
log.loadAddon(fitAddon);
const logMessages = [];
- log.open(document.getElementById('log'), false);
+ log.open(document.getElementById("log"), false);
fitAddon.fit();
$(window).resize(function () {
@@ -20,31 +20,31 @@ export function setUpLog() {
const $panelBody = $("div.panel-body");
log.show = function () {
- $('#toggle-logs button.toggle').text('hide');
- $panelBody.removeClass('hidden');
+ $("#toggle-logs button.toggle").text("hide");
+ $panelBody.removeClass("hidden");
};
log.hide = function () {
- $('#toggle-logs button.toggle').text('show');
- $panelBody.addClass('hidden');
+ $("#toggle-logs button.toggle").text("show");
+ $panelBody.addClass("hidden");
};
log.toggle = function () {
- if ($panelBody.hasClass('hidden')) {
+ if ($panelBody.hasClass("hidden")) {
log.show();
} else {
log.hide();
}
};
- $('#view-raw-logs').on('click', function (ev) {
- const blob = new Blob([logMessages.join('')], { type: 'text/plain' });
+ $("#view-raw-logs").on("click", function (ev) {
+ const blob = new Blob([logMessages.join("")], { type: "text/plain" });
this.href = window.URL.createObjectURL(blob);
// Prevent the toggle action from firing
ev.stopPropagation();
});
- $('#toggle-logs').click(log.toggle);
+ $("#toggle-logs").click(log.toggle);
log.writeAndStore = function (msg) {
logMessages.push(msg);
diff --git a/binderhub/static/js/src/repo.js b/binderhub/static/js/src/repo.js
index c71c7672e..42dceb62b 100644
--- a/binderhub/static/js/src/repo.js
+++ b/binderhub/static/js/src/repo.js
@@ -10,21 +10,23 @@ function setLabels() {
const label_prop_disabled = configDict[provider]["label_prop_disabled"];
const placeholder = "HEAD";
- $("#ref").attr('placeholder', placeholder).prop("disabled", ref_prop_disabled);
+ $("#ref")
+ .attr("placeholder", placeholder)
+ .prop("disabled", ref_prop_disabled);
$("label[for=ref]").text(tag_text).prop("disabled", label_prop_disabled);
- $("#repository").attr('placeholder', text);
+ $("#repository").attr("placeholder", text);
$("label[for=repository]").text(text);
}
export function updateRepoText() {
if (Object.keys(configDict).length === 0) {
const configUrl = BASE_URL + "_config";
- fetch(configUrl).then(resp => {
- resp.json().then(data => {
- configDict = data
+ fetch(configUrl).then((resp) => {
+ resp.json().then((data) => {
+ configDict = data;
setLabels();
});
- })
+ });
} else {
setLabels();
}
diff --git a/binderhub/static/js/src/urls.js b/binderhub/static/js/src/urls.js
index b869c10f6..5f58a9ecd 100644
--- a/binderhub/static/js/src/urls.js
+++ b/binderhub/static/js/src/urls.js
@@ -1,6 +1,6 @@
-import { makeBadgeMarkup } from './badge';
-import { getBuildFormValues } from './form';
-import { BADGE_BASE_URL, BASE_URL } from './constants';
+import { makeBadgeMarkup } from "./badge";
+import { getBuildFormValues } from "./form";
+import { BADGE_BASE_URL, BASE_URL } from "./constants";
function v2url(providerPrefix, repository, ref, path, pathType) {
// return a v2 url from a providerPrefix, repository, ref, and (file|url)path
@@ -10,14 +10,22 @@ function v2url(providerPrefix, repository, ref, path, pathType) {
}
let url;
if (BADGE_BASE_URL) {
- url = BADGE_BASE_URL + 'v2/' + providerPrefix + '/' + repository + '/' + ref;
- }
- else {
- url = window.location.origin + BASE_URL + 'v2/' + providerPrefix + '/' + repository + '/' + ref;
+ url =
+ BADGE_BASE_URL + "v2/" + providerPrefix + "/" + repository + "/" + ref;
+ } else {
+ url =
+ window.location.origin +
+ BASE_URL +
+ "v2/" +
+ providerPrefix +
+ "/" +
+ repository +
+ "/" +
+ ref;
}
if (path && path.length > 0) {
// encode the path, it will be decoded in loadingMain
- url = url + '?' + pathType + 'path=' + encodeURIComponent(path);
+ url = url + "?" + pathType + "path=" + encodeURIComponent(path);
}
return url;
}
@@ -31,23 +39,25 @@ export function updateUrls(formValues) {
formValues.repo,
formValues.ref,
formValues.path,
- formValues.pathType
+ formValues.pathType,
);
- if ((url || '').trim().length > 0) {
+ if ((url || "").trim().length > 0) {
// update URLs and links (badges, etc.)
- $("#badge-link").attr('href', url);
- $('#basic-url-snippet').text(url);
- $('#markdown-badge-snippet').text(
- makeBadgeMarkup(BADGE_BASE_URL, BASE_URL, url, 'markdown')
+ $("#badge-link").attr("href", url);
+ $("#basic-url-snippet").text(url);
+ $("#markdown-badge-snippet").text(
+ makeBadgeMarkup(BADGE_BASE_URL, BASE_URL, url, "markdown"),
);
- $('#rst-badge-snippet').text(
- makeBadgeMarkup(BADGE_BASE_URL, BASE_URL, url, 'rst')
+ $("#rst-badge-snippet").text(
+ makeBadgeMarkup(BADGE_BASE_URL, BASE_URL, url, "rst"),
);
} else {
- ['#basic-url-snippet', '#markdown-badge-snippet', '#rst-badge-snippet'].map(function (item) {
- const el = $(item);
- el.text(el.attr('data-default'));
- });
+ ["#basic-url-snippet", "#markdown-badge-snippet", "#rst-badge-snippet"].map(
+ function (item) {
+ const el = $(item);
+ el.text(el.attr("data-default"));
+ },
+ );
}
}
diff --git a/examples/appendix/static/custom.js b/examples/appendix/static/custom.js
index ca98c1481..8b62e0f7a 100644
--- a/examples/appendix/static/custom.js
+++ b/examples/appendix/static/custom.js
@@ -1,35 +1,48 @@
function copy_link_into_clipboard(b) {
- var $temp = $("");
- $(b).parent().append($temp);
- $temp.val($(b).data('url')).select();
- document.execCommand("copy");
- $temp.remove();
+ var $temp = $("");
+ $(b).parent().append($temp);
+ $temp.val($(b).data("url")).select();
+ document.execCommand("copy");
+ $temp.remove();
}
function add_binder_buttons() {
- var copy_button = '';
+ var copy_button =
+ '";
- var link_button = '' +
- ' Go to {name}';
+ var link_button =
+ '' +
+ " Go to {name}";
- var s = $("");
- s.append(link_button.replace(/{name}/g, 'repo').replace('{url}', '{repo_url}'));
- s.append(copy_button.replace(/{name}/g, 'binder').replace('{url}', '{binder_url}'));
- if ($("#ipython_notebook").length && $("#ipython_notebook>a").length) {
- s.append(copy_button.replace(/{name}/g, 'session').replace('{url}', window.location.origin.concat($("#ipython_notebook>a").attr('href'))));
- }
- // add buttons at the end of header-container
- $("#header-container").append(s);
+ var s = $("");
+ s.append(
+ link_button.replace(/{name}/g, "repo").replace("{url}", "{repo_url}"),
+ );
+ s.append(
+ copy_button.replace(/{name}/g, "binder").replace("{url}", "{binder_url}"),
+ );
+ if ($("#ipython_notebook").length && $("#ipython_notebook>a").length) {
+ s.append(
+ copy_button
+ .replace(/{name}/g, "session")
+ .replace(
+ "{url}",
+ window.location.origin.concat($("#ipython_notebook>a").attr("href")),
+ ),
+ );
+ }
+ // add buttons at the end of header-container
+ $("#header-container").append(s);
}
add_binder_buttons();
diff --git a/js/packages/binderhub-client/lib/index.js b/js/packages/binderhub-client/lib/index.js
index fa9c66f56..46b09164f 100644
--- a/js/packages/binderhub-client/lib/index.js
+++ b/js/packages/binderhub-client/lib/index.js
@@ -1,4 +1,4 @@
-import { NativeEventSource, EventSourcePolyfill } from 'event-source-polyfill';
+import { NativeEventSource, EventSourcePolyfill } from "event-source-polyfill";
// Use native browser EventSource if available, and use the polyfill if not available
const EventSource = NativeEventSource || EventSourcePolyfill;
@@ -16,20 +16,22 @@ export class BinderRepository {
constructor(providerSpec, buildEndpointUrl, buildToken) {
this.providerSpec = providerSpec;
// Make sure that buildEndpointUrl is a real URL - this ensures hostname is properly set
- if(!(buildEndpointUrl instanceof URL)) {
- throw new TypeError(`buildEndpointUrl must be a URL object, got ${buildEndpointUrl} instead`);
+ if (!(buildEndpointUrl instanceof URL)) {
+ throw new TypeError(
+ `buildEndpointUrl must be a URL object, got ${buildEndpointUrl} instead`,
+ );
}
// We make a copy here so we don't modify the passed in URL object
this.buildEndpointUrl = new URL(buildEndpointUrl);
// The binderHub API is path based, so the buildEndpointUrl must have a trailing slash. We add
// it if it is not passed in here to us.
- if(!this.buildEndpointUrl.pathname.endsWith('/')) {
+ if (!this.buildEndpointUrl.pathname.endsWith("/")) {
this.buildEndpointUrl.pathname += "/";
}
// The actual URL we'll make a request to build this particular providerSpec
this.buildUrl = new URL(this.providerSpec, this.buildEndpointUrl);
- if(buildToken) {
+ if (buildToken) {
this.buildUrl.searchParams.append("build_token", buildToken);
}
this.callbacks = {};
@@ -43,7 +45,7 @@ export class BinderRepository {
this.eventSource.onerror = (err) => {
console.error("Failed to construct event stream", err);
this._changeState("failed", {
- message: "Failed to connect to event stream\n"
+ message: "Failed to connect to event stream\n",
});
};
this.eventSource.addEventListener("message", (event) => {
@@ -108,11 +110,10 @@ export class BinderRepository {
}
}
- url.searchParams.append('token', token);
+ url.searchParams.append("token", token);
return url;
}
-
/**
* Add callback whenever state of the current build changes
*
@@ -128,7 +129,7 @@ export class BinderRepository {
}
_changeState(state, data) {
- [state, "*"].map(key => {
+ [state, "*"].map((key) => {
const callbacks = this.callbacks[key];
if (callbacks) {
for (let i = 0; i < callbacks.length; i++) {
diff --git a/js/packages/binderhub-client/lib/index.test.js b/js/packages/binderhub-client/lib/index.test.js
index 7fd96c445..dd03daab3 100644
--- a/js/packages/binderhub-client/lib/index.test.js
+++ b/js/packages/binderhub-client/lib/index.test.js
@@ -4,7 +4,7 @@ test("Passed in URL object is not modified", () => {
const buildEndpointUrl = new URL("https://test-binder.org/build");
const br = new BinderRepository("gh/test/test", buildEndpointUrl, "token");
expect(br.buildEndpointUrl.toString()).not.toEqual(
- buildEndpointUrl.toString()
+ buildEndpointUrl.toString(),
);
});
@@ -18,7 +18,7 @@ test("Trailing slash added if needed", () => {
const buildEndpointUrl = new URL("https://test-binder.org/build");
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
expect(br.buildEndpointUrl.toString()).toEqual(
- "https://test-binder.org/build/"
+ "https://test-binder.org/build/",
);
});
@@ -26,7 +26,7 @@ test("Build URL correctly built from Build Endpoint", () => {
const buildEndpointUrl = new URL("https://test-binder.org/build");
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
expect(br.buildUrl.toString()).toEqual(
- "https://test-binder.org/build/gh/test/test"
+ "https://test-binder.org/build/gh/test/test",
);
});
@@ -34,26 +34,26 @@ test("Build URL correctly built from Build Endpoint when used with token", () =>
const buildEndpointUrl = new URL("https://test-binder.org/build");
const br = new BinderRepository("gh/test/test", buildEndpointUrl, "token");
expect(br.buildUrl.toString()).toEqual(
- "https://test-binder.org/build/gh/test/test?build_token=token"
+ "https://test-binder.org/build/gh/test/test?build_token=token",
);
});
test("Get full redirect URL with correct token but no path", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
.getFullRedirectURL("https://hub.test-binder.org/user/something", "token")
- .toString()
+ .toString(),
).toBe("https://hub.test-binder.org/user/something?token=token");
});
test("Get full redirect URL with urlpath", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -61,16 +61,16 @@ test("Get full redirect URL with urlpath", () => {
"https://hub.test-binder.org/user/something",
"token",
"rstudio",
- "url"
+ "url",
)
- .toString()
+ .toString(),
).toBe("https://hub.test-binder.org/user/something/rstudio?token=token");
});
test("Get full redirect URL when opening a file with jupyterlab", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -78,16 +78,18 @@ test("Get full redirect URL when opening a file with jupyterlab", () => {
"https://hub.test-binder.org/user/something",
"token",
"index.ipynb",
- "lab"
+ "lab",
)
- .toString()
- ).toBe("https://hub.test-binder.org/user/something/doc/tree/index.ipynb?token=token");
+ .toString(),
+ ).toBe(
+ "https://hub.test-binder.org/user/something/doc/tree/index.ipynb?token=token",
+ );
});
test("Get full redirect URL when opening a file with classic notebook (with file= path)", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -95,16 +97,18 @@ test("Get full redirect URL when opening a file with classic notebook (with file
"https://hub.test-binder.org/user/something",
"token",
"index.ipynb",
- "file"
+ "file",
)
- .toString()
- ).toBe("https://hub.test-binder.org/user/something/tree/index.ipynb?token=token");
+ .toString(),
+ ).toBe(
+ "https://hub.test-binder.org/user/something/tree/index.ipynb?token=token",
+ );
});
test("Get full redirect URL and deal with excessive slashes (with pathType=url)", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -114,16 +118,16 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=url)"
"token",
// Trailing slash should be preserved here, but leading slash should not be repeated
"/rstudio/",
- "url"
+ "url",
)
- .toString()
+ .toString(),
).toBe("https://hub.test-binder.org/user/something/rstudio/?token=token");
});
test("Get full redirect URL and deal with excessive slashes (with pathType=lab)", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -132,16 +136,18 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=lab)"
"token",
// Both leading and trailing slashes should be gone here.
"/directory/index.ipynb/",
- "lab"
+ "lab",
)
- .toString()
- ).toBe("https://hub.test-binder.org/user/something/doc/tree/directory/index.ipynb?token=token");
+ .toString(),
+ ).toBe(
+ "https://hub.test-binder.org/user/something/doc/tree/directory/index.ipynb?token=token",
+ );
});
test("Get full redirect URL and deal with excessive slashes (with pathType=file)", () => {
const br = new BinderRepository(
"gh/test/test",
- new URL("https://test-binder.org/build")
+ new URL("https://test-binder.org/build"),
);
expect(
br
@@ -150,8 +156,10 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=file)
"token",
// Both leading and trailing slashes should be gone here.
"/directory/index.ipynb/",
- "file"
+ "file",
)
- .toString()
- ).toBe("https://hub.test-binder.org/user/something/tree/directory/index.ipynb?token=token");
+ .toString(),
+ ).toBe(
+ "https://hub.test-binder.org/user/something/tree/directory/index.ipynb?token=token",
+ );
});
diff --git a/webpack.config.js b/webpack.config.js
index eedab27ce..ba73e76a6 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,58 +1,58 @@
-const webpack = require('webpack');
-const path = require('path');
+const webpack = require("webpack");
+const path = require("path");
-const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
- mode: 'production',
- context: path.resolve(__dirname, 'binderhub/static'),
- entry: "./js/index.js",
- output: {
- path: path.resolve(__dirname, 'binderhub/static/dist/'),
- filename: "bundle.js",
- publicPath: '/static/dist/'
- },
- plugins: [
- new webpack.ProvidePlugin({
- $: 'jquery',
- jQuery: 'jquery',
- }),
- new MiniCssExtractPlugin({
- filename: 'styles.css'
- }),
- ],
- module: {
- rules: [
- {
- test: /\.js$/,
- exclude: /(node_modules|bower_components)/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['@babel/preset-env'],
- }
- }
- },
- {
- test: /\.css$/i,
- use: [
- {
- loader: MiniCssExtractPlugin.loader,
- options: {
- // Set publicPath as relative path ("./").
- // By default it uses the `output.publicPath` ("/static/dist/"), when it rewrites the URLs in styles.css.
- // And it causes these files unavailabe if BinderHub has a different base_url than "/".
- publicPath: "./",
- },
- },
- 'css-loader'
- ],
+ mode: "production",
+ context: path.resolve(__dirname, "binderhub/static"),
+ entry: "./js/index.js",
+ output: {
+ path: path.resolve(__dirname, "binderhub/static/dist/"),
+ filename: "bundle.js",
+ publicPath: "/static/dist/",
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ $: "jquery",
+ jQuery: "jquery",
+ }),
+ new MiniCssExtractPlugin({
+ filename: "styles.css",
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components)/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ presets: ["@babel/preset-env"],
+ },
+ },
+ },
+ {
+ test: /\.css$/i,
+ use: [
+ {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ // Set publicPath as relative path ("./").
+ // By default it uses the `output.publicPath` ("/static/dist/"), when it rewrites the URLs in styles.css.
+ // And it causes these files unavailabe if BinderHub has a different base_url than "/".
+ publicPath: "./",
},
- {
- test: /\.(eot|woff|ttf|woff2|svg)$/,
- type: 'asset/resource'
- }
- ]
- },
- devtool: 'source-map',
-}
+ },
+ "css-loader",
+ ],
+ },
+ {
+ test: /\.(eot|woff|ttf|woff2|svg)$/,
+ type: "asset/resource",
+ },
+ ],
+ },
+ devtool: "source-map",
+};