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
- ijs.detectIJS - Detect if we are within iJavaScript context
- ijs.detectContext - Identify the $$ and console variables of the current cell
- Introspection
- ijs.listGlobals - List global variables
- ijs.listStatic - List the static values on a class
- Rendering
- ijs.markdown - Render output as markdown
- ijs.htmlScript - Leverage external libraries like D3, Leaflet, etc.
- Printing
- ijs.clearOutput - clears the output to declutter results (like importing libraries, or functions)
- ijs.initializePageBreaks - call at least once to allow pageBreaks when rendering PDFs
- ijs.printPageBreak - call to print a page break when rendering PDFs
- ijs.printPageBreak - generates the html used in to allow for pagebreaks (so you can render them as you'd like)
- ijs.generatePageBreakHTML - generates the html that uses the styles to render pagebreaks (so you can render them as you'd like)
- ijs.internalComment - render markdown, but be able to turn it off, prior to printing
- ijs.markDocumentPosition - render something in html for post processing (such as identifying the start of document and removing everything prior)
- ijs.markStartOfContent - most comment mark - the start of content
- ijs.markEndOfContent - most comment mark - the end of content
- 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
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/");
}
});
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.
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}
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/");
}
});
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);
}
});
Parameters:
Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options |
Object | additional options available during rendering Properties
|
(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, ``);
and then when preparing to ship, you don't include it
printable=true
utils.ijs.internalComment(!printable, ``);
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)
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()
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"> </span>
(static) markStartOfContent()
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"> </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:
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:
-
- file.readJSON - reads a local JSON file
- file.writeJSON - writes to a JSON file
- file.useCache - can be much easier than using promises
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