--- regenerate: true --- {% capture cache %} {% comment %} # ----------------------------------------------------------------------------- # ~/assets/themes/j1/adapter/js/nbinteract.js # Liquid template to adapt nbinteract-core JS API # # Product/Info: # https://jekyll.one # Copyright (C) 2023 Juergen Adams # # J1 Theme is licensed under the MIT License. # For details, see: https://github.com/jekyll-one-org/j1-template/blob/main/LICENSE.md # ----------------------------------------------------------------------------- # Test data: # {{ liquid_var | debug }} # ----------------------------------------------------------------------------- {% endcomment %} {% comment %} Liquid procedures -------------------------------------------------------------------------------- {% endcomment %} {% comment %} Set global settings -------------------------------------------------------------------------------- {% endcomment %} {% assign environment = site.environment %} {% assign asset_path = "/assets/themes/j1" %} {% comment %} Process YML config data ================================================================================ {% endcomment %} {% comment %} Set config files -------------------------------------------------------------------------------- {% endcomment %} {% assign template_config = site.data.j1_config %} {% assign blocks = site.data.blocks %} {% assign modules = site.data.modules %} {% comment %} Set config data -------------------------------------------------------------------------------- {% endcomment %} {% assign nbinteract_defaults = modules.defaults.nbinteract.defaults %} {% assign nbinteract_settings = modules.nbinteract.settings %} {% comment %} Set config options -------------------------------------------------------------------------------- {% endcomment %} {% assign nbinteract_options = nbinteract_defaults | merge: nbinteract_settings %} {% comment %} Detect prod mode -------------------------------------------------------------------------------- {% endcomment %} {% assign production = false %} {% if environment == 'prod' or environment == 'production' %} {% assign production = true %} {% endif %} /* # ----------------------------------------------------------------------------- # ~/assets/themes/j1/adapter/js/nbinteract.js # J1 Adapter for j1-nbinteract # # Product/Info: # https://jekyll.one # # Copyright (C) 2023 Juergen Adams # # J1 Theme is licensed under the MIT License. # For details, see: https://github.com/jekyll-one-org/j1-template/blob/main/LICENSE.md # ----------------------------------------------------------------------------- # Adapter generated: {{site.time}} # ----------------------------------------------------------------------------- */ // ----------------------------------------------------------------------------- // ESLint shimming // ----------------------------------------------------------------------------- /* eslint indent: "off" */ // ----------------------------------------------------------------------------- 'use strict'; j1.adapter.nbinteract = (function (j1, window) { {% comment %} Set global variables ------------------------------------------------------------------------------ {% endcomment %} var environment = '{{environment}}'; var moduleOptions = {}; var moduleSettings = {}; var message = {}; var flags = { checkURL: false }; var spinnerOpts = { // (default) options for a spinner lines: 13, // number of lines to draw length: 38, // length of each line width: 17, // line thickness radius: 45, // radius of the inner circle scale: 2, // scales overall size of the spinner corners: 1, // corner roundness (0..1) speed: 0.5, // rounds per second rotate: 0, // rotation offset animation: 'spinner-line-fade-more', // CSS animation name for the lines: spinner-line-fade-quick | spinner-line-shrink | spinner-line-fade-more direction: 1, // 1: clockwise, -1: counterclockwise color: '#424242', // CSS color or array of colors: orange (EF6C00) | blue (1565C0) | gray (424242) fadeColor: 'transparent', // CSS color or array of colors top: '70%', // top position relative to parent left: '50%', // left position relative to parent shadow: '0 0 1px transparent', // box-shadow for the lines zIndex: 2000000000, // z-index (defaults to 2e9) className: 'spinner', // CSS class assined to the spinner position: 'fixed', // element positioning: absolute|fixed }; var nbActions = { "resetLocalStorage": true }; var spinnerStarted = false; // switch to indicate a started spinner var nbiContentModalInfoID = 'nbiModalInfoBody'; // ID of the content (messages) for the INFO modal var nbiContentModalSuccessID = 'nbiModalSuccessBody'; // ID of the content (messages) for the SUCCESS modal var nbiContentModalErrorID = 'nbiModalErrorBody'; // ID of the content (messages) for the SUCCESS modal var nbiModalTopInfo = '#' + 'nbiModalTopInfo'; // ID of the TopInfo modal var nbiModalTRInfo = '#' + 'nbiModalTRInfo'; // ID of the TRInfo modal var nbiModalSuccessID = '#' + 'nbiModalTRSuccess'; // ID of the SUCCESS modal var nbiModalErrorID = '#' + 'nbiModalTLDanger'; // ID of the ERROR modal var nbinteract_prepared = false; // switch to indicate if ??? var nbiModalSuccessMessagesID = 'nbiModalSuccessMessages'; // UL contalner SUCCESS messages var nbiModalErrorMessagesID = 'nbiModalErrorMessages'; // UL contalner ERROR messages var nbiCellsRendered = false; // flag indicates if all widgets in page are rendered var nbiShowMessages; // switch to show NBI messages var nbiIndicateNbiActivity; // switch to show a spinner while NBI is being initialized var nbiModalAutoClose; // switch to auto-close nbi message modals var nbiModalAutoCloseDelay; // delay auto-close nbi message modals var nbiInitTimeout; // delay indicate NBI failed var nbiInitMathJax; // Load and run MathJax at runtime var textbooks; // ALL notebokks enabled var textbook; // current textbook (processed) var target; // target container for the (activity) spinner var spinner; // the (activity) spinner var nbiModal; var _this; var interact; var logger; var coreLogger; var logText; var widgetCells; var widgetCellsRendered; var nbiNotebookReady; var Events; // --------------------------------------------------------------------------- // Helper functions // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Main object // --------------------------------------------------------------------------- return { // ------------------------------------------------------------------------- // Initializer // ------------------------------------------------------------------------- init: function (options) { // ----------------------------------------------------------------------- // global event handler // ----------------------------------------------------------------------- Events = { documentReady: function (onDocumentReady) { if (document.readyState !== 'loading') { onDocumentReady(); } else { document.addEventListener('DOMContentLoaded', onDocumentReady); } } }; // ----------------------------------------------------------------------- // Default module settings // ----------------------------------------------------------------------- var settings = $.extend ({ module_name: 'j1.adapter.nbinteract', generated: '{{site.time}}' }, options); moduleOptions = $.extend({}, {{nbinteract_options | replace: 'nil', 'null' | replace: '=>', ':' }}); // ----------------------------------------------------------------------- // Global variable settings // ----------------------------------------------------------------------- _this = j1.adapter.nbinteract; logger = log4javascript.getLogger('j1.adapter.nbinteract'); nbiModalAutoClose = moduleOptions.nbi_messages_auto_close; nbiModalAutoCloseDelay = moduleOptions.nbi_messages_auto_close_delay; nbiInitTimeout = moduleOptions.nbi_init_timeout; nbiShowMessages = moduleOptions.show_nbi_messages; nbiIndicateNbiActivity = moduleOptions.indicate_nbi_activity; nbiInitMathJax = moduleOptions.nbi_init_mathjax; nbiNotebookReady = moduleOptions.notebook_ready; // ----------------------------------------------------------------------- // load|configure Mathjax // ----------------------------------------------------------------------- if (nbiInitMathJax) { _this.initMathJax(); } // ----------------------------------------------------------------------- // load|configure NBI dialog (modal) // ----------------------------------------------------------------------- _this.loadDialog(moduleOptions); // ----------------------------------------------------------------------- // load all modals (HTML portion) used by NBI // ----------------------------------------------------------------------- _this.loadNbiModals(); // ----------------------------------------------------------------------- // load all textbooks (HTML portion) configured|enabled // ----------------------------------------------------------------------- _this.loadNbiTextbooks(moduleOptions); // ----------------------------------------------------------------------- // run a spinner to indicate activity of 'nbInteract' if enabled // ----------------------------------------------------------------------- $(document).ready(function() { if (nbiIndicateNbiActivity && !spinnerStarted) { spinnerStarted = true; target = document.getElementById('content'); spinner = new Spinner(spinnerOpts).spin(target); } }); // ----------------------------------------------------------------------- // register callbacks (actions) for all modals used // ----------------------------------------------------------------------- if (nbiShowMessages) { _this.registerNbiModalsCB(); } // ----------------------------------------------------------------------- // interactNbiTextbooks() // connect to the configured BinderHub instance to create a // Jupyter kernel if required // ----------------------------------------------------------------------- _this.interactNbiTextbooks(moduleOptions); // toggle hide|show the FAB button, if to wait on 'last_widget' rendered // if (nbiNotebookReady == 'last_widget') { var dependencies_met_page_rendered = setInterval(function() { if (nbiCellsRendered) { $('.fab-btn').show(); clearInterval(dependencies_met_page_rendered); } else { $('.fab-btn').hide(); } }, 25); // END interval dependencies_met_page_rendered } }, // END init // ------------------------------------------------------------------------- // initMathJax() // load|configure MathJax at runtime // See: https://docs.mathjax.org/en/v2.7-latest/options/preprocessors/tex2jax.html // ------------------------------------------------------------------------- initMathJax: function () { var scriptMathjax = document.createElement('script'); var scriptMathjaxConfig = document.createElement('script'); // Loading MathJax from a CDN // scriptMathjax.setAttribute('src','//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-AMS_HTML'); // create/load MathJax configuration settings // scriptMathjaxConfig.setAttribute('type','text/x-mathjax-config'); scriptMathjaxConfig.innerHTML = ` MathJax.Hub.Config({ tex2jax: { // inlineMath, displayMath // --------------------------------------------------------------- // TeX 'delimiters'. By default, the tex2jax preprocessor // definesthe LaTeX math delimiters, which are \(...\) for // 'in-line' math, and \[...\] for 'displayed' equations. // It also defines the TeX delimiters $$...$$ for displayed // equations, but it does not define $...$ as in-line math // delimiters. That is because dollar signs appear too often // in non-mathematical settings, which could cause some text // to be treated as mathematics unexpectedly. For example, // with single-dollar delimiters, 'the cost is $2.50' for the // first one, and '$2.00' for each additional one would cause // the phrase '2.50 for the first one' to be treated as // mathematics since it falls between dollar signs. For this // reason, if you want to use single-dollars for in-line math // mode, you must enable that explicitly. // inlineMath: [ ['$','$'], ["\\(","\\) "] ], displayMath: [ ['$$','$$'], ["\\[","\\]"] ], // processEscapes // --------------------------------------------------------------- // When 'processEscapes' set to true, you may use \$ to // representa literal dollar sign, rather than using it as // a math delimiter. When false, \$ will not be altered, // and the dollar sign may be considered part of a math // delimiter. Typically this is set to 'true' if you enable // the $ ... $ in-line delimiters, so you can type \$ and // tex2jax will convert it to a regular dollar sign in the // rendered document. // processEscapes: true, // processEnvironments // --------------------------------------------------------------- // When 'processEnvironments' true, tex2jax looks not only // for the in-line and display math delimiters, but also for // LaTeX environments (\begin{something}...\end{something}) // and marks them forprocessing by MathJax. When false, LaTeX // environments will not be processed outside of math mode. // processEnvironments: false, // skipTags // --------------------------------------------------------------- // The array 'skipTags' lists the names of the tags whose // contents should NOT be processed by tex2jax. // skipTags: ["script","noscript","style","textarea","pre","code"], // processClass, ignoreClass // --------------------------------------------------------------- // These are the (CSS) classes name used to mark elements whose // contents should or should NOT be processed by tex2jax. // processClass: "mathjax", ignoreClass: "nomathjax" }, // displayAlign // ----------------------------------------------------------------- // Align (displayMath) equations left-sided in code and all // markdown cells. // displayAlign: 'left', // preview // ----------------------------------------------------------------- // Controls whether tex2jax inserts 'MathJax_Preview' spans to // make a preview available, and what preview to use, when it // locates in-line or display mathematics in the page. The default // is 'TeX', which means use the TeX code as the preview (which // will be visible until it is processed by MathJax). Set to // 'none' to prevent previews from being inserted. // preview: "TeX", // CommonHTML, HTML-CSS, and SVG output processors // ----------------------------------------------------------------- // The CommonHTML, HTML-CSS, and SVG output processors implement // most of the MathML3 automatic line-breaking specification. // Since line-breaking takes extra processing and so can slow // down the mathematical output, it is off by default // CommonHTML: { linebreaks: { automatic: false } }, "HTML-CSS": { linebreaks: { automatic: false } }, SVG: { linebreaks: { automatic: false } } }); // Use Jax renderer 'CommonHTML' for default // MathJax.Hub.Register.StartupHook("End Jax",function () { var BROWSER = MathJax.Hub.Browser; var jax = "CommonHTML"; if (BROWSER.hasMathPlayer) jax = "NativeMML"; return MathJax.Hub.setRenderer(jax); }); // Register a message MessageHook // --------------------------------------------------------------------- // MathJax.Hub.Register.MessageHook("New Math", function (message) { // var script = MathJax.Hub.getJaxFor(message[1]).SourceElement(); // var j1_message = {}; // j1_message.type = 'command'; // j1_message.action = 'mathjax'; // j1_message.text = message[1]; // this.j1.sendMessage('MathJax.Hub.MessageHook', 'j1.adapter.nbinteract', j1_message); // // console.error('nbinteract, Hub - New Math: ' + message[1]); // }); // --------------------------------------------------------------------- // END config `; // add Mathjax resources // document.head.appendChild(scriptMathjax); document.head.appendChild(scriptMathjaxConfig); return; }, // END intMathjax // ------------------------------------------------------------------------- // loadNbiTextbooks() // load the HTML portion for all textbooks configured (enabled) // ------------------------------------------------------------------------- loadNbiTextbooks: function (settings) { var textbooks = settings.textbooks; textbooks.forEach (function (elm) { if (elm.textbook.enabled) { textbook = elm.textbook; var textbook_id = textbook.id; var $selector = $('#' + textbook_id); // load the HTML portion for the textbook // if ($selector.length) { _this.loadTextbookHTML ({ xhr_container_id: textbook.id, xhr_data: textbook.xhr_data, xhr_data_path: textbook.xhr_data_path, use_mathjax: textbook.use_mathjax, translate_mathjax: textbook.translate_mathjax, altair_cleanups: textbook.altair_cleanups, buttonStyles: settings.button_styles, }); } } }); }, // ------------------------------------------------------------------------- // interactNbiTextbooks() // connect to the configured BinderHub instance to create a // Jupyter kernel if required. A BinderHub instance in created // on a per textbook basis but trigeered only done once, // controlled by nbinteract_prepared. // ------------------------------------------------------------------------- interactNbiTextbooks: function (options) { var state; // collect (preferred) Binder references // var preferredBaseUrl = options.baseUrls.preferred; var myBaseUrl = options.baseUrls[preferredBaseUrl]; var preferredProvider = options.providers.preferred; var myProviderId = options.providers[preferredProvider].provider_id; var myProviderSpec = options.providers[preferredProvider].provider_spec; // initialize state flag _this.setState('started'); logger.debug('\n' + 'state: ' + _this.getState()); // check if the Binder Service is available // _this.checkURL(myBaseUrl, flags); var log_text = '\n' + 'nbinteract is being initialized'; logger.info(log_text); var dependencies_met_binder_responsive = setInterval(function() { state = _this.getState(); if (state == 'finished_checks') { clearInterval(dependencies_met_binder_responsive); if (flags.checkURL) { {% comment %} initialize nbinteract per textbook {% assign textbook_spec = item.textbook.spec %} {% assign textbook_baseUrl = item.textbook.baseUrl %} {% assign textbook_provider = item.textbook.provider %} -------------------------------------------------------------------------- {% endcomment %} {% for item in nbinteract_options.textbooks %} {% if item.textbook.enabled %} {% assign textbook_id = item.textbook.id %} if ($('#{{textbook_id}}').length) { var dependencies_met_nb_loaded = setInterval(function() { if ($('#{{textbook_id}}').attr('data-nb-textbook') == 'loaded') { var nbiButtonsFound = document.querySelectorAll('.js-nbinteract-widget').length if (nbiIndicateNbiActivity && nbiButtonsFound == 1) { var log_text = '\n' + 'localized textbook found, skip NBI initialization for: {{textbook_id}}'; logger.warn(log_text); spinner.stop(); } if(!nbinteract_prepared && nbiButtonsFound > 1) { logText = '\n' + 'jupyter kernel is being generated ...'; logger.info(logText); // create nbInteract (core) instance // coreLogger = log4javascript.getLogger('j1.nbinteract-core'); interact = new NbInteract({ baseUrl: myBaseUrl, provider: myProviderId, spec: myProviderSpec, logger: coreLogger, j1API: j1, }); // generate a jupyter kernel via BinderHub interact.prepare(); nbinteract_prepared = true; // issue an error if the NBI (init) button never removed by // nbInteract-core (util or manager) // TODO: The 'timeout' condition should be replaced // state-based triggered from nbinteract-core. // window.setTimeout(function() { var nbiButtonState = _this.getNbiButtonState(); if (nbiButtonState) { // button NOT removed logger.warn('NBI initialialization failed on textbook: {{textbook_id}}'); spinner.stop(); // hide the info modal $(nbiModalSuccessID).modal('hide'); // show the error modal $(nbiModalSuccessID).on('hidden.bs.modal', function () { if ($(nbiModalErrorID).is(':hidden')) { var messageErrorUL = document.getElementById(nbiModalErrorMessagesID); _this.appendModalMessage(messageErrorUL, 'NBI initialialization failed for textbook: {{textbook_id}}') $(nbiModalErrorID).modal('show'); // auto-close the error modal if (nbiModalAutoClose) { window.setTimeout(function() { $(nbiModalErrorID).modal('hide'); }, nbiModalAutoCloseDelay); } } }); } else { // button removed logger.info('NBI initialized successfully.'); } }, nbiInitTimeout); } clearInterval(dependencies_met_nb_loaded); } // END dependencies_met_nb_loaded }, 25); return; } // END textbook_id: {{ textbook_id }} {% endif %} {% endfor %} } else { spinner.stop(); var modaBodyText = ` The Binder Service is currently not available or is overloaded. All interactive components on the page are not available. You can reload the page or re-open later again. `; logger.error('\n', 'Binder access: failed'); if ($(nbiModalTRInfo).is(':hidden')) { document.getElementById('nbiModalTRInfoBody').innerHTML = modaBodyText; $(nbiModalTRInfo).modal('show'); } clearInterval(dependencies_met_binder_responsive); } } }, 25); // END dependencies_met_binder_responsive }, // ------------------------------------------------------------------------- // loadTextbookHTML() // Load HTML data asychronously using XHR|jQuery on an element // (e.g.
) specified by xhr_container_id, xhr_data_path // ------------------------------------------------------------------------- loadTextbookHTML: function (options) { var html_data_path = options.xhr_data_path + '/' + options.xhr_data; var id = options.xhr_container_id; var mathjaxEnabled = options.use_mathjax ? options.use_mathjax : false; var mathjaxTranslate = options.translate_mathjax ? options.translate_mathjax : false; var cleanupAltair = options.altair_cleanups ? options.altair_cleanups : false; var isPageRendered = false; var $selector = $('#' + id); var allID; var allMathJaxSpan; var allMathJaxElement; var logText; var cb_load_closure = function(id, mathjaxFlag) { return function (responseTxt, statusTxt, xhr) { var logger = log4javascript.getLogger('j1.adapter.loadHTML'); if (statusTxt === 'success') { j1.setXhrDataState(id, statusTxt); j1.setXhrDomState(id, 'pending'); // set data attribute to indicate HTML data loaded // $selector.attr('data-nb-textbook', 'loaded'); // run HTML cleanups // $selector.find('button').replaceWith( function() { return ''; }); // enable MathJax for the (current) J1 Textbook container // processed if enabled for the (containing) textbook // var currentTextbook = document.getElementById(id); if (mathjaxFlag) { currentTextbook.classList.add('mathjax'); } else { currentTextbook.classList.add('nomathjax'); } // ------------------------------------------------------------------ // see: https://www.codingexercises.com/replace-all-instances-of-css-class-in-vanilla-js // see: https://wiki.thishtml.org/wiki/JavaScript/Operatoren/Rest-_oder_Spread-Operator // ------------------------------------------------------------------ // disable (Google) translation for all HTML 'output_wrapper' elements // var output_wrapper = document.getElementsByClassName('output_wrapper'); [...output_wrapper].forEach(function(x) { if (!x.className.includes('notranslate')) { x.className += ' notranslate'; } }); // make all output_wrapper cells "responsive" (especially for tables) // var output_wrapper = document.getElementsByClassName('output_wrapper'); [...output_wrapper].forEach(function(x) { if (!x.className.includes('cell-responsive')) { x.className += ' cell-responsive'; } }); // cleanups for Altair for all HTML 'output_wrapper' elements // if (cleanupAltair) { var reUnderscores = new RegExp(/_/, 'g'); var reMultipleSpaces = new RegExp(/\s+/, 'g'); var reMultipleSpacesStart = new RegExp(/^\s+/, 'g'); var reMultipleSpacesEnd = new RegExp(/\s+$/, 'g'); var reSkipWords = new RegExp(/vgsid|bla/, 'g'); var reDuplicateWords = new RegExp(/(\b\S.+\b)(?=.*\1)/, 'g'); var content; var newContent; var newContentWritten = false; var isWidget; var outputDiv; var childNodes; var clientHeight; var lastWidget = false; // check/wait for ALL Altair widgets in the page are rendered (by JS) // if rendering finished, flag 'isPageRendered' is set to 'true' // allID = document.querySelectorAll('*[id^="altair-viz"]'); for (var l = 0; l < allID.length; l++) { outputDiv = document.getElementById(allID[l].id); var dependencies_met_page_rendered = setInterval(function() { lastWidget = (l == allID.length) ? true : false; if (outputDiv.clientHeight && lastWidget && !isPageRendered) { isPageRendered = true; logger.debug('\n' + 'last widget rendered ' + outputDiv.id + ' : ' + outputDiv.clientHeight); clearInterval(dependencies_met_page_rendered); } }, 25); // END interval } // END for all ID var dependencies_met_widgets_updated = setInterval(function() { if (isPageRendered) { for (var item of allID) { outputDiv = document.getElementById(item.id); logger.debug('\n' + 'processing widget on id: ' + item.id); childNodes = outputDiv.getElementsByClassName('vega-bind-name'); if (childNodes.length) { for (var i = 0; i < childNodes.length; i++) { content = childNodes[i].innerHTML; newContent = content.replace(reUnderscores, ' '); newContent = newContent.replace(reDuplicateWords, ''); newContent = newContent.replace(reSkipWords, ''); newContent = newContent.replace(reMultipleSpaces, ' '); newContent = newContent.replace(reMultipleSpacesStart, ''); newContent = newContent.replace(reMultipleSpacesEnd, ''); childNodes[i].innerHTML = newContent; newContentWritten = true; } // END for } // END if childNodes.length } // END for logger.debug('\n' + 'all widgets updated'); clearInterval(dependencies_met_widgets_updated); } }, 25); // END interval } // END if cleanup Altair // disable MathJax for all HTML 'output_wrapper' elements // [...output_wrapper].forEach(function(x) { if (x.className.includes('nomathjax')) { x.className += ' nomathjax'; } }); // make all 'image' elements responsive (BS@5) // var images = document.getElementsByTagName('img');; [...images].forEach(function(x) { if (!x.className.includes('img-fluid')) { x.className += ' img-fluid'; } }); // disable for now // mathjaxTranslate = true // disable translation on MathJax containers // ----------------------------------------------------------------- if (mathjaxEnabled && !mathjaxTranslate) { var dependencies_met_mathjax_available = setInterval(function() { // document.getElementsByClassName('MJXc-display'); // document.querySelectorAll('*[id^="MathJax-Element"]'); // allMathJaxSpan = document.querySelectorAll('*[id^="MathJax-Element"]'); // allMathJaxSpan = document.querySelectorAll('*[id^="MathJax-Element-"]'); // allMathJaxSpan = document.getElementsByClassName('mjx-chtml'); allMathJaxSpan = document.querySelectorAll('*[id$="-Frame"]'); if (allMathJaxSpan.length) { // var lastElm = document.getElementById(allMathJaxSpan[allMathJaxSpan.length-1].id); var dependencies_met_mathjax_rendered = setInterval(function() { // var isProcessing = lastElm.classList.contains('MathJax_Processing'); var mj_state = j1.adapter.nbinteract.getState() if (mj_state == 'mathjax_finished') { allMathJaxSpan = document.querySelectorAll('*[id$="-Frame"]'); for (var l = 0; l < allMathJaxSpan.length; l++) { // outputDiv = document.getElementById(allMathJaxSpan[l].id); outputDiv = allMathJaxSpan[l]; logger.error('\n' + 'element: ' + outputDiv.id ); // outputDiv.classList.remove('MJXc-processing'); outputDiv.classList.add('notranslate'); } clearInterval(dependencies_met_mathjax_rendered); } }, 25); // END interval dependencies_met_mathjax_rendered clearInterval(dependencies_met_mathjax_available); }; }, 25); // END interval dependencies_met_mathjax_available } // END // disable translation on MathJax // rewrite headlines in textbook HTML, add an id to be used by toccer // $selector.find('h1').replaceWith( function() { // return '

' + $(this).text().slice(0,-1) + '

'; return '

' + $(this).text().slice(0,-1) + '

'; }); $selector.find('h2').replaceWith( function() {   return '

' + $(this).text().slice(0,-1) + '

'; }); $selector.find('h3').replaceWith( function() {   return '

' + $(this).text().slice(0,-1) + '

'; }); $selector.find('h4').replaceWith( function() {   return '

' + $(this).text().slice(0,-1) + '

'; }); $selector.find('h5').replaceWith( function() {   return '
' + $(this).text().slice(0,-1) + '
'; }); logText = '\n' + 'data loaded successfully on id: ' + id; logger.info(logText); } if (statusTxt === 'error') { // jadams, 2020-07-21: to be checked why id could be UNDEFINED if (typeof(id) != "undefined") { var state = 'failed'; if (nbiIndicateNbiActivity) { spinner.stop(); } // logger.info('\n' + 'set state for ' + mod + ' to: ' + state); // jadams, 2020-07-21: intermediate state should DISABLED // executeFunctionByName(mod + '.setState', window, state); j1.setXhrDataState(id, statusTxt); j1.setXhrDomState(id, 'pending'); logText = '\n' + 'loading data failed on id: ' +id+ ', error ' + xhr.status + ': ' + xhr.statusText; logger.error(logText); state = false; } } }; }; // failsafe - prevent XHR load errors // if (options.xhr_data !== '') { logger.info('\n' + 'HTML data file found: ' + options.xhr_data); } else { logger.warning('\n' + 'no HTML data file found, loading data aborted'); return; } if ($selector.length) { $selector.load( html_data_path, cb_load_closure(id, mathjaxEnabled)); } return; }, // ------------------------------------------------------------------------- // registerNbiModalsCB() // regsiter callbacks for all (NBI) modals used // ------------------------------------------------------------------------- registerNbiModalsCB: function () { // auto-scroll to the END of the SUCCESS messages // see: https://stackoverflow.com/questions/7303948/how-to-auto-scroll-to-end-of-div-when-data-is-added // window.setInterval(function() { var contentModalSuccess = document.getElementById(nbiContentModalSuccessID); contentModalSuccess.scrollTop = contentModalSuccess.scrollHeight; }, 500); // remove all INFO messages on modal CLOSED // $('#nbiModalTopInfo').on('hidden.bs.modal', function () { var ul = document.getElementById(nbiModalInfoMessagesID); var listLength = ul.children.length; if (listLength) { logger.debug('modal closed nbiModalTopInfo: remove all messages'); _this.removeModalMessages(nbiModalInfoMessagesID); } }); // remove all SUCCESS messages on modal CLOSED // $('#nbiModalTRSuccess').on('hidden.bs.modal', function () { var ul = document.getElementById(nbiModalSuccessMessagesID); var listLength = ul.children.length; if (listLength) { logger.debug('modal closed nbiModalTRSuccess: remove all messages'); _this.removeModalMessages(nbiModalSuccessMessagesID); } }); // remove all ERROR messages on modal CLOSED // $('#nbiModalTLDanger').on('hidden.bs.modal', function () { var ul = document.getElementById(nbiModalErrorMessagesID); var listLength = ul.children.length; if (listLength) { logger.debug('modal closed nbiModalTLDanger: remove all messages'); _this.removeModalMessages(nbiModalErrorMessagesID); } if (nbiIndicateNbiActivity) { spinner.stop(); } }); }, // ------------------------------------------------------------------------- // loadNbiModals() // Load HTML data for all (NBI) modals used // ------------------------------------------------------------------------- loadNbiModals: function () { const nbiModalTopInfo = ` ` const nbiModalTRInfo = ` ` const nbiModalTRSuccess = ` ` const nbiModalTLDanger = ` ` nbiModal = document.createElement('div'); nbiModal.id = 'nbi-modal-info'; nbiModal.className = 'nbi-modal'; nbiModal.innerHTML = nbiModalTopInfo; document.body.appendChild(nbiModal); nbiModal = document.createElement('div'); nbiModal.id = 'nbi-modal-info'; nbiModal.className = 'nbi-modal'; nbiModal.innerHTML = nbiModalTRInfo; document.body.appendChild(nbiModal); nbiModal = document.createElement('div'); nbiModal.id = 'nbi-modal-success'; nbiModal.className = 'nbi-modal'; nbiModal.innerHTML = nbiModalTRSuccess; document.body.appendChild(nbiModal); nbiModal = document.createElement('div'); nbiModal.id = 'nbi-modal-danger'; nbiModal.className = 'nbi-modal'; nbiModal.innerHTML = nbiModalTLDanger; document.body.appendChild(nbiModal); }, // ------------------------------------------------------------------------- // messageHandler() // Manage messages send from other J1 modules // ------------------------------------------------------------------------- messageHandler: function (sender, message) { var json_message = JSON.stringify(message, undefined, 2); var messageSuccessUL = document.getElementById(nbiModalSuccessMessagesID); var messageErrorUL = document.getElementById(nbiModalErrorMessagesID); var isUpdated = false; var message; logText = '\n' + 'received message from ' + sender + ': ' + json_message; logger.debug(logText); // ----------------------------------------------------------------------- // Process commands|actions // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- // command|nbi_init_started // ----------------------------------------------------------------------- if (message.type === 'command' && message.action === 'nbi_init_started') { if (nbiShowMessages) { if (nbiModalAutoClose) { window.setTimeout(function() { $(nbiModalSuccessID).modal('hide'); }, nbiModalAutoCloseDelay); } } if (nbiNotebookReady == 'first_widget') { if (nbiIndicateNbiActivity) spinner.stop(); $('.fab-btn').show(); } widgetCells = document.querySelectorAll('.output_widget_view').length; var dependencies_met_page_rendered = setInterval(function() { widgetCellsRendered = document.querySelectorAll('.widget-vbox').length; if (widgetCellsRendered >= widgetCells) { logger.info('\n' + 'widgets rendered in page (interactive|total) : ' + widgetCells + '|' + widgetCellsRendered); nbiCellsRendered = true; if (nbiIndicateNbiActivity) spinner.stop(); _this.setState('finished'); logger.debug('\n' + 'state: ' + _this.getState()); logger.info('\n' + 'initializing module finished'); clearInterval(dependencies_met_page_rendered); } }, 25); // END interval dependencies_met_page_rendered // --------------------------------------------------------------------- // show the quicklinks icon // --------------------------------------------------------------------- $('#quickLinksNotebookseButton').show(); } // END message command/nbi_init_started // ----------------------------------------------------------------------- // command|mathjax // ----------------------------------------------------------------------- if (message.type === 'command' && message.action === 'mathjax') { logger.error('\n' + 'New Math, ID: ' + message.text); // Register a MathJax callback if page is FULLY rendered // TODO: Dosn't for now tha way !!! // MathJax.Hub.Startup.signal.Interest(function (message) { // logger.error("Startup: " + message) // if (message.contains('End')) { // logger.error("Startup: " + message) // } // }); var dependencies_met_mathjax_rendered = setInterval(function() { var elm = document.getElementById('MathJax-Element-6' + '-Frame'); var isProcessing = (elm.classList.contains('MJXc-processing') || elm.classList.contains('MJXc-processed')) ? true : false; if (!isProcessing && !isUpdated) { isUpdated = true; elm.classList.add('notranslate'); logger.error('\n' + 'New Math, ID: ' + message.text); //clearInterval(dependencies_met_mathjax_rendered); } clearInterval(dependencies_met_mathjax_rendered); }, 25); // END interval dependencies_met_mathjax_rendered } // END message command/mathjax // ----------------------------------------------------------------------- // command|info // TODO: count messages contain 'Pulling image'. // Potentially a enless loop // ----------------------------------------------------------------------- if (message.type === 'command' && message.action === 'info') { // var reMessageTS = new RegExp('/(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,9})?(?:Z|[+-][01]\d:[0-5]\d)/'); var messageTS; // widget render info messages for logging // if (message.text.includes('Displaying widget') || message.text.includes('First widget')) { logger.debug('\n' + message.text); } // remove timestamp|loglevel from message if exists // messageTS = message.text.split('] ')[1]; if (messageTS) { message.text = messageTS; } _this.appendModalMessage(messageSuccessUL, message.text) logger.debug('\n' + message.text); // show the info modal // if (nbiShowMessages) { if ($(nbiModalSuccessID).is(':hidden')) { $(nbiModalSuccessID).modal('show'); } } } // END message command/info // ----------------------------------------------------------------------- // command|error // ----------------------------------------------------------------------- if (message.type === 'command' && message.action === 'error') { var messageTS; if (message.text.includes('Too many users') || message.text.includes('Insufficent nodes') || message.text.includes('ImagePullBackOff') || message.text.includes('kernel failed') || message.text.includes('failed to connect') ) { var modaBodyText = ` The Binder Service seems currently not available or is overloaded. All interactive components on the page are not available. You can reload the page now to re-connect or re-open it at a later time. `; logger.error('\n', 'Binder access failed: ' + message.text); if ($(nbiModalTRInfo).is(':hidden')) { document.getElementById('nbiModalTRInfoBody').innerHTML = modaBodyText; $(nbiModalTRInfo).modal('show'); } return; } // remove timestamp|loglevel from message if exists // messageTS = message.text.split('] ')[1]; if (messageTS) { message.text = messageTS; } _this.appendModalMessage(messageErrorUL, message.text) logger.error('\n' + message.text); // stop the (progress) spinner // if (moduleOptions.indicate_nbi_activity) { spinner.stop(); } if (nbiShowMessages) { // hide the info modal if shown // $(nbiModalSuccessID).on('shown.bs.modal', function () { $(nbiModalSuccessID).modal('hide'); }); // hide the info modal uncondionally (need ???) // $(nbiModalSuccessID).modal('hide'); // show the error modal // $(nbiModalSuccessID).on('hidden.bs.modal', function () { if ($(nbiModalErrorID).is(':hidden')) { $(nbiModalErrorID).modal('show'); // auto-close the error modal // if (nbiModalAutoClose) { window.setTimeout(function() { $(nbiModalErrorID).modal('hide'); }, nbiModalAutoCloseDelay); } } }); } if (nbiIndicateNbiActivity) { spinner.stop(); } } // END message command/error return true; }, // END messageHandler // ------------------------------------------------------------------------- // getNbiButtonsState() // Gets the current (processing) state of NBI widget buttons // ------------------------------------------------------------------------- getNbiButtonState: function () { var state; var cellButtons = document.querySelectorAll('.js-nbinteract-widget') if (cellButtons.length) { state = true; } else { state = false; } return state; }, // END getNbiButtonsState // ------------------------------------------------------------------------- // setState() // Sets the current (processing) state of the module // ------------------------------------------------------------------------- setState: function (stat) { _this.state = stat; }, // END setState // ------------------------------------------------------------------------- // getState() // Returns the current (processing) state of the module // ------------------------------------------------------------------------- getState: function () { return _this.state; }, // END getState // ------------------------------------------------------------------------- // checkURL() // Returns the current (processing) state of the module // ------------------------------------------------------------------------- checkURL: function (uri, flags) { _this.setState('process_checks'); if (uri.includes('localhost')) { _this.setState('finished_checks'); flags.checkURL = true; return true; } else { $.get(uri).done(function (e) { _this.setState('finished_checks'); flags.checkURL = true; return true; }).fail(function (e) { _this.setState('finished_checks'); flags.checkURL = false; }); } }, // END checkURL // ------------------------------------------------------------------------- // loadDialog() // Loads the NBI dialog (modal) // ------------------------------------------------------------------------- loadDialog: function (options) { Events.documentReady(function () { _this.modal = document.getElementById(options.dialogContainerID); if (!_this.modal) { logger.info('\n' + 'load consent modal'); _this.modal = document.createElement('div'); _this.modal.id = options.dialogContainerID; _this.modal.style.display = 'none'; _this.modal.setAttribute('class', 'modal fade'); _this.modal.setAttribute('tabindex', '-1'); _this.modal.setAttribute('role', 'dialog'); _this.modal.setAttribute('aria-labelledby', options.dialogContainerID); document.body.append(_this.modal); _this.$modal = $(_this.modal); // ------------------------------------------------------------------- // load|initialize the dialog (modal content) // ------------------------------------------------------------------- var templateUrl = options.contentURL + '/' + 'index.html'; $.get(templateUrl) .done(function (data) { // load ALL modals HTML _this.modal.innerHTML = data; // select only the requested modal _this.modal.innerHTML = $('#' + options.xhrDataElement).eq(0).html(); // set dialog type to 'modal' // $(_this.modal).modal({ backdrop: 'static', keyboard: false }); // register all button links // _this.$buttonDoNotAgree = $('#nbi-buttonDoNotAgree'); _this.$buttonAgree = $('#nbi-buttonAgree'); _this.$buttonSave = $('#nbi-buttonSave'); _this.$buttonAgreeAll = $('#nbi-buttonAgreeAll'); // register all actions // _this.registerActions(); // register button event handler // _this.$buttonDoNotAgree.click(function () { _this.doNotAgree(); }); _this.$buttonAgree.click(function () { _this.agreeAll(); }); }) .fail(function () { logger.error('\n' + 'loading nbi dialog (modal): failed'); logger.warn('\n' + 'probably no|wrong `contentURL` set'); }); } // --------------------------------------------------------------------- // register events for the dialog (modal) // --------------------------------------------------------------------- // --------------------------------------------------------------------- // on 'show' // --------------------------------------------------------------------- // _this.$modal.on('show.bs.modal', function () { // // hide the menubar for the modal header // $('#navigator_nav_navbar').hide(); // }); // END modal on 'show' // --------------------------------------------------------------------- // on 'hidden' // --------------------------------------------------------------------- // _this.$modal.on('hidden.bs.modal', function () { // // if the modal is closed, show the menubar // $('#navigator_nav_navbar').show(); // }); // END modal on 'hidden' }.bind(_this)); }, // END loadDialog // ------------------------------------------------------------------------- // showDialog() // Show the NBI dialog (modal) // ------------------------------------------------------------------------- showDialog: function () { this.$modal.modal('show'); }, // END showDialog // ------------------------------------------------------------------------- // registerActions() // register actions to run // ------------------------------------------------------------------------- registerActions: function () { $('input:checkbox[name="checkboxClearLocalStorage"]').on('click', function (e) { nbActions.resetLocalStorage = $(this).is(':checked'); logText = '\n' + 'action ClearLocalStorage changed to: ' + value; logger.info(logText); e.stopPropagation(); }); }, // END registerActions // ------------------------------------------------------------------------- // doNotAgree() // action to run ... // ------------------------------------------------------------------------- doNotAgree: function (elmID, msg) { _this.$modal.modal('hide'); }, // END doNotAgree // ------------------------------------------------------------------------- // agreeAll() // caction to run ... // ------------------------------------------------------------------------- agreeAll: function (elmID, msg) { if (nbActions.resetLocalStorage) { logText = '\n' + 'run action: "Clear Binder Settings"'; logger.info(logText); localStorage.removeItem('serverParams'); localStorage.removeItem('kernelId'); } _this.$modal.modal('hide'); location.reload(true); }, // END agreeAll // ------------------------------------------------------------------------- // appendModalMessage() // Appends a message to given (NBI) modal // ------------------------------------------------------------------------- appendModalMessage: function (elmID, msg) { var li = document.createElement('li'); li.setAttribute('class','item'); elmID.appendChild(li); li.innerHTML = li.innerHTML + msg; }, // END appendModalMessage // ------------------------------------------------------------------------- // removeModalMessages() // Remove (clear) all modal messages if a given (NBI) modal // ------------------------------------------------------------------------- removeModalMessages: function (elmID) { var ul = document.getElementById(elmID); var listLength = ul.children.length; if (listLength) { for (var i = 0; i < listLength; i++) { ul.removeChild(ul.children[0]); } } } // END removeModalMessages }; // END return })(j1, window); {% endcapture %} {% if production %} {{ cache | minifyJS }} {% else %} {{ cache | strip_empty_lines }} {% endif %} {% assign cache = nil %}