Skip to content

Commit

Permalink
Merge with upstream nbviwer.js
Browse files Browse the repository at this point in the history
Merge commit '3d0623678923e98594ad2d6e443372e71efd6e6d'
  • Loading branch information
tuxu committed Jul 15, 2017
2 parents d713af9 + 3d06236 commit 8db398e
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 22 deletions.
4 changes: 2 additions & 2 deletions nbviewer.js/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### client side rendering of jupyter notebooks

*tl;dr: Render Jupyter notebooks straight in the browser, without a back end converter. Can be used as a library.*
*tl;dr: Render Jupyter notebooks straight in the browser, without a back end converter. Can be used as a library. Or if you're on macOS, you can even fire it up in Quick Look, see [ipynb-quicklook](https://github.com/tuxu/ipynb-quicklook).*

I often want to read through my Jupyter notebooks, but I rarely have my Jupyter instances running in the right folders. I can't quite use the [online nbviewer](http://nbviewer.jupyter.org/), because I don't have a public URL for these, so I resort to running dummy Jupyter instances or uploading my file as a one-time gist on Github (one I have to delete thereafter). One last possibility is `nbconvert` in the command line.

Expand All @@ -16,7 +16,7 @@ Or you can use [the demo](https://kokes.github.io/nbviewer.js/viewer.html) (or a

Two immediate use cases come to mind:

1. In Mac OS, you can preview files and this system, [Quick Look](https://support.apple.com/kb/PH21920?locale=en_US), supports plugins. It would be rather handy to have a notebook preview one keystroke away.
1. ~~In Mac OS, you can preview files and this system, [Quick Look](https://support.apple.com/kb/PH21920?locale=en_US), supports plugins. It would be rather handy to have a notebook preview one keystroke away.~~ This has been done, see [ipynb-quicklook](https://github.com/tuxu/ipynb-quicklook).
2. Ever since GitHub introduced notebook rendering last year, Gitlab users have been [requesting the same](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508). Gitlab itself recently dropped Python as a dependency, so its reintroduction just for converting notebooks is rather unlikely. A browser-based solution like this could be a good substitute.

### Tech details
Expand Down
213 changes: 213 additions & 0 deletions nbviewer.js/github-browser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>nbviewer.js</title>

<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js' data-manual></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-python.min.js' data-manual></script>

<style type='text/css'>
body {
font: 0.8em Arial, sans-serif;
background-color: #eee;
}
div#instructions {
max-width: 960px;
font-size: 2em;
color: #aaa;
text-align: center;
/*padding-top: 5%;*/
margin: 0 auto;
}
p#examples {
font-size: .5em;
}
div#serving-info {
font-size: .7em;
font-style: italic;
margin-top: 1em;
}
div#bugs {
font-size: .6em;
}
input#ghproj {
display: block;
font-size: 1em;
text-align: center;
margin: 0 auto;
width: 50%;
}
div#filelist {
margin: 0 auto;
width: 50%;
text-align: left;
font-size: .8em;
}
div#filelist ul {
list-style-type: none;
}
div#filelist ul li {
vertical-align: center;
}
div#filelist ul li a {
text-decoration: none;
}
</style>

<!-- nbviewer.js -->
<script src='lib/nbv.js'></script>
</head>
<body>
<a href="https://github.com/kokes/nbviewer.js"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png"></a>

<div id='doc'>
<div id='instructions'>
<p>Enter a <code>username/repo</code> combination from Github</p>
<p id='examples'>
<a href='#aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9yc3ZwL2ZlY29uMjM1L2NvbnRlbnRzL25iL2ZyZWQtZW1wbG95LW5mcC5pcHluYj9yZWY9bWFzdGVy'>Example 1</a>
<a href='#aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9hcm9rZW0vdGVhY2hfb3B0aW1pemF0aW9uL2NvbnRlbnRzL29wdGltaXphdGlvbi5pcHluYj9yZWY9bWFzdGVy'>Example 2</a>
<form>
<input id='ghproj' type='text' placeholder='username/repo' />
<input id='ghlookup' type='submit' value='Browse repo' />
</form>

<div id='serving-info'></div>
<div id='bugs'>(We know there's an encoding issue making non-latin characters render incorrectly.)</div>

<div id='filelist'>

</div>
</div>
<div id='notebook'>

</div>

</div>


<script type='text/javascript'>
var d = document;
var cache = {};
var gg; // debugging

window.onload = function() {
var lk = d.getElementById('ghlookup');
var ghproj = d.getElementById('ghproj');

// block submitting again
lk.addEventListener('click', function(e) {
e.target.disabled = 'disabled';
// TODO: validate inputs
// should be something like [a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-], not sure what exactly
var turl = get_gh_url('repos/' + ghproj.value + '/contents/'); // living on the edgeeee
window.location.hash = btoa(turl);
});
// re-enable the submit button when changed
ghproj.addEventListener('keyup', function(e) {
lk.disabled = '';
});
hash_changed();
};

window.onhashchange = hash_changed;

function hash_changed() {
render_gh_files();
}

function render_gh_files() {
wipeElement(d.getElementById('serving-info')); // clear
var hs = window.location.hash.slice(1); // without '#'
var hsurl = atob(hs); // base64 -> text
ajax_get_json(hsurl, function(dt) {
gg = dt;
// get pathname
var a = d.createElement('a');
a.href = hsurl;
var path = a.pathname;

if (path.endsWith('ipynb')) {
var nb = JSON.parse(atob(dt.content)); // decode incoming data
var tg = d.getElementById('notebook');
nbv.render(nb, tg);

// serving info
var turl = window.location;
d.getElementById('serving-info').innerHTML = `Serving <code>${dt.name}</code>. <a href='${turl}'>Link to this render</a> / <a href='${dt.html_url}'>original Github source</a>`;
return;
}

var fl = d.getElementById('filelist');
var ul = d.createElement('ul');
wipeElement(fl);
for (var j=0; j<dt.length; j++) {
var el = dt[j];
var li = d.createElement('li');
li.appendChild(function() {
if (el.type === 'file' && !el.name.endsWith('.ipynb')) {
return d.createTextNode(el.name);
}

var a = d.createElement('a');
a.textContent = el.name;
a.setAttribute('href', '#' + btoa(el.url));
return a;
}());

ul.appendChild(li);
}
fl.appendChild(ul);

});
}

function get_gh_url(path) {
return 'https://api.github.com/' + path;
}

function ajax_get_json(url, callback)
{
console.log(url);
if (cache[url] !== undefined) {
console.log('serving from cache');
return callback(cache[url]);
}

var rr = new XMLHttpRequest();
rr.onreadystatechange = function() {
if (rr.readyState == 4 && rr.status == 200) {
cache[url] = JSON.parse(rr.responseText)
callback(JSON.parse(rr.responseText));
} else if (rr.readyState == 4) {
throw Error(err);
}
}
rr.open('GET', url);
rr.send(null);
}

function wipeElement(el) {
while (el.firstChild) {
el.removeChild(el.firstChild);
}
}

</script>

<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-98368126-1', 'auto');
ga('send', 'pageview');

</script>

</body>
</html>

44 changes: 26 additions & 18 deletions nbviewer.js/lib/nbv.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ var nbv = (function() {
'margin-bottom: 0',
'margin-top: 0',
'border-radius: 2px',
'overflow-x: scroll',
'min-height: .85em'
].join(';'));
var code = d.createElement('code');
Expand Down Expand Up @@ -141,6 +140,10 @@ var nbv = (function() {
case 'stream':
dm.appendChild(handle_stream_output(dt));
break;
case 'pyerr': // v3
case 'error': // v4
dm.appendChild(handle_error_cell(dt));
break;
case 'display_data':
if (st.nbformat > 3) {
dm.appendChild(handle_cell_output(dt));
Expand Down Expand Up @@ -184,7 +187,6 @@ var nbv = (function() {
break;

case 'text/html':
dm.style.overflow = 'scroll';
dm.innerHTML = dt.data[fmt].join('');

// we may have generated some HTML tables we need to style
Expand All @@ -203,13 +205,17 @@ var nbv = (function() {
}

}

break;

case 'image/svg+xml':
dm.innerHTML = dt.data[fmt].join('');
break;

default:
if (fmt.startsWith('image/')) {
dm = d.createElement('img');
dm.setAttribute('src', 'data:' + fmt + ';base64,' + dt.data[fmt]);
dm.setAttribute('src', 'data:' + fmt + ';base64,' +
((typeof dt.data[fmt]) == 'string' ? dt.data[fmt] : dt.data[fmt].join('')));
break;
}
console.error('unexpected format: ' + fmt);
Expand All @@ -221,15 +227,29 @@ var nbv = (function() {
return el;
}

function handle_error_cell(dt) {
var cn = d.createElement('pre');
var txt = dt.traceback.join('\n');
// stripping ANSI colours for now https://github.com/chalk/ansi-regex/blob/master/index.js#L3
// could use a full library at some point https://github.com/drudru/ansi_up
txt = txt.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
cn.textContent = txt;

return cn;
}

function handle_stream_output(dt) {
// name in v4, stream in v3
if ( (dt.name || dt.stream) != 'stdout' || dt.output_type != 'stream')
console.error('unexpected stream spec');
var outt = dt.name || dt.stream; // v4 || v3; contains 'stdout' or 'stderr'

if (!dt.hasOwnProperty('text'))
console.error('data for stream missing');

var cn = d.createElement('pre');
// stderr red background, stdout is plain white
if (outt === 'stderr') {
cn.setAttribute('style', 'background-color: #fdd; padding: .5em');
}
cn.textContent = dt.text.join('');

return cn;
Expand Down Expand Up @@ -287,18 +307,6 @@ var nbv = (function() {
return el.toLowerCase().replace(/\W+/g, '-');
}

// function render_toc() {
// var els = st.target.querySelector('h1,h2,h3,h4,h5,h6')
// var minl = 6
// for (var j=0; j<els.length;j++) {
// minl = (els[j].nodeName[1] < minl) ?
// parseInt(els[j].nodeName[1]) : minl
// }
// console.log(minl)

// }


return {
render: render_ipynb
};
Expand Down
18 changes: 16 additions & 2 deletions nbviewer.js/viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
padding-top: 10%;
margin: 0 auto;
}
div#instructions small {
font-size: .7em;
}
div#dropzone {
position: fixed;
top: 0; left: 0;
Expand All @@ -36,7 +39,6 @@
/*display: block;*/
font-size: 2em;
margin: 0 auto;
z
}
</style>

Expand All @@ -48,7 +50,8 @@

<div id='doc'>
<select id='file-selector'></select>
<div id='instructions'>Drag and drop Jupyter notebooks anywhere here</div>
<div id='instructions'>Drag and drop Jupyter notebooks anywhere here<br />
<small>(if you wish to render directly from Github, <a href='github-browser.html'>try our new tool</a>)</small></div>

</div>

Expand Down Expand Up @@ -161,6 +164,17 @@

</script>

<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-98368126-1', 'auto');
ga('send', 'pageview');

</script>

</body>
</html>

0 comments on commit 8db398e

Please sign in to comment.