In the IPython notebook (v3.1, for example), I could add a ~/.ipython/profile_default/static/custom/custom.js file to execute some custom JavaScript.  For example, I could do something like this:
require(['base/js/namespace', 'base/js/events'], function(IPython, events) {
    console.log("A");
    events.on('app_initialized.NotebookApp', function() {
        console.log("B");
    });
    console.log("C");
});
Then, in the JS console, I would see A, followed by B, followed by C.  
Now, as of version 4.0, they've split it out into the Jupyter notebook.  The same file gets loaded (despite the fact that it's under ~/.ipython, rather than under ~/.jupyter), and the code gets executed.  However, I no longer see the B line.  I guess the app isn't getting initialized.  I still see it get triggered in the source code, but does that comes later, or is it just not working?
How do I get things working again?  Do I just not need to wait for app_initialized any more?  Is any of this documented somewhere?
Edit
This page seems to suggest that the way to do it nowadays is to create a custom extension and put all the action in the load_ipython_extension function.  Is that right?  If so, how about mathjax?  And CodeMirror options?
 
     
     
     
     
    