Repository: bokeh/bokeh

Issue:
Notebook output generates console errors
Since introducing JupyterLab compatibility in bokeh master displaying bokeh plots in the classic notebook generates various JS errors in certain conditions (which I haven't yet narrowed down). Here's an example that reliable reproduces the issue:

```python
import math

from bokeh.io import show
from bokeh.plotting import figure
from bokeh.models import GraphRenderer, StaticLayoutProvider, Oval
from bokeh.palettes import Spectral8
from bokeh.layouts import row
from bokeh.io import output_notebook

output_notebook(hide_banner=False)


def create_graph():
    N = 8
    node_indices = list(range(N))

    plot = figure(title="Graph Layout Demonstration", x_range=(-1.1,1.1), y_range=(-1.1,1.1),
                  tools="", toolbar_location=None)

    graph = GraphRenderer()

    graph.node_renderer.glyph = Oval(height=0.1, width=0.2, fill_color="fill_color")
    graph.node_renderer.data_source.data = dict(
        index=node_indices,
        fill_color=Spectral8)

    graph.edge_renderer.data_source.data = dict(
        start=[0]*N,
        end=node_indices)

    ### start of layout code
    circ = [i*2*math.pi/8 for i in node_indices]
    x = [math.cos(i) for i in circ]
    y = [math.sin(i) for i in circ]

    graph_layout = dict(zip(node_indices, zip(x, y)))
    graph.layout_provider = StaticLayoutProvider(graph_layout=graph_layout)

    plot.renderers.append(graph)
    return plot

p1 = create_graph()
p2 = create_graph()

show(row(p1, p2))
```

This produces the following error on the console, which appears twice:

```
Exception in event handler for output_added.OutputArea TypeError: Cannot read property 'hasOwnProperty' of undefined
    at window._Events.handleAddOutput (eval at append_javascript (outputarea.js:727), <anonymous>:67:21)
    at window._Events.dispatch (jquery.min.js:5)
    at window._Events.y.handle (jquery.min.js:5)
    at Object.trigger (jquery.min.js:5)
    at window._Events.<anonymous> (jquery.min.js:5)
    at Function.each (jquery.min.js:4)
    at init.each (jquery.min.js:4)
    at init.trigger (jquery.min.js:5)
    at init.events.trigger (events.js:31)
    at OutputArea.append_output (outputarea.js:339) (2) ["output_added.OutputArea", {…}, callee: (...), Symbol(Symbol.iterator): ƒ]
```

This is presumable occurs due to an issue here in the autoload_nb_js template: https://github.com/bokeh/bokeh/blob/master/bokeh/core/_templates/autoload_nb_js.js#L59




Discussion:
cc @canavandl 
```
    // limit handleAddOutput to EXEC_MIME_TYPE content only
    if (!output.data.hasOwnProperty(EXEC_MIME_TYPE)) {
      return
    }
```

Should this just be `output.hasOwnProperty(EXEC_MIME_TYPE)` ?
The issue is the bokeh warning:

<img width="1108" alt="screen shot 2017-09-08 at 10 21 34 am" src="https://user-images.githubusercontent.com/2198981/30219143-ca6e3b24-9480-11e7-99ef-cf5e770baafa.png">

I don't entirely understand why this message is getting picked up by the bokeh mime renderer in the first place however.

Resolving patch:
diff --git a/bokeh/util/notebook.py b/bokeh/util/notebook.py
--- a/bokeh/util/notebook.py
+++ b/bokeh/util/notebook.py
@@ -5,6 +5,8 @@
 
 from IPython.display import publish_display_data
 
+from ..core.templates import SCRIPT_TAG, AUTOLOAD_NB_JS
+
 HTML_MIME_TYPE = 'text/html'
 JS_MIME_TYPE   = 'application/javascript'
 LOAD_MIME_TYPE = 'application/vnd.bokehjs_load.v0+json'
@@ -52,31 +54,34 @@ def load_notebook(resources=None, verbose=False, hide_banner=False, load_timeout
     if resources is None:
         resources = CDN
 
-    if resources.mode == 'inline':
-        js_info = 'inline'
-        css_info = 'inline'
-    else:
-        js_info = resources.js_files[0] if len(resources.js_files) == 1 else resources.js_files
-        css_info = resources.css_files[0] if len(resources.css_files) == 1 else resources.css_files
+    if not hide_banner:
 
-    warnings = ["Warning: " + msg['text'] for msg in resources.messages if msg['type'] == 'warn']
+        if resources.mode == 'inline':
+            js_info = 'inline'
+            css_info = 'inline'
+        else:
+            js_info = resources.js_files[0] if len(resources.js_files) == 1 else resources.js_files
+            css_info = resources.css_files[0] if len(resources.css_files) == 1 else resources.css_files
 
-    if _notebook_loaded and verbose:
-        warnings.append('Warning: BokehJS previously loaded')
+        warnings = ["Warning: " + msg['text'] for msg in resources.messages if msg['type'] == 'warn']
+        if _notebook_loaded and verbose:
+            warnings.append('Warning: BokehJS previously loaded')
 
-    _notebook_loaded = resources
+        element_id = make_id()
 
-    element_id = make_id()
+        html = NOTEBOOK_LOAD.render(
+            element_id    = element_id,
+            verbose       = verbose,
+            js_info       = js_info,
+            css_info      = css_info,
+            bokeh_version = __version__,
+            warnings      = warnings,
+        )
 
-    html = NOTEBOOK_LOAD.render(
-        element_id    = element_id,
-        verbose       = verbose,
-        js_info       = js_info,
-        css_info      = css_info,
-        bokeh_version = __version__,
-        warnings      = warnings,
-        hide_banner   = hide_banner,
-    )
+    else:
+        element_id = None
+
+    _notebook_loaded = resources
 
     custom_models_js = bundle_all_models()
 
@@ -84,28 +89,26 @@ def load_notebook(resources=None, verbose=False, hide_banner=False, load_timeout
     jl_js = _loading_js(resources, element_id, custom_models_js, load_timeout, register_mime=False)
 
     if notebook_type=='jupyter':
-
         if not hide_banner:
             publish_display_data({'text/html': html})
-
         publish_display_data({
             JS_MIME_TYPE   : nb_js,
             LOAD_MIME_TYPE : jl_js
         })
 
     else:
-        _publish_zeppelin_data(html, jl_js)
+        if not hide_banner:
+            _publish_zeppelin_data(html)
+        _publish_zeppelin_data(SCRIPT_TAG.render(js_code=jl_js))
 
 # TODO (bev) This will eventually go away
-def _publish_zeppelin_data(html, js):
+def _publish_zeppelin_data(html):
+    '''Embed html content via %html magic'''
     print('%html ' + html)
-    print('%html ' + '<script type="text/javascript">' + js + "</script>")
 
 def _loading_js(resources, element_id, custom_models_js, load_timeout=5000, register_mime=True):
 
-    from ..core.templates import AUTOLOAD_NB_JS
-
-    js = AUTOLOAD_NB_JS.render(
+    return AUTOLOAD_NB_JS.render(
         elementid = element_id,
         js_urls   = resources.js_files,
         css_urls  = resources.css_files,
@@ -116,8 +119,6 @@ def _loading_js(resources, element_id, custom_models_js, load_timeout=5000, regi
         register_mime = register_mime
     )
 
-    return js
-
 def get_comms(target_name):
     ''' Create a Jupyter comms object for a specific target, that can
     be used to update Bokeh documents in the Jupyter notebook.


Test patch: