ijs

Simple library to support working within the iJavaScript kernel within Jupyter

Note that this is available as ijs from within the jupyter-ijavascript-utils module

  • Asynchronous Methods
    • ijs.await - Helper function to support Await / Async functions in iJavaScript
    • ijs.asyncConsole - Utility function for consoling a value in a .then() clause
    • ijs.asyncWait - Utility function for waiting x seconds between promise resolutions
  • iJavaScript Context Detection
  • Introspection
  • Rendering
  • Printing
  • using a cache for long running executions
    • ijs.useCache() - perform an expensive calculation and write to a cache, or read from the cache transparently

For example:

//-- get the data
//-- fetch the data
//-- and do not execute the next cell until received.
utils.ijs.await(async ($$, console) => {
 barley = await utils.datasets.fetch('barley.json');
})
//-- use the data as though it was synchronously received

//-- get the min max of the types of barley
barleyByVarietySite = d3.group(barley, d => d.variety, d => d.site)
//-- now group by variety and year
barleyByVarietyYear = d3.group(barley, d => d.variety, d => d.year)

then later

utils.ijs.listGlobals();
// ['barley','d3','barleyByVariety','barleyByVarietySite',...]

Or passing NodeJS variables to JavaScript

(See ijs.htmlScript for more)

utils.ijs.htmlScript({
    scripts: ['https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js'],
    height: '100%',
    onReady: ({rootEl}) => {
        new QRCode(rootEl, "https://jupyter-ijavascript-utils.onrender.com/");
    }
});

Screenshot of QR Code

Methods

(static) asyncConsole(message) → {function}

Simple promise chain for sending a console message

Example
Promise.resolve(200)
 .then(utils.ijs.asyncWait(2))
 .then(utils.ijs.asyncConsole('after waiting for 2 seconds'))
 .then((results) => console.log('results passed through: ${results}`));

//--
after waiting for 2 seconds
results passed through: 200
Parameters:
Name Type Description
message String

the message to be sent to console

Returns:
  • (results) => results passthrough
Type
function

(static) asyncWait(seconds) → {function}

Simple promise chain for waiting N seconds.

Example
Promise.resolve(200)
 .then(utils.ijs.asyncConsole('before waiting for 2 seconds'))
 .then(utils.ijs.asyncWait(2))
 .then(utils.ijs.asyncConsole('after waiting for 2 seconds'))
 .then((results) => console.log('results passed through: ${results}`));

//--
before waiting for 2 seconds
after waiting for 2 seconds
results passed through: 200
Parameters:
Name Type Description
seconds Number

the number of seconds to wait

Returns:
  • (results) => results passthrough
Type
function

(static) await()

Allow for asynchronous programming within iJavaScript nodes.

Screenshot

Example
utils.ijs.await(async ($$, console) => {
   //-- $$ is the display for the current cell
   //-- console is the console for the current cell
   
   barley = await utils.datasets.fetch('barley.json');
   
   console.log(`retrieved:${barley.length} nodes`);

   // return will be the value sent to $$.sendResults()
   // and shown alongside console
   return barley.slice(0,1)
})

//-- outputs

retrieved:120 nodes

[
 {
   yield: 27,
   variety: 'Manchuria',
   year: 1931,
   site: 'University Farm'
 }
]

(static) clearOutput()

Clears the output.

(This is useful to put after importing libraries, or defining a list of functions)

(static) detectContext() → {IJavaScriptContext}

Determines the current global display and console from iJavaScript (or null if not within iJavaScript)

Returns:
  • ({ display, console }) or null if not within iJavaScript
Type
IJavaScriptContext

(static) detectIJS() → {Boolean}

Determines if we are currently within the iJavaScript context

Returns:
  • true if the code is running within an iJavaScript kernel
Type
Boolean

(static) generatePageBreakHTML() → {String}

See:

Generates the html used for creating a page break used by the library.

This gives you options about when and how to render it.

utils.ijs.generatePageBreakHTML();
//-- uses the style defined in initializePageBreaks
// <div class="pagebreak"></div>

For example, you can use this so you automatically have a page break after generating the styles

utils.ijs.initializePageBreaks(null, utils.ijs.generatePageBreakHTML());
Returns:
Type
String

(static) generatePageBreakStylesHTML() → {String}

Returns the HTML used for generating pageBreaks within the library.

This gives you control over rendering the text together

utils.ijs.generatePageBreakStylesHTML();
// <style>

//-- an identifier that can be used to find if this script exists on the page
\/\* ID:___InitializePageBreaks___ \*\/

// @media print {
// .pagebreak { page-break-before: always; }
// }

// </style>
Returns:
  • the html styles tag used to allow for page breaks to be generated
Type
String

(static) htmlScript(options)

Generates and renders an html block that loads external css and javascript.

For example, allows running browser side d3js in a Jupyter cell, Leaflet, etc.

Remember, your cells in Jupyter are running in NodeJS.

Once all the files have loaded, then onReady will execute in JavaScript.

Note that only the function in onReady is executed in JavaScript (i.e. Data from other cells in Jupyter would normally not be available).

If data is needed from jupyter, pass them through options.data. (ex: airportData example below)

For More - See the htmlScript tutorial.

Example

For example using a cdn library for qr codes

utils.ijs.htmlScript({
    scripts: ['https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js'],
    height: '100%',
    onReady: ({rootEl}) => {
        new QRCode(rootEl, "https://jupyter-ijavascript-utils.onrender.com/");
    }
});

Screenshot of QR Code

Or working with Leaflet - to show maps

//-- nodeJS Variable
airportData = { ohareORD: { lat: 41.975813, lon: -87.909428, title: "O'Hare Intl Airport" } };
//-- render out html
utils.ijs.htmlScript({
    scripts: ['https://unpkg.com/leaflet@1.6.0/dist/leaflet.js',
              'https://unpkg.com/leaflet-providers@1.13.0/leaflet-providers.js'],
    css: ['https://unpkg.com/leaflet@1.6.0/dist/leaflet.css'],
    data: airportData,
    height: 150,
    //-- function will be executed in javaScript
    onReady: ({rootEl, data}) => {
        // L is globally available from the leaflet.js script.
        
        //-- capture the nodeJS data and use in JavaScript. Neat!
        ohareORD = data.ohareORD;

        map = L.map(rootEl);
        map.setView([ohareORD.lat, ohareORD.lon], 14);
        
        new L.marker([ohareORD.lat, ohareORD.lon]).bindPopup(ohareORD.title).addTo(map);
        
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
           attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);
    }
});

Screenshot of Leaflet

Parameters:
Name Type Description
options Object

additional options available during rendering

Properties
Name Type Attributes Default Description
onReady String | function

JavaScript to run once all files have loaded

Properties
Name Type Attributes Description
rootEl Element

results div container

data any <optional>

the options.data parameter

utilityFunctions Object

the options.utilityFunctions object

options Object

the options object passed

animate Object

alias to requestAnimationFrame with additional checks to avoid leaks

scripts Array.<String> <optional>
[]

Array of JavaScript file addresses to load

css Array.<String> <optional>
[]

Array of CSS file addresses to load

data any <optional>

any nodejs data you would like available in javaScript

html String <optional>
''

html elements to include within the result

width String | Number <optional>
'100%'

width of the div container (ex: 400 or '400px')

height String | Number <optional>
'200px'

height of the div container (ex: 200 or '200px')

debug Boolean <optional>
false

whether to incude a debugger breakpoint (once scripts are loaded)

console Boolean <optional>
true

whether to include console statements

(static) initializePageBreaks(htmlToInjectBeforeopt, htmlToInjectAfteropt)

Required to be called first - in order to write page-breaks in the html results.

(For Example:)

Assume you have the following cells:

* Some Text
* utils.ijs.initializePageBreaks()
* some other text
* utils.ijs.printPageBreak();
* end of document

When you print to HTML and then render to PDF, you'll see the following:

* Some Text
* utils.ijs.initializePageBreaks()

//-- page break

* some other text
* utils.ijs.printPageBreak();

//-- page break

* end of document

Note, sometimes you want to include a text prior to the page break, like mentioning that a page is left intentionally blank - or to identify this is the cell that has the style - so it shouldn't be removed prior to printing.

utils.ijs.initializePageBreaks('<h1>This page left intentionally blank</h1>');

Or, perhaps you always want a page break after defining the styles

utils.ijs.initializePageBreaks(null, utils.ijs.generatePageBreakHTML());
Parameters:
Name Type Attributes Description
htmlToInjectBefore String <optional>

optional html text to include prior to the pageBreak (This can be helpful like - page left intentionally blank)

htmlToInjectAfter String <optional>

optional html text to include prior to the pageBreak

(static) internalComment(shouldRender, markdownText, displayopt)

Capture internal comments that can render as markdown (including images) but be turned off - so they are not rendered in the final output

utils.ijs.internalComment(true, `![Screenshot](localhost://path-to-file)`);

and then when preparing to ship, you don't include it

printable=true
utils.ijs.internalComment(!printable, `![Screenshot](localhost://path-to-file)`);
Parameters:
Name Type Attributes Description
shouldRender Boolean

whether the comment should be rendered to the output

markdownText String

the markdown text to render

display Jupyter$$ <optional>

the display to render the output to.

(static) listGlobals() → {Array.<String>}

See:

List the globals currently defined.

This can be very useful when keeping track of values after a few cells.

For example:

cars = utils.datasets.fetch('cars.json').then(data => global.cars = data);

then later

utils.ijs.listGlobals();
// cars
Returns:
  • list of the global variables
Type
Array.<String>

(static) listStatic(target) → {Array.<StaticMember>}

See:

List the static members and functions of a class.

Example
utils.ijs.listStatic(utils.ijs)
// [{type:'function', constructor:'Function', isMethod:true, name:'listStatic'}, ...]
Parameters:
Name Type Description
target class

the target class

Returns:
Type
Array.<StaticMember>

(static) markDocumentPosition(elementId, markerCommentopt, innerHTMLopt)

See:

Renders HTML that can be easily found in the HTML of an exported document.

This allows for simpler and consistent post-processing (such as which cells to remove prior to rendering to PDF)

For example, within a Jupyter / Jupyter Lab Notebook:

utils.ijs.markDocumentPosition('ijsutils-start-of-content', 'Remove this and everything above', '<h1>Start of content</h1>');

//-- this will render a cell with the following:
// <!-- ##start of stuff## -->
// <span id="ijsutils-start-of-content"><h1>Start of content</h1></span>

We can then look for that id within the output of the notebook.
(Like within a bookmarklet)

parentCell = document.querySelector('#ijsutils-start-of-content').closest('.jp-Cell');

and then remove it - and any cells before it

cellsToRemove = [];
while(parentCell) {
    cellsToRemove.push(parentCell);
    parentCell = parentCell.previousElementSibling;
}

//-- to remove them
cellsToRemove.forEach((el) => el.remove())
Parameters:
Name Type Attributes Description
elementId String

id of the element that could be found through document.querySelector

markerComment String <optional>

Any additional text to put in an html comment

innerHTML String <optional>

Optional text to be shown in the span (useful for making it easier to find)

(static) markEndOfContent()

See:

Marks the end of content.

This could be useful for removing all cells and content after this point from printing when reviewing the html output.

Example
utils.ijs.markEndOfContent();

//-- will render the following in the cell
// <!-- ##End of content. This could be used to remove everything after from printing.## -->
// <span id="ijsutils-end-of-content">&nbsp;</span>

(static) markStartOfContent()

See:

Marks the start of content.

This could be useful for removing all cells and content prior from printing when reviewing the html output.

Example
utils.ijs.markStartOfContent();

//-- will render the following in the cell
// <!-- ##Start of content. This could be used to remove everything prior from printing.## -->
// <span id="ijsutils-start-of-content">&nbsp;</span>

(static) markdown(markdownText, markdownText, displayopt)

Prints markdown if in the context of iJavaScript.

This can be deceptively helpful, as it allows your text to be data driven:

Screenshot of markdown

Example
utils.ijs.markdown(`# Overview
This is markdown rendered in a cell.`);
Parameters:
Name Type Attributes Description
markdownText String

The markdown to be rendered

markdownText *

the markdown text to render

display Jupyter$$ <optional>

the display to render the output to.

(static) printPageBreak(htmlToInjectBeforeopt, htmlToInjectAfteropt)

After the {@see module:ijs.initializePageBreaks|utils.ijs.initializePageBreaks} is called, this will create another page break.

Parameters:
Name Type Attributes Description
htmlToInjectBefore String <optional>

optional html text to include prior to the pageBreak (This can be helpful like - page left intentionally blank)

htmlToInjectAfter String <optional>

optional html text to include prior to the pageBreak

(static) useCache(shouldWrite, cachePath, cacheFile, expensiveFn, fsOptions) → {any}

See:

For very long or time-intensive executions, sometimes it is better to cache the results than to execute them every single time.

Note that this supports promises, and can be a bit harder to understand.

As opposed to file.useCache - which works synchronously

shouldWrite = true; /// we will write to the cache with the results from the execution
utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
  //-- all the items to cache 
  return Promise.resolve()
     .then(() => ajax.retrieve(...))
     .then((results) => {
         const data = results
           .map(obj => ({ ...obj, date: Date.parse(obj.epoch) }));
         .. other things to do
         return data;
      });
})
  //-- using the information AFTER the expensive function or retrieval from cache
  //-- this can ALSO be run in a subsequence cell
  .then((results) => {
     expensiveresults = results;
     conosole.log(`expensiveResults.length: ${results.length}`);
   });

but sometimes I would rather just skip to the end

shouldWrite = false; /// we will read from the cache instead,
// everything else remains the same

utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
  //-- all the items to cache 
  return Promise.resolve()
    ... blah blah - none of this will get executed
})
  //-- using the information AFTER the expensive function or retrieval from cache
  //-- this can ALSO be run in a subsequence cell
  .then((results) => {
     expensiveresults = results;
     conosole.log(`expensiveResults.length: ${results.length}`);
   });

//-- completely transparent to the runner
expensiveresults.length = 1023424;
Parameters:
Name Type Description
shouldWrite Boolean

whether we should write to the cache (true) or read from the cache (false)

cachePath String

Path to the cache folder, ex: './cache'

cacheFile String

Filename of the cache file to use for this execution, ex: 'ExecutionsPerMin.js'

expensiveFn function

function that returns the results to be stored in the cache

fsOptions Object

options to use when writing or reading files

Returns:
  • either the deserialized json from the cache or the results from the expensive function
Type
any